Version 1.5.0-dev.4.4

Reverting bleeding edge revisions 37107 and r37101 from trunk
svn merge -r37107:37106 https://dart.googlecode.com/svn/branches/bleeding_edge trunk
svn merge -r37101:37100 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@37111 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analysis_server/lib/src/computer/computer_highlights.dart b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
new file mode 100644
index 0000000..06a3db4
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_highlights.dart
@@ -0,0 +1,517 @@
+// 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 computer.highlights;
+
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
+import 'package:analyzer/src/generated/scanner.dart';
+
+
+/**
+ * A computer for [HighlightRegion]s in a Dart [CompilationUnit].
+ */
+class DartUnitHighlightsComputer {
+  final CompilationUnit _unit;
+
+  final List<Map<String, Object>> _regions = <Map<String, Object>>[];
+
+  DartUnitHighlightsComputer(this._unit);
+
+  /**
+   * Returns the computed highlight regions, not `null`.
+   */
+  List<Map<String, Object>> compute() {
+    _unit.accept(new _DartUnitHighlightsComputerVisitor(this));
+    return _regions;
+  }
+
+  void _addIdentifierRegion(SimpleIdentifier node) {
+    if (_addIdentifierRegion_keyword(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_class(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_constructor(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_dynamicType(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_getterSetterDeclaration(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_field(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_function(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_functionTypeAlias(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_importPrefix(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_localVariable(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_method(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_parameter(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_topLevelVariable(node)) {
+      return;
+    }
+    if (_addIdentifierRegion_typeParameter(node)) {
+      return;
+    }
+    _addRegion_node(node, HighlightType.IDENTIFIER_DEFAULT);
+  }
+
+  void _addIdentifierRegion_annotation(Annotation node) {
+    ArgumentList arguments = node.arguments;
+    if (arguments == null) {
+      _addRegion_node(node, HighlightType.ANNOTATION);
+    } else {
+      _addRegion_nodeStart_tokenEnd(node, arguments.beginToken, HighlightType.ANNOTATION);
+      _addRegion_token(arguments.endToken, HighlightType.ANNOTATION);
+    }
+  }
+
+  bool _addIdentifierRegion_class(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! ClassElement) {
+      return false;
+    }
+    return _addRegion_node(node, HighlightType.CLASS);
+  }
+
+  bool _addIdentifierRegion_constructor(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! ConstructorElement) {
+      return false;
+    }
+    return _addRegion_node(node, HighlightType.CONSTRUCTOR);
+  }
+
+  bool _addIdentifierRegion_dynamicType(SimpleIdentifier node) {
+    // should be variable
+    Element element = node.staticElement;
+    if (element is! VariableElement) {
+      return false;
+    }
+    // has propagated type
+    if (node.propagatedType != null) {
+      return false;
+    }
+    // has dynamic static type
+    DartType staticType = node.staticType;
+    if (staticType == null || !staticType.isDynamic) {
+      return false;
+    }
+    // OK
+    return _addRegion_node(node, HighlightType.DYNAMIC_TYPE);
+  }
+
+  bool _addIdentifierRegion_field(SimpleIdentifier node) {
+    Element element = node.bestElement;
+    if (element is FieldFormalParameterElement) {
+      element = (element as FieldFormalParameterElement).field;
+    }
+    if (element is FieldElement) {
+      if ((element as FieldElement).isStatic) {
+        return _addRegion_node(node, HighlightType.FIELD_STATIC);
+      } else {
+        return _addRegion_node(node, HighlightType.FIELD);
+      }
+    }
+    if (element is PropertyAccessorElement) {
+      if ((element as PropertyAccessorElement).isStatic) {
+        return _addRegion_node(node, HighlightType.FIELD_STATIC);
+      } else {
+        return _addRegion_node(node, HighlightType.FIELD);
+      }
+    }
+    return false;
+  }
+
+  bool _addIdentifierRegion_function(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! FunctionElement) {
+      return false;
+    }
+    HighlightType type;
+    if (node.inDeclarationContext()) {
+      type = HighlightType.FUNCTION_DECLARATION;
+    } else {
+      type = HighlightType.FUNCTION;
+    }
+    return _addRegion_node(node, type);
+  }
+
+  bool _addIdentifierRegion_functionTypeAlias(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! FunctionTypeAliasElement) {
+      return false;
+    }
+    return _addRegion_node(node, HighlightType.FUNCTION_TYPE_ALIAS);
+  }
+
+  bool _addIdentifierRegion_getterSetterDeclaration(SimpleIdentifier node) {
+    // should be declaration
+    AstNode parent = node.parent;
+    if (!(parent is MethodDeclaration || parent is FunctionDeclaration)) {
+      return false;
+    }
+    // should be property accessor
+    Element element = node.staticElement;
+    if (element is! PropertyAccessorElement) {
+      return false;
+    }
+    // getter or setter
+    PropertyAccessorElement propertyAccessorElement = element as PropertyAccessorElement;
+    if (propertyAccessorElement.isGetter) {
+      return _addRegion_node(node, HighlightType.GETTER_DECLARATION);
+    } else {
+      return _addRegion_node(node, HighlightType.SETTER_DECLARATION);
+    }
+  }
+
+  bool _addIdentifierRegion_importPrefix(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! PrefixElement) {
+      return false;
+    }
+    return _addRegion_node(node, HighlightType.IMPORT_PREFIX);
+  }
+
+  bool _addIdentifierRegion_keyword(SimpleIdentifier node) {
+    String name = node.name;
+    if (name == "void") {
+      return _addRegion_node(node, HighlightType.KEYWORD);
+    }
+    return false;
+  }
+
+  bool _addIdentifierRegion_localVariable(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! LocalVariableElement) {
+      return false;
+    }
+    // OK
+    HighlightType type;
+    if (node.inDeclarationContext()) {
+      type = HighlightType.LOCAL_VARIABLE_DECLARATION;
+    } else {
+      type = HighlightType.LOCAL_VARIABLE;
+    }
+    return _addRegion_node(node, type);
+  }
+
+  bool _addIdentifierRegion_method(SimpleIdentifier node) {
+    Element element = node.bestElement;
+    if (element is! MethodElement) {
+      return false;
+    }
+    MethodElement methodElement = element as MethodElement;
+    bool isStatic = methodElement.isStatic;
+    // OK
+    HighlightType type;
+    if (node.inDeclarationContext()) {
+      if (isStatic) {
+        type = HighlightType.METHOD_DECLARATION_STATIC;
+      } else {
+        type = HighlightType.METHOD_DECLARATION;
+      }
+    } else {
+      if (isStatic) {
+        type = HighlightType.METHOD_STATIC;
+      } else {
+        type = HighlightType.METHOD;
+      }
+    }
+    return _addRegion_node(node, type);
+  }
+
+  bool _addIdentifierRegion_parameter(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! ParameterElement) {
+      return false;
+    }
+    return _addRegion_node(node, HighlightType.PARAMETER);
+  }
+
+  bool _addIdentifierRegion_topLevelVariable(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! TopLevelVariableElement) {
+      return false;
+    }
+    return _addRegion_node(node, HighlightType.TOP_LEVEL_VARIABLE);
+  }
+
+  bool _addIdentifierRegion_typeParameter(SimpleIdentifier node) {
+    Element element = node.staticElement;
+    if (element is! TypeParameterElement) {
+      return false;
+    }
+    return _addRegion_node(node, HighlightType.TYPE_PARAMETER);
+  }
+
+  void _addRegion(int offset, int length, HighlightType type) {
+    _regions.add({'offset': offset, 'length': length, 'type': type.name});
+  }
+
+  bool _addRegion_node(AstNode node, HighlightType type) {
+    int offset = node.offset;
+    int length = node.length;
+    _addRegion(offset, length, type);
+    return true;
+  }
+
+  void _addRegion_nodeStart_tokenEnd(AstNode a, Token b, HighlightType type) {
+    int offset = a.offset;
+    int end = b.end;
+    _addRegion(offset, end - offset, type);
+  }
+
+  void _addRegion_token(Token token, HighlightType type) {
+    if (token != null) {
+      int offset = token.offset;
+      int length = token.length;
+      _addRegion(offset, length, type);
+    }
+  }
+
+  void _addRegion_tokenStart_tokenEnd(Token a, Token b, HighlightType type) {
+    int offset = a.offset;
+    int end = b.end;
+    _addRegion(offset, end - offset, type);
+  }
+}
+
+
+/**
+ * An AST visitor for [DartUnitHighlightsComputer].
+ */
+class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<Object> {
+  final DartUnitHighlightsComputer computer;
+
+  _DartUnitHighlightsComputerVisitor(this.computer);
+
+  @override
+  Object visitAnnotation(Annotation node) {
+    computer._addIdentifierRegion_annotation(node);
+    return super.visitAnnotation(node);
+  }
+
+  @override
+  Object visitAsExpression(AsExpression node) {
+    computer._addRegion_token(node.asOperator, HighlightType.BUILT_IN);
+    return super.visitAsExpression(node);
+  }
+
+  @override
+  Object visitBooleanLiteral(BooleanLiteral node) {
+    computer._addRegion_node(node, HighlightType.LITERAL_BOOLEAN);
+    return super.visitBooleanLiteral(node);
+  }
+
+  @override
+  Object visitCatchClause(CatchClause node) {
+    computer._addRegion_token(node.onKeyword, HighlightType.BUILT_IN);
+    return super.visitCatchClause(node);
+  }
+
+  @override
+  Object visitClassDeclaration(ClassDeclaration node) {
+    computer._addRegion_token(node.abstractKeyword, HighlightType.BUILT_IN);
+    return super.visitClassDeclaration(node);
+  }
+
+  @override
+  Object visitConstructorDeclaration(ConstructorDeclaration node) {
+    computer._addRegion_token(node.externalKeyword, HighlightType.BUILT_IN);
+    computer._addRegion_token(node.factoryKeyword, HighlightType.BUILT_IN);
+    return super.visitConstructorDeclaration(node);
+  }
+
+  @override
+  Object visitDoubleLiteral(DoubleLiteral node) {
+    computer._addRegion_node(node, HighlightType.LITERAL_DOUBLE);
+    return super.visitDoubleLiteral(node);
+  }
+
+  @override
+  Object visitExportDirective(ExportDirective node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitExportDirective(node);
+  }
+
+  @override
+  Object visitFieldDeclaration(FieldDeclaration node) {
+    computer._addRegion_token(node.staticKeyword, HighlightType.BUILT_IN);
+    return super.visitFieldDeclaration(node);
+  }
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    computer._addRegion_token(node.externalKeyword, HighlightType.BUILT_IN);
+    computer._addRegion_token(node.propertyKeyword, HighlightType.BUILT_IN);
+    return super.visitFunctionDeclaration(node);
+  }
+
+  @override
+  Object visitFunctionTypeAlias(FunctionTypeAlias node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitFunctionTypeAlias(node);
+  }
+
+  @override
+  Object visitHideCombinator(HideCombinator node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitHideCombinator(node);
+  }
+
+  @override
+  Object visitImplementsClause(ImplementsClause node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitImplementsClause(node);
+  }
+
+  @override
+  Object visitImportDirective(ImportDirective node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    computer._addRegion_token(node.deferredToken, HighlightType.BUILT_IN);
+    computer._addRegion_token(node.asToken, HighlightType.BUILT_IN);
+    return super.visitImportDirective(node);
+  }
+
+  @override
+  Object visitIntegerLiteral(IntegerLiteral node) {
+    computer._addRegion_node(node, HighlightType.LITERAL_INTEGER);
+    return super.visitIntegerLiteral(node);
+  }
+
+  @override
+  Object visitLibraryDirective(LibraryDirective node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitLibraryDirective(node);
+  }
+
+  @override
+  Object visitMethodDeclaration(MethodDeclaration node) {
+    computer._addRegion_token(node.externalKeyword, HighlightType.BUILT_IN);
+    computer._addRegion_token(node.modifierKeyword, HighlightType.BUILT_IN);
+    computer._addRegion_token(node.operatorKeyword, HighlightType.BUILT_IN);
+    computer._addRegion_token(node.propertyKeyword, HighlightType.BUILT_IN);
+    return super.visitMethodDeclaration(node);
+  }
+
+  @override
+  Object visitNativeClause(NativeClause node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitNativeClause(node);
+  }
+
+  @override
+  Object visitNativeFunctionBody(NativeFunctionBody node) {
+    computer._addRegion_token(node.nativeToken, HighlightType.BUILT_IN);
+    return super.visitNativeFunctionBody(node);
+  }
+
+  @override
+  Object visitPartDirective(PartDirective node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitPartDirective(node);
+  }
+
+  @override
+  Object visitPartOfDirective(PartOfDirective node) {
+    computer._addRegion_tokenStart_tokenEnd(node.partToken, node.ofToken, HighlightType.BUILT_IN);
+    return super.visitPartOfDirective(node);
+  }
+
+  @override
+  Object visitShowCombinator(ShowCombinator node) {
+    computer._addRegion_token(node.keyword, HighlightType.BUILT_IN);
+    return super.visitShowCombinator(node);
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    computer._addIdentifierRegion(node);
+    return super.visitSimpleIdentifier(node);
+  }
+
+  @override
+  Object visitSimpleStringLiteral(SimpleStringLiteral node) {
+    computer._addRegion_node(node, HighlightType.LITERAL_STRING);
+    return super.visitSimpleStringLiteral(node);
+  }
+
+  @override
+  Object visitTypeName(TypeName node) {
+    DartType type = node.type;
+    if (type != null) {
+      if (type.isDynamic && node.name.name == "dynamic") {
+        computer._addRegion_node(node, HighlightType.TYPE_NAME_DYNAMIC);
+        return null;
+      }
+    }
+    return super.visitTypeName(node);
+  }
+}
+
+
+/**
+ * Highlighting kinds constants.
+ */
+class HighlightType {
+  static const HighlightType ANNOTATION = const HighlightType('ANNOTATION');
+  static const HighlightType BUILT_IN = const HighlightType('BUILT_IN');
+  static const HighlightType CLASS = const HighlightType('CLASS');
+  static const HighlightType COMMENT_BLOCK = const HighlightType('COMMENT_BLOCK');
+  static const HighlightType COMMENT_DOCUMENTATION = const HighlightType('COMMENT_DOCUMENTATION');
+  static const HighlightType COMMENT_END_OF_LINE = const HighlightType('COMMENT_END_OF_LINE');
+  static const HighlightType CONSTRUCTOR = const HighlightType('CONSTRUCTOR');
+  static const HighlightType DIRECTIVE = const HighlightType('DIRECTIVE');
+  static const HighlightType DYNAMIC_TYPE = const HighlightType('DYNAMIC_TYPE');
+  static const HighlightType FIELD = const HighlightType('FIELD');
+  static const HighlightType FIELD_STATIC = const HighlightType('FIELD_STATIC');
+  static const HighlightType FUNCTION_DECLARATION = const HighlightType('FUNCTION_DECLARATION');
+  static const HighlightType FUNCTION = const HighlightType('FUNCTION');
+  static const HighlightType FUNCTION_TYPE_ALIAS = const HighlightType('FUNCTION_TYPE_ALIAS');
+  static const HighlightType GETTER_DECLARATION = const HighlightType('GETTER_DECLARATION');
+  static const HighlightType KEYWORD = const HighlightType('KEYWORD');
+  static const HighlightType IDENTIFIER_DEFAULT = const HighlightType('IDENTIFIER_DEFAULT');
+  static const HighlightType IMPORT_PREFIX = const HighlightType('IMPORT_PREFIX');
+  static const HighlightType LITERAL_BOOLEAN = const HighlightType('LITERAL_BOOLEAN');
+  static const HighlightType LITERAL_DOUBLE = const HighlightType('LITERAL_DOUBLE');
+  static const HighlightType LITERAL_INTEGER = const HighlightType('LITERAL_INTEGER');
+  static const HighlightType LITERAL_LIST = const HighlightType('LITERAL_LIST');
+  static const HighlightType LITERAL_MAP = const HighlightType('LITERAL_MAP');
+  static const HighlightType LITERAL_STRING = const HighlightType('LITERAL_STRING');
+  static const HighlightType LOCAL_VARIABLE_DECLARATION = const HighlightType('LOCAL_VARIABLE_DECLARATION');
+  static const HighlightType LOCAL_VARIABLE = const HighlightType('LOCAL_VARIABLE');
+  static const HighlightType METHOD_DECLARATION = const HighlightType('METHOD_DECLARATION');
+  static const HighlightType METHOD_DECLARATION_STATIC = const HighlightType('METHOD_DECLARATION_STATIC');
+  static const HighlightType METHOD = const HighlightType('METHOD');
+  static const HighlightType METHOD_STATIC = const HighlightType('METHOD_STATIC');
+  static const HighlightType PARAMETER = const HighlightType('PARAMETER');
+  static const HighlightType SETTER_DECLARATION = const HighlightType('SETTER_DECLARATION');
+  static const HighlightType TOP_LEVEL_VARIABLE = const HighlightType('TOP_LEVEL_VARIABLE');
+  static const HighlightType TYPE_NAME_DYNAMIC = const HighlightType('TYPE_NAME_DYNAMIC');
+  static const HighlightType TYPE_PARAMETER = const HighlightType('TYPE_PARAMETER');
+
+  final String name;
+
+  @override
+  String toString() => name;
+
+  const HighlightType(this.name);
+}
diff --git a/pkg/analysis_server/lib/src/computer/computer_navigation.dart b/pkg/analysis_server/lib/src/computer/computer_navigation.dart
new file mode 100644
index 0000000..d329425
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_navigation.dart
@@ -0,0 +1,208 @@
+// 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 computer.navigation;
+
+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/source.dart';
+
+
+/**
+ * A computer for navigation regions in a Dart [CompilationUnit].
+ */
+class DartUnitNavigationComputer {
+  final CompilationUnit _unit;
+
+  List<Map<String, Object>> _regions = <Map<String, Object>>[];
+
+  DartUnitNavigationComputer(this._unit);
+
+  /**
+   * Returns the computed navigation regions, not `null`.
+   */
+  List<Map<String, Object>> compute() {
+    _unit.accept(new _DartUnitNavigationComputerVisitor(this));
+    return new List.from(_regions);
+  }
+
+  void _addRegion(int offset, int length, Element element) {
+    Map<String, Object> target = _createTarget(element);
+    if (target == null) {
+      return;
+    }
+    _regions.add({
+      'offset': offset,
+      'length': length,
+      'targets': [target]
+    });
+  }
+
+  void _addRegion_nodeStart_nodeEnd(AstNode a, AstNode b, Element element) {
+    int offset = a.offset;
+    int length = b.end - offset;
+    _addRegion(offset, length, element);
+  }
+
+  void _addRegion_nodeStart_nodeStart(AstNode a, AstNode b, Element element) {
+    int offset = a.offset;
+    int length = b.offset - offset;
+    _addRegion(offset, length, element);
+  }
+
+  void _addRegion_tokenStart_nodeEnd(Token a, AstNode b, Element element) {
+    int offset = a.offset;
+    int length = b.end - offset;
+    _addRegion(offset, length, element);
+  }
+
+  void _addRegionForNode(AstNode node, Element element) {
+    int offset = node.offset;
+    int length = node.length;
+    _addRegion(offset, length, element);
+  }
+
+  void _addRegionForToken(Token token, Element element) {
+    int offset = token.offset;
+    int length = token.length;
+    _addRegion(offset, length, element);
+  }
+
+  /**
+   * Returns the JSON for the given [Element], maybe `null` if `null` was given.
+   */
+  Map<String, Object> _createTarget(Element element) {
+    if (element == null) {
+      return null;
+    }
+    if (element is FieldFormalParameterElement) {
+      element = (element as FieldFormalParameterElement).field;
+    }
+    // prepare Source
+    Source source = element.source;
+    if (source == null) {
+      return null;
+    }
+    // prepare location
+    int offset = element.nameOffset;
+    int length = element.displayName.length;
+    if (element is CompilationUnitElement) {
+      offset = 0;
+      length = 0;
+    }
+    // return as JSON
+    return {
+      'file': source.fullName,
+      'offset': offset,
+      'length': length,
+      'elementId': element.location.encoding
+    };
+  }
+}
+
+
+
+class _DartUnitNavigationComputerVisitor extends RecursiveAstVisitor {
+  final DartUnitNavigationComputer computer;
+
+  _DartUnitNavigationComputerVisitor(this.computer);
+
+  @override
+  visitAssignmentExpression(AssignmentExpression node) {
+    computer._addRegionForToken(node.operator, node.bestElement);
+    return super.visitAssignmentExpression(node);
+  }
+
+  @override
+  visitBinaryExpression(BinaryExpression node) {
+    computer._addRegionForToken(node.operator, node.bestElement);
+    return super.visitBinaryExpression(node);
+  }
+
+  @override
+  visitConstructorDeclaration(ConstructorDeclaration node) {
+    // associate constructor with "T" or "T.name"
+    {
+      AstNode firstNode = node.returnType;
+      AstNode lastNode = node.name;
+      if (lastNode == null) {
+        lastNode = firstNode;
+      }
+      if (firstNode != null && lastNode != null) {
+        computer._addRegion_nodeStart_nodeEnd(firstNode, lastNode, node.element);
+      }
+    }
+    return super.visitConstructorDeclaration(node);
+  }
+
+  @override
+  visitExportDirective(ExportDirective node) {
+    ExportElement exportElement = node.element;
+    if (exportElement != null) {
+      Element element = exportElement.exportedLibrary;
+      computer._addRegion_tokenStart_nodeEnd(node.keyword, node.uri, element);
+    }
+    return super.visitExportDirective(node);
+  }
+
+  @override
+  visitImportDirective(ImportDirective node) {
+    ImportElement importElement = node.element;
+    if (importElement != null) {
+      Element element = importElement.importedLibrary;
+      computer._addRegion_tokenStart_nodeEnd(node.keyword, node.uri, element);
+    }
+    return super.visitImportDirective(node);
+  }
+
+  @override
+  visitIndexExpression(IndexExpression node) {
+    computer._addRegionForToken(node.rightBracket, node.bestElement);
+    return super.visitIndexExpression(node);
+  }
+
+  @override
+  visitInstanceCreationExpression(InstanceCreationExpression node) {
+    Element element = node.staticElement;
+    if (element != null && element.isSynthetic) {
+      element = element.enclosingElement;
+    }
+    computer._addRegion_nodeStart_nodeStart(node, node.argumentList, element);
+    return super.visitInstanceCreationExpression(node);
+  }
+
+  @override
+  visitPartDirective(PartDirective node) {
+    computer._addRegion_tokenStart_nodeEnd(node.keyword, node.uri, node.element);
+    return super.visitPartDirective(node);
+  }
+
+  @override
+  visitPartOfDirective(PartOfDirective node) {
+    computer._addRegion_tokenStart_nodeEnd(node.keyword, node.libraryName, node.element);
+    return super.visitPartOfDirective(node);
+  }
+
+  @override
+  visitPostfixExpression(PostfixExpression node) {
+    computer._addRegionForToken(node.operator, node.bestElement);
+    return super.visitPostfixExpression(node);
+  }
+
+  @override
+  visitPrefixExpression(PrefixExpression node) {
+    computer._addRegionForToken(node.operator, node.bestElement);
+    return super.visitPrefixExpression(node);
+  }
+
+  @override
+  visitSimpleIdentifier(SimpleIdentifier node) {
+    if (node.parent is ConstructorDeclaration) {
+    } else {
+      computer._addRegionForNode(node, node.bestElement);
+    }
+    return super.visitSimpleIdentifier(node);
+  }
+}
diff --git a/pkg/analysis_server/lib/src/computer/computer_outline.dart b/pkg/analysis_server/lib/src/computer/computer_outline.dart
new file mode 100644
index 0000000..3609759
--- /dev/null
+++ b/pkg/analysis_server/lib/src/computer/computer_outline.dart
@@ -0,0 +1,373 @@
+// 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 computer.outline;
+
+import 'package:analysis_server/src/constants.dart';
+import 'package:analyzer/src/generated/ast.dart';
+
+
+/**
+ * A computer for [CompilationUnit] outline.
+ */
+class DartUnitOutlineComputer {
+  final CompilationUnit _unit;
+
+  DartUnitOutlineComputer(this._unit);
+
+  /**
+   * Returns the computed outline, not `null`.
+   */
+  Map<String, Object> compute() {
+    _Outline unitOutline = _newUnitOutline();
+    for (CompilationUnitMember unitMember in _unit.declarations) {
+      if (unitMember is ClassDeclaration) {
+        ClassDeclaration classDeclaration = unitMember;
+        _Outline classOutline = _newClassOutline(unitOutline, classDeclaration);
+        for (ClassMember classMember in classDeclaration.members) {
+          if (classMember is ConstructorDeclaration) {
+            ConstructorDeclaration constructorDeclaration = classMember;
+            _newConstructorOutline(classOutline, constructorDeclaration);
+          }
+          if (classMember is FieldDeclaration) {
+            FieldDeclaration fieldDeclaration = classMember;
+            VariableDeclarationList fields = fieldDeclaration.fields;
+            if (fields != null) {
+              TypeName fieldType = fields.type;
+              String fieldTypeName = fieldType != null ? fieldType.toSource() : "";
+              for (VariableDeclaration field in fields.variables) {
+                _newVariableOutline(classOutline, fieldTypeName, _OutlineKind.FIELD, field, fieldDeclaration.isStatic);
+              }
+            }
+          }
+          if (classMember is MethodDeclaration) {
+            MethodDeclaration methodDeclaration = classMember;
+            _newMethodOutline(classOutline, methodDeclaration);
+          }
+        }
+      }
+      if (unitMember is TopLevelVariableDeclaration) {
+        TopLevelVariableDeclaration fieldDeclaration = unitMember;
+        VariableDeclarationList fields = fieldDeclaration.variables;
+        if (fields != null) {
+          TypeName fieldType = fields.type;
+          String fieldTypeName = fieldType != null ? fieldType.toSource() : "";
+          for (VariableDeclaration field in fields.variables) {
+            _newVariableOutline(unitOutline, fieldTypeName, _OutlineKind.TOP_LEVEL_VARIABLE, field, false);
+          }
+        }
+      }
+      if (unitMember is FunctionDeclaration) {
+        FunctionDeclaration functionDeclaration = unitMember;
+        _newFunctionOutline(unitOutline, functionDeclaration);
+      }
+      if (unitMember is ClassTypeAlias) {
+        ClassTypeAlias alias = unitMember;
+        _newClassTypeAlias(unitOutline, alias);
+      }
+      if (unitMember is FunctionTypeAlias) {
+        FunctionTypeAlias alias = unitMember;
+        _newFunctionTypeAliasOutline(unitOutline, alias);
+      }
+    }
+    return unitOutline.toJson();
+  }
+
+  void _addLocalFunctionOutlines(_Outline parent, FunctionBody body) {
+    body.accept(new _LocalFunctionOutlinesVisitor(this, parent));
+  }
+
+  /**
+   * Returns the [AstNode]'s source region.
+   */
+  _SourceRegion _getSourceRegion(AstNode node) {
+    int endOffset = node.end;
+    // prepare position of the node among its siblings
+    int firstOffset;
+    List<AstNode> siblings;
+    AstNode parent = node.parent;
+    // field
+    if (parent is VariableDeclarationList) {
+      VariableDeclarationList variableList = parent as VariableDeclarationList;
+      List<VariableDeclaration> variables = variableList.variables;
+      int variableIndex = variables.indexOf(node);
+      if (variableIndex == variables.length - 1) {
+        endOffset = variableList.parent.end;
+      }
+      if (variableIndex == 0) {
+        node = parent.parent;
+        parent = node.parent;
+      } else if (variableIndex >= 1) {
+        firstOffset = variables[variableIndex - 1].end;
+        return new _SourceRegion(firstOffset, endOffset - firstOffset);
+      }
+    }
+    // unit or class member
+    if (parent is CompilationUnit) {
+      firstOffset = 0;
+      siblings = (parent as CompilationUnit).declarations;
+    } else if (parent is ClassDeclaration) {
+      ClassDeclaration classDeclaration = parent as ClassDeclaration;
+      firstOffset = classDeclaration.leftBracket.end;
+      siblings = classDeclaration.members;
+    } else {
+      int offset = node.offset;
+      return new _SourceRegion(offset, endOffset - offset);
+    }
+    // first child: [endOfParent, endOfNode]
+    int index = siblings.indexOf(node);
+    if (index == 0) {
+      return new _SourceRegion(firstOffset, endOffset - firstOffset);
+    }
+    // not first child: [endOfPreviousSibling, endOfNode]
+    int prevSiblingEnd = siblings[index - 1].end;
+    return new _SourceRegion(prevSiblingEnd, endOffset - prevSiblingEnd);
+  }
+
+  _Outline _newClassOutline(_Outline parent, ClassDeclaration classDeclaration) {
+    SimpleIdentifier nameNode = classDeclaration.name;
+    String name = nameNode.name;
+    _SourceRegion sourceRegion = _getSourceRegion(classDeclaration);
+    _Outline outline = new _Outline(
+        _OutlineKind.CLASS, name,
+        nameNode.offset, nameNode.length,
+        sourceRegion.offset, sourceRegion.length,
+        classDeclaration.isAbstract, false,
+        null, null);
+    parent.children.add(outline);
+    return outline;
+  }
+
+  void _newClassTypeAlias(_Outline parent, ClassTypeAlias alias) {
+    SimpleIdentifier nameNode = alias.name;
+    String name = nameNode.name;
+    _SourceRegion sourceRegion = _getSourceRegion(alias);
+    _Outline outline = new _Outline(
+        _OutlineKind.CLASS_TYPE_ALIAS, name,
+        nameNode.offset, nameNode.length,
+        sourceRegion.offset, sourceRegion.length,
+        alias.isAbstract, false,
+        null, null);
+    parent.children.add(outline);
+  }
+
+  void _newConstructorOutline(_Outline parent, ConstructorDeclaration constructor) {
+    Identifier returnType = constructor.returnType;
+    String name = returnType.name;
+    int offset = returnType.offset;
+    int length = returnType.length;
+    SimpleIdentifier constructorNameNode = constructor.name;
+    if (constructorNameNode != null) {
+      String constructorName = constructorNameNode.name;
+      name += ".${constructorName}";
+      offset = constructorNameNode.offset;
+      length = constructorNameNode.length;
+    }
+    _SourceRegion sourceRegion = _getSourceRegion(constructor);
+    FormalParameterList parameters = constructor.parameters;
+    String parametersStr = parameters != null ? parameters.toSource() : "";
+    _Outline outline = new _Outline(
+        _OutlineKind.CONSTRUCTOR, name,
+        offset, length,
+        sourceRegion.offset, sourceRegion.length,
+        false, false,
+        parametersStr, null);
+    parent.children.add(outline);
+    _addLocalFunctionOutlines(outline, constructor.body);
+  }
+
+  void _newFunctionOutline(_Outline parent, FunctionDeclaration function) {
+    TypeName returnType = function.returnType;
+    SimpleIdentifier nameNode = function.name;
+    String name = nameNode.name;
+    FunctionExpression functionExpression = function.functionExpression;
+    FormalParameterList parameters = functionExpression.parameters;
+    _OutlineKind kind;
+    if (function.isGetter) {
+      kind = _OutlineKind.GETTER;
+    } else if (function.isSetter) {
+      kind = _OutlineKind.SETTER;
+    } else {
+      kind = _OutlineKind.FUNCTION;
+    }
+    _SourceRegion sourceRegion = _getSourceRegion(function);
+    String parametersStr = parameters != null ? parameters.toSource() : "";
+    String returnTypeStr = returnType != null ? returnType.toSource() : "";
+    _Outline outline = new _Outline(
+        kind, name,
+        nameNode.offset, nameNode.length,
+        sourceRegion.offset, sourceRegion.length,
+        false, false,
+        parametersStr, returnTypeStr);
+    parent.children.add(outline);
+    _addLocalFunctionOutlines(outline, functionExpression.body);
+  }
+
+  void _newFunctionTypeAliasOutline(_Outline parent, FunctionTypeAlias alias) {
+    TypeName returnType = alias.returnType;
+    SimpleIdentifier nameNode = alias.name;
+    String name = nameNode.name;
+    _SourceRegion sourceRegion = _getSourceRegion(alias);
+    FormalParameterList parameters = alias.parameters;
+    String parametersStr = parameters != null ? parameters.toSource() : "";
+    String returnTypeStr = returnType != null ? returnType.toSource() : "";
+    _Outline outline = new _Outline(
+        _OutlineKind.FUNCTION_TYPE_ALIAS, name,
+        nameNode.offset, nameNode.length,
+        sourceRegion.offset, sourceRegion.length,
+        false, false,
+        parametersStr, returnTypeStr);
+    parent.children.add(outline);
+  }
+
+  void _newMethodOutline(_Outline parent, MethodDeclaration method) {
+    TypeName returnType = method.returnType;
+    SimpleIdentifier nameNode = method.name;
+    String name = nameNode.name;
+    FormalParameterList parameters = method.parameters;
+    _OutlineKind kind;
+    if (method.isGetter) {
+      kind = _OutlineKind.GETTER;
+    } else if (method.isSetter) {
+      kind = _OutlineKind.SETTER;
+    } else {
+      kind = _OutlineKind.METHOD;
+    }
+    _SourceRegion sourceRegion = _getSourceRegion(method);
+    String parametersStr = parameters != null ? parameters.toSource() : "";
+    String returnTypeStr = returnType != null ? returnType.toSource() : "";
+    _Outline outline = new _Outline(
+        kind, name,
+        nameNode.offset, nameNode.length,
+        sourceRegion.offset, sourceRegion.length,
+        method.isAbstract, method.isStatic,
+        parametersStr, returnTypeStr);
+    parent.children.add(outline);
+    _addLocalFunctionOutlines(outline, method.body);
+  }
+
+  _Outline _newUnitOutline() {
+    return new _Outline(
+        _OutlineKind.COMPILATION_UNIT, "<unit>",
+        _unit.offset, _unit.length,
+        _unit.offset, _unit.length,
+        false, false,
+        null, null);
+  }
+
+  void _newVariableOutline(_Outline parent, String typeName, _OutlineKind kind, VariableDeclaration variable, bool isStatic) {
+    SimpleIdentifier nameNode = variable.name;
+    String name = nameNode.name;
+    _SourceRegion sourceRegion = _getSourceRegion(variable);
+    _Outline outline = new _Outline(
+        kind, name,
+        nameNode.offset, nameNode.length,
+        sourceRegion.offset, sourceRegion.length,
+        false, isStatic,
+        null, typeName);
+    parent.children.add(outline);
+  }
+}
+
+
+/**
+ * A visitor for building local function outlines.
+ */
+class _LocalFunctionOutlinesVisitor extends RecursiveAstVisitor {
+  final DartUnitOutlineComputer outlineComputer;
+  final _Outline parent;
+
+  _LocalFunctionOutlinesVisitor(this.outlineComputer, this.parent);
+
+  @override
+  visitFunctionDeclaration(FunctionDeclaration node) {
+    outlineComputer._newFunctionOutline(parent, node);
+  }
+}
+
+
+
+/**
+ * A range of characters.
+ */
+class _SourceRegion {
+  final int offset;
+  final int length;
+  _SourceRegion(this.offset, this.length);
+}
+
+
+/**
+ * Element outline kinds.
+ */
+class _OutlineKind {
+  static const _OutlineKind CLASS = const _OutlineKind('CLASS');
+  static const _OutlineKind CLASS_TYPE_ALIAS = const _OutlineKind('CLASS_TYPE_ALIAS');
+  static const _OutlineKind COMPILATION_UNIT = const _OutlineKind('COMPILATION_UNIT');
+  static const _OutlineKind CONSTRUCTOR = const _OutlineKind('CONSTRUCTOR');
+  static const _OutlineKind GETTER = const _OutlineKind('GETTER');
+  static const _OutlineKind FIELD = const _OutlineKind('FIELD');
+  static const _OutlineKind FUNCTION = const _OutlineKind('FUNCTION');
+  static const _OutlineKind FUNCTION_TYPE_ALIAS = const _OutlineKind('FUNCTION_TYPE_ALIAS');
+  static const _OutlineKind LIBRARY = const _OutlineKind('LIBRARY');
+  static const _OutlineKind METHOD = const _OutlineKind('METHOD');
+  static const _OutlineKind SETTER = const _OutlineKind('SETTER');
+  static const _OutlineKind TOP_LEVEL_VARIABLE = const _OutlineKind('TOP_LEVEL_VARIABLE');
+  static const _OutlineKind UNKNOWN = const _OutlineKind('UNKNOWN');
+  static const _OutlineKind UNIT_TEST_CASE = const _OutlineKind('UNIT_TEST_CASE');
+  static const _OutlineKind UNIT_TEST_GROUP = const _OutlineKind('UNIT_TEST_GROUP');
+
+  final String name;
+
+  const _OutlineKind(this.name);
+}
+
+
+/**
+ * An element outline.
+ */
+class _Outline {
+  static const List<_Outline> EMPTY_ARRAY = const <_Outline>[];
+
+  final _OutlineKind kind;
+  final String name;
+  final int nameOffset;
+  final int nameLength;
+  final int elementOffset;
+  final int elementLength;
+  final bool isAbstract;
+  final bool isStatic;
+  final String parameters;
+  final String returnType;
+  final List<_Outline> children = <_Outline>[];
+
+  _Outline(this.kind, this.name,
+           this.nameOffset, this.nameLength,
+           this.elementOffset, this.elementLength,
+           this.isAbstract, this.isStatic,
+           this.parameters, this.returnType);
+
+  Map<String, Object> toJson() {
+    Map<String, Object> json = {
+      KIND: kind.name,
+      NAME: name,
+      NAME_OFFSET: nameOffset,
+      NAME_LENGTH: nameLength,
+      ELEMENT_OFFSET: elementOffset,
+      ELEMENT_LENGTH: elementLength,
+      IS_ABSTRACT: isAbstract,
+      IS_STATIC: isStatic
+    };
+    if (parameters != null) {
+      json[PARAMETERS] = parameters;
+    }
+    if (returnType != null) {
+      json[RETURN_TYPE] = returnType;
+    }
+    if (children.isNotEmpty) {
+      json[CHILDREN] = children.map((child) => child.toJson()).toList();
+    }
+    return json;
+  }
+}
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
new file mode 100644
index 0000000..2ea2e1d
--- /dev/null
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -0,0 +1,280 @@
+// 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 test.domain.analysis.abstract;
+
+import 'dart:async';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_analysis.dart';
+import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/resource.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:unittest/unittest.dart';
+
+import 'mocks.dart';
+
+
+/**
+ * An abstract base for all 'analysis' domain tests.
+ */
+class AbstractAnalysisTest {
+  MockServerChannel serverChannel;
+  MemoryResourceProvider resourceProvider;
+  AnalysisServer server;
+  AnalysisDomainHandler handler;
+
+  Map<String, List<String>> analysisSubscriptions = {};
+
+  String projectPath = '/project';
+  String testFile = '/project/bin/test.dart';
+  String testCode;
+
+//  Map<String, List<AnalysisError>> filesErrors = {};
+//  Map<String, List<Map<String, Object>>> filesHighlights = {};
+//  Map<String, List<Map<String, Object>>> filesNavigation = {};
+
+
+  AbstractAnalysisTest() {
+  }
+
+  void setUp() {
+    serverChannel = new MockServerChannel();
+    resourceProvider = new MemoryResourceProvider();
+    server = new AnalysisServer(serverChannel, resourceProvider);
+    handler = new AnalysisDomainHandler(server);
+    // listen for notifications
+    Stream<Notification> notificationStream = serverChannel.notificationController.stream;
+    notificationStream.listen((Notification notification) {
+      processNotification(notification);
+    });
+    // create an empty project
+    _createEmptyProject();
+  }
+
+  void addAnalysisSubscription(AnalysisService service, String file) {
+    // add file to subscription
+    var files = analysisSubscriptions[service.name];
+    if (files == null) {
+      files = <String>[];
+      analysisSubscriptions[service.name] = files;
+    }
+    files.add(file);
+    // set subscriptions
+    Request request = new Request('0', METHOD_SET_ANALYSIS_SUBSCRIPTIONS);
+    request.setParameter(SUBSCRIPTIONS, analysisSubscriptions);
+    handleSuccessfulRequest(request);
+  }
+
+  void tearDown() {
+    server.done();
+    handler = null;
+    server = null;
+    resourceProvider = null;
+    serverChannel = null;
+  }
+
+  void processNotification(Notification notification) {
+//    if (notification.event == NOTIFICATION_ERRORS) {
+//      String file = notification.getParameter(FILE);
+//      List<Map<String, Object>> errorMaps = notification.getParameter(ERRORS);
+//      filesErrors[file] = errorMaps.map(jsonToAnalysisError).toList();
+//    }
+//    if (notification.event == NOTIFICATION_HIGHLIGHTS) {
+//      String file = notification.getParameter(FILE);
+//      filesHighlights[file] = notification.getParameter(REGIONS);
+//    }
+//    if (notification.event == NOTIFICATION_NAVIGATION) {
+//      String file = notification.getParameter(FILE);
+//      filesNavigation[file] = notification.getParameter(REGIONS);
+//    }
+  }
+
+  /**
+   * Returns a [Future] that completes when the [AnalysisServer] finishes
+   * all its scheduled tasks.
+   */
+  Future waitForTasksFinished() {
+    return waitForServerOperationsPerformed(server);
+  }
+
+  /**
+   * Returns the offset of [search] in [testCode].
+   * Fails if not found.
+   */
+  int findFileOffset(String path, String search) {
+    File file = resourceProvider.getResource(path) as File;
+    String code = file.createSource(UriKind.FILE_URI).contents.data;
+    int offset = code.indexOf(search);
+    expect(offset, isNot(-1), reason: '"$search" in\n$code');
+    return offset;
+  }
+
+  /**
+   * Returns the offset of [search] in [testCode].
+   * Fails if not found.
+   */
+  int findOffset(String search) {
+    int offset = testCode.indexOf(search);
+    expect(offset, isNot(-1));
+    return offset;
+  }
+
+//  /**
+//   * Returns [AnalysisError]s recorded for the given [file].
+//   * May be empty, but not `null`.
+//   */
+//  List<AnalysisError> getErrors(String file) {
+//    List<AnalysisError> errors = filesErrors[file];
+//    if (errors != null) {
+//      return errors;
+//    }
+//    return <AnalysisError>[];
+//  }
+//
+//  /**
+//   * Returns highlights recorded for the given [file].
+//   * May be empty, but not `null`.
+//   */
+//  List<Map<String, Object>> getHighlights(String file) {
+//    List<Map<String, Object>> highlights = filesHighlights[file];
+//    if (highlights != null) {
+//      return highlights;
+//    }
+//    return [];
+//  }
+//
+//  /**
+//   * Returns navigation regions recorded for the given [file].
+//   * May be empty, but not `null`.
+//   */
+//  List<Map<String, Object>> getNavigation(String file) {
+//    List<Map<String, Object>> navigation = filesNavigation[file];
+//    if (navigation != null) {
+//      return navigation;
+//    }
+//    return [];
+//  }
+//
+//  /**
+//   * Returns [AnalysisError]s recorded for the [testFile].
+//   * May be empty, but not `null`.
+//   */
+//  List<AnalysisError> getTestErrors() {
+//    return getErrors(testFile);
+//  }
+//
+//  /**
+//   * Returns highlights recorded for the given [testFile].
+//   * May be empty, but not `null`.
+//   */
+//  List<Map<String, Object>> getTestHighlights() {
+//    return getHighlights(testFile);
+//  }
+//
+//  /**
+//   * Returns navigation information recorded for the given [testFile].
+//   * May be empty, but not `null`.
+//   */
+//  List<Map<String, Object>> getTestNavigation() {
+//    return getNavigation(testFile);
+//  }
+
+  /**
+   * Creates an empty project `/project/`.
+   */
+  void _createEmptyProject() {
+    resourceProvider.newFolder(projectPath);
+    Request request = new Request('0', METHOD_SET_ANALYSIS_ROOTS);
+    request.setParameter(INCLUDED, [projectPath]);
+    request.setParameter(EXCLUDED, []);
+    handleSuccessfulRequest(request);
+  }
+
+//  /**
+//   * Creates a project with a single Dart file `/project/bin/test.dart` with
+//   * the given [code].
+//   */
+//  void createSingleFileProject(code) {
+//    this.testCode = _getCodeString(code);
+//    resourceProvider.newFolder('/project');
+//    resourceProvider.newFile(testFile, testCode);
+//    Request request = new Request('0', METHOD_SET_ANALYSIS_ROOTS);
+//    request.setParameter(INCLUDED, ['/project']);
+//    request.setParameter(EXCLUDED, []);
+//    handleSuccessfulRequest(request);
+//  }
+
+  String addFile(String path, String content) {
+    resourceProvider.newFile(path, content);
+    return path;
+  }
+
+  String addTestFile(String content) {
+    addFile(testFile, content);
+    this.testCode = content;
+    return testFile;
+  }
+
+  /**
+   * Validates that the given [request] is handled successfully.
+   */
+  void handleSuccessfulRequest(Request request) {
+    Response response = handler.handleRequest(request);
+    expect(response, isResponseSuccess('0'));
+  }
+
+  static String _getCodeString(code) {
+    if (code is List<String>) {
+      code = code.join('\n');
+    }
+    return code as String;
+  }
+}
+
+
+
+class AnalysisError {
+  final String file;
+  final String errorCode;
+  final int offset;
+  final int length;
+  final String message;
+  final String correction;
+  AnalysisError(this.file, this.errorCode, this.offset, this.length,
+      this.message, this.correction);
+
+  @override
+  String toString() {
+    return 'NotificationError(file=$file; errorCode=$errorCode; '
+        'offset=$offset; length=$length; message=$message)';
+  }
+}
+
+
+AnalysisError _jsonToAnalysisError(Map<String, Object> json) {
+  return new AnalysisError(
+      json['file'],
+      json['errorCode'],
+      json['offset'],
+      json['length'],
+      json['message'],
+      json['correction']);
+}
+
+
+int findIdentifierLength(String search) {
+  int length = 0;
+  while (length < search.length) {
+    int c = search.codeUnitAt(length);
+    if (!(c >= 'a'.codeUnitAt(0) && c <= 'z'.codeUnitAt(0) ||
+          c >= 'A'.codeUnitAt(0) && c <= 'Z'.codeUnitAt(0) ||
+          c >= '0'.codeUnitAt(0) && c <= '9'.codeUnitAt(0))) {
+      break;
+    }
+    length++;
+  }
+  return length;
+}
diff --git a/pkg/analysis_server/test/analysis_notification_outline_test.dart b/pkg/analysis_server/test/analysis_notification_outline_test.dart
new file mode 100644
index 0000000..49011ef
--- /dev/null
+++ b/pkg/analysis_server/test/analysis_notification_outline_test.dart
@@ -0,0 +1,681 @@
+// 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 test.domain.analysis.notification.outline;
+
+import 'dart:async';
+import 'dart:mirrors';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:unittest/unittest.dart';
+
+import 'analysis_abstract.dart';
+import 'reflective_tests.dart';
+
+
+main() {
+  group('notification.outline', () {
+    runReflectiveTests(AnalysisNotificationOutlineTest);
+  });
+}
+
+
+@ReflectiveTestCase()
+class AnalysisNotificationOutlineTest extends AbstractAnalysisTest {
+  _Outline outline;
+
+  void processNotification(Notification notification) {
+    if (notification.event == NOTIFICATION_OUTLINE) {
+      String file = notification.getParameter(FILE);
+      if (file == testFile) {
+        Map<String, Object> json = notification.getParameter(OUTLINE);
+        outline = new _Outline.fromJson(json);
+      }
+    }
+  }
+
+  Future prepareOutline(then()) {
+    addAnalysisSubscription(AnalysisService.OUTLINE, testFile);
+    return waitForTasksFinished().then((_) {
+      then();
+    });
+  }
+
+  test_class() {
+    addTestFile('''
+class A {
+  int fa, fb;
+  String fc;
+  A(int i, String s);
+  A.name(num p);
+  A._privateName(num p);
+  static String ma(int pa) => null;
+  _mb(int pb);
+  String get propA => null;
+  set propB(int v) {}
+}
+class B {
+  B(int p);
+}");
+''');
+    return prepareOutline(() {
+      _Outline unitOutline = outline;
+      List<_Outline> topOutlines = unitOutline.children;
+      expect(topOutlines, hasLength(2));
+      // A
+      {
+        _Outline outline_A = topOutlines[0];
+        expect(outline_A.kind, _OutlineKind.CLASS);
+        expect(outline_A.name, "A");
+        expect(outline_A.nameOffset, testCode.indexOf("A {"));
+        expect(outline_A.nameLength, 1);
+        expect(outline_A.parameters, null);
+        expect(outline_A.returnType, null);
+        // A children
+        List<_Outline> outlines_A = outline_A.children;
+        expect(outlines_A, hasLength(10));
+        {
+          _Outline outline = outlines_A[0];
+          expect(outline.kind, _OutlineKind.FIELD);
+          expect(outline.name, "fa");
+          expect(outline.parameters, isNull);
+          expect(outline.returnType, "int");
+        }
+        {
+          _Outline outline = outlines_A[1];
+          expect(outline.kind, _OutlineKind.FIELD);
+          expect(outline.name, "fb");
+          expect(outline.parameters, isNull);
+          expect(outline.returnType, "int");
+        }
+        {
+          _Outline outline = outlines_A[2];
+          expect(outline.kind, _OutlineKind.FIELD);
+          expect(outline.name, "fc");
+          expect(outline.parameters, isNull);
+          expect(outline.returnType, "String");
+        }
+        {
+          _Outline outline = outlines_A[3];
+          expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+          expect(outline.name, "A");
+          expect(outline.nameOffset, testCode.indexOf("A(int i, String s);"));
+          expect(outline.nameLength, "A".length);
+          expect(outline.parameters, "(int i, String s)");
+          expect(outline.returnType, isNull);
+          expect(outline.isAbstract, isFalse);
+          expect(outline.isStatic, isFalse);
+        }
+        {
+          _Outline outline = outlines_A[4];
+          expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+          expect(outline.name, "A.name");
+          expect(outline.nameOffset, testCode.indexOf("name(num p);"));
+          expect(outline.nameLength, "name".length);
+          expect(outline.parameters, "(num p)");
+          expect(outline.returnType, isNull);
+          expect(outline.isAbstract, isFalse);
+          expect(outline.isStatic, isFalse);
+        }
+        {
+          _Outline outline = outlines_A[5];
+          expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+          expect(outline.name, "A._privateName");
+          expect(outline.nameOffset, testCode.indexOf("_privateName(num p);"));
+          expect(outline.nameLength, "_privateName".length);
+          expect(outline.parameters, "(num p)");
+          expect(outline.returnType, isNull);
+          expect(outline.isAbstract, isFalse);
+          expect(outline.isStatic, isFalse);
+        }
+        {
+          _Outline outline = outlines_A[6];
+          expect(outline.kind, _OutlineKind.METHOD);
+          expect(outline.name, "ma");
+          expect(outline.nameOffset, testCode.indexOf("ma(int pa) => null;"));
+          expect(outline.nameLength, "ma".length);
+          expect(outline.parameters, "(int pa)");
+          expect(outline.returnType, "String");
+          expect(outline.isAbstract, isFalse);
+          expect(outline.isStatic, isTrue);
+        }
+        {
+          _Outline outline = outlines_A[7];
+          expect(outline.kind, _OutlineKind.METHOD);
+          expect(outline.name, "_mb");
+          expect(outline.nameOffset, testCode.indexOf("_mb(int pb);"));
+          expect(outline.nameLength, "_mb".length);
+          expect(outline.parameters, "(int pb)");
+          expect(outline.returnType, "");
+          expect(outline.isAbstract, isTrue);
+          expect(outline.isStatic, isFalse);
+        }
+        {
+          _Outline outline = outlines_A[8];
+          expect(outline.kind, _OutlineKind.GETTER);
+          expect(outline.name, "propA");
+          expect(outline.nameOffset, testCode.indexOf("propA => null;"));
+          expect(outline.nameLength, "propA".length);
+          expect(outline.parameters, "");
+          expect(outline.returnType, "String");
+        }
+        {
+          _Outline outline = outlines_A[9];
+          expect(outline.kind, _OutlineKind.SETTER);
+          expect(outline.name, "propB");
+          expect(outline.nameOffset, testCode.indexOf("propB(int v) {}"));
+          expect(outline.nameLength, "propB".length);
+          expect(outline.parameters, "(int v)");
+          expect(outline.returnType, "");
+        }
+      }
+      // B
+      {
+        _Outline outline_B = topOutlines[1];
+        expect(outline_B.kind, _OutlineKind.CLASS);
+        expect(outline_B.name, "B");
+        expect(outline_B.nameOffset, testCode.indexOf("B {"));
+        expect(outline_B.nameLength, 1);
+        expect(outline_B.parameters, null);
+        expect(outline_B.returnType, null);
+        // B children
+        List<_Outline> outlines_B = outline_B.children;
+        expect(outlines_B, hasLength(1));
+        {
+          _Outline outline = outlines_B[0];
+          expect(outline.kind, _OutlineKind.CONSTRUCTOR);
+          expect(outline.name, "B");
+          expect(outline.nameOffset, testCode.indexOf("B(int p);"));
+          expect(outline.nameLength, "B".length);
+          expect(outline.parameters, "(int p)");
+          expect(outline.returnType, isNull);
+        }
+      }
+    });
+  }
+
+  test_sourceRange_inClass() {
+    addTestFile('''
+class A { // leftA
+  int methodA() {} // endA
+  int methodB() {} // endB
+}
+''');
+    return prepareOutline(() {
+      _Outline unitOutline = outline;
+      List<_Outline> outlines = unitOutline.children[0].children;
+      expect(outlines, hasLength(2));
+      // methodA
+      {
+        _Outline outline = outlines[0];
+        expect(outline.kind, _OutlineKind.METHOD);
+        expect(outline.name, "methodA");
+        {
+          int offset = testCode.indexOf(" // leftA");
+          int end = testCode.indexOf(" // endA");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // methodB
+      {
+        _Outline outline = outlines[1];
+        expect(outline.kind, _OutlineKind.METHOD);
+        expect(outline.name, "methodB");
+        {
+          int offset = testCode.indexOf(" // endA");
+          int end = testCode.indexOf(" // endB");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+    });
+  }
+
+  test_sourceRange_inClass_inVariableList() {
+    addTestFile('''
+class A { // leftA
+  int fieldA, fieldB, fieldC; // marker
+  int fieldD; // marker2
+}
+''');
+    return prepareOutline(() {
+      _Outline unitOutline = outline;
+      List<_Outline> outlines = unitOutline.children[0].children;
+      expect(outlines, hasLength(4));
+      // fieldA
+      {
+        _Outline outline = outlines[0];
+        expect(outline.kind, _OutlineKind.FIELD);
+        expect(outline.name, "fieldA");
+        {
+          int offset = testCode.indexOf(" // leftA");
+          int end = testCode.indexOf(", fieldB");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // fieldB
+      {
+        _Outline outline = outlines[1];
+        expect(outline.kind, _OutlineKind.FIELD);
+        expect(outline.name, "fieldB");
+        {
+          int offset = testCode.indexOf(", fieldB");
+          int end = testCode.indexOf(", fieldC");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // fieldC
+      {
+        _Outline outline = outlines[2];
+        expect(outline.kind, _OutlineKind.FIELD);
+        expect(outline.name, "fieldC");
+        {
+          int offset = testCode.indexOf(", fieldC");
+          int end = testCode.indexOf(" // marker");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // fieldD
+      {
+        _Outline outline = outlines[3];
+        expect(outline.kind, _OutlineKind.FIELD);
+        expect(outline.name, "fieldD");
+        {
+          int offset = testCode.indexOf(" // marker");
+          int end = testCode.indexOf(" // marker2");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+    });
+  }
+
+  test_sourceRange_inUnit() {
+    addTestFile('''
+class A {
+} // endA
+class B {
+} // endB
+''');
+    return prepareOutline(() {
+      _Outline unitOutline = outline;
+      List<_Outline> topOutlines = unitOutline.children;
+      expect(topOutlines, hasLength(2));
+      // A
+      {
+        _Outline outline = topOutlines[0];
+        expect(outline.kind, _OutlineKind.CLASS);
+        expect(outline.name, "A");
+        {
+          int offset = 0;
+          int end = testCode.indexOf(" // endA");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // B
+      {
+        _Outline outline = topOutlines[1];
+        expect(outline.kind, _OutlineKind.CLASS);
+        expect(outline.name, "B");
+        {
+          int offset = testCode.indexOf(" // endA");
+          int end = testCode.indexOf(" // endB");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+    });
+  }
+
+  test_sourceRange_inUnit_inVariableList() {
+    addTestFile('''
+int fieldA, fieldB, fieldC; // marker
+int fieldD; // marker2
+''');
+    return prepareOutline(() {
+      _Outline unitOutline = outline;
+      List<_Outline> outlines = unitOutline.children;
+      expect(outlines, hasLength(4));
+      // fieldA
+      {
+        _Outline outline = outlines[0];
+        expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+        expect(outline.name, "fieldA");
+        {
+          int offset = 0;
+          int end = testCode.indexOf(", fieldB");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // fieldB
+      {
+        _Outline outline = outlines[1];
+        expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+        expect(outline.name, "fieldB");
+        {
+          int offset = testCode.indexOf(", fieldB");
+          int end = testCode.indexOf(", fieldC");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // fieldC
+      {
+        _Outline outline = outlines[2];
+        expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+        expect(outline.name, "fieldC");
+        {
+          int offset = testCode.indexOf(", fieldC");
+          int end = testCode.indexOf(" // marker");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+      // fieldD
+      {
+        _Outline outline = outlines[3];
+        expect(outline.kind, _OutlineKind.TOP_LEVEL_VARIABLE);
+        expect(outline.name, "fieldD");
+        {
+          int offset = testCode.indexOf(" // marker");
+          int end = testCode.indexOf(" // marker2");
+          expect(outline.elementOffset, offset);
+          expect(outline.elementLength, end - offset);
+        }
+      }
+    });
+  }
+
+  test_localFunctions() {
+    addTestFile('''
+class A {
+  A() {
+    int local_A() {}
+  }
+  m() {
+    local_m() {}
+  }
+}
+f() {
+  local_f1(int i) {}
+  local_f2(String s) {
+    local_f21(int p) {}
+  }
+}
+''');
+    return prepareOutline(() {
+      _Outline unitOutline = outline;
+      List<_Outline> topOutlines = unitOutline.children;
+      expect(topOutlines, hasLength(2));
+      // A
+      {
+        _Outline outline_A = topOutlines[0];
+        expect(outline_A.kind, _OutlineKind.CLASS);
+        expect(outline_A.name, "A");
+        expect(outline_A.nameOffset, testCode.indexOf("A {"));
+        expect(outline_A.nameLength, "A".length);
+        expect(outline_A.parameters, null);
+        expect(outline_A.returnType, null);
+        // A children
+        List<_Outline> outlines_A = outline_A.children;
+        expect(outlines_A, hasLength(2));
+        {
+          _Outline constructorOutline = outlines_A[0];
+          expect(constructorOutline.kind, _OutlineKind.CONSTRUCTOR);
+          expect(constructorOutline.name, "A");
+          expect(constructorOutline.nameOffset, testCode.indexOf("A() {"));
+          expect(constructorOutline.nameLength, "A".length);
+          expect(constructorOutline.parameters, "()");
+          expect(constructorOutline.returnType, isNull);
+          // local function
+          List<_Outline> outlines_constructor = constructorOutline.children;
+          expect(outlines_constructor, hasLength(1));
+          {
+            _Outline outline = outlines_constructor[0];
+            expect(outline.kind, _OutlineKind.FUNCTION);
+            expect(outline.name, "local_A");
+            expect(outline.nameOffset, testCode.indexOf("local_A() {}"));
+            expect(outline.nameLength, "local_A".length);
+            expect(outline.parameters, "()");
+            expect(outline.returnType, "int");
+          }
+        }
+        {
+          _Outline outline_m = outlines_A[1];
+          expect(outline_m.kind, _OutlineKind.METHOD);
+          expect(outline_m.name, "m");
+          expect(outline_m.nameOffset, testCode.indexOf("m() {"));
+          expect(outline_m.nameLength, "m".length);
+          expect(outline_m.parameters, "()");
+          expect(outline_m.returnType, "");
+          // local function
+          List<_Outline> methodChildren = outline_m.children;
+          expect(methodChildren, hasLength(1));
+          {
+            _Outline outline = methodChildren[0];
+            expect(outline.kind, _OutlineKind.FUNCTION);
+            expect(outline.name, "local_m");
+            expect(outline.nameOffset, testCode.indexOf("local_m() {}"));
+            expect(outline.nameLength, "local_m".length);
+            expect(outline.parameters, "()");
+            expect(outline.returnType, "");
+          }
+        }
+      }
+      // f()
+      {
+        _Outline outline_f = topOutlines[1];
+        expect(outline_f.kind, _OutlineKind.FUNCTION);
+        expect(outline_f.name, "f");
+        expect(outline_f.nameOffset, testCode.indexOf("f() {"));
+        expect(outline_f.nameLength, "f".length);
+        expect(outline_f.parameters, "()");
+        expect(outline_f.returnType, "");
+        // f() children
+        List<_Outline> outlines_f = outline_f.children;
+        expect(outlines_f, hasLength(2));
+        {
+          _Outline outline_f1 = outlines_f[0];
+          expect(outline_f1.kind, _OutlineKind.FUNCTION);
+          expect(outline_f1.name, "local_f1");
+          expect(outline_f1.nameOffset, testCode.indexOf("local_f1(int i) {}"));
+          expect(outline_f1.nameLength, "local_f1".length);
+          expect(outline_f1.parameters, "(int i)");
+          expect(outline_f1.returnType, "");
+        }
+        {
+          _Outline outline_f2 = outlines_f[1];
+          expect(outline_f2.kind, _OutlineKind.FUNCTION);
+          expect(outline_f2.name, "local_f2");
+          expect(outline_f2.nameOffset, testCode.indexOf("local_f2(String s) {"));
+          expect(outline_f2.nameLength, "local_f2".length);
+          expect(outline_f2.parameters, "(String s)");
+          expect(outline_f2.returnType, "");
+          // local_f2() local function
+          List<_Outline> outlines_f2 = outline_f2.children;
+          expect(outlines_f2, hasLength(1));
+          {
+            _Outline outline_f21 = outlines_f2[0];
+            expect(outline_f21.kind, _OutlineKind.FUNCTION);
+            expect(outline_f21.name, "local_f21");
+            expect(outline_f21.nameOffset, testCode.indexOf("local_f21(int p) {"));
+            expect(outline_f21.nameLength, "local_f21".length);
+            expect(outline_f21.parameters, "(int p)");
+            expect(outline_f21.returnType, "");
+          }
+        }
+      }
+    });
+  }
+
+  test_topLevel() {
+    addTestFile('''
+typedef String FTA(int i, String s);
+typedef FTB(int p);
+class A {}
+class B {}
+class CTA = A with B;
+String fA(int i, String s) => null;
+fB(int p) => null;
+String get propA => null;
+set propB(int v) {}
+''');
+    return prepareOutline(() {
+      _Outline unitOutline = outline;
+      List<_Outline> topOutlines = unitOutline.children;
+      expect(topOutlines, hasLength(9));
+      // FTA
+      {
+        _Outline outline = topOutlines[0];
+        expect(outline.kind, _OutlineKind.FUNCTION_TYPE_ALIAS);
+        expect(outline.name, "FTA");
+        expect(outline.nameOffset, testCode.indexOf("FTA("));
+        expect(outline.nameLength, "FTA".length);
+        expect(outline.parameters, "(int i, String s)");
+        expect(outline.returnType, "String");
+      }
+      // FTB
+      {
+        _Outline outline = topOutlines[1];
+        expect(outline.kind, _OutlineKind.FUNCTION_TYPE_ALIAS);
+        expect(outline.name, "FTB");
+        expect(outline.nameOffset, testCode.indexOf("FTB("));
+        expect(outline.nameLength, "FTB".length);
+        expect(outline.parameters, "(int p)");
+        expect(outline.returnType, "");
+      }
+      // CTA
+      {
+        _Outline outline = topOutlines[4];
+        expect(outline.kind, _OutlineKind.CLASS_TYPE_ALIAS);
+        expect(outline.name, "CTA");
+        expect(outline.nameOffset, testCode.indexOf("CTA ="));
+        expect(outline.nameLength, "CTA".length);
+        expect(outline.parameters, isNull);
+        expect(outline.returnType, isNull);
+      }
+      // fA
+      {
+        _Outline outline = topOutlines[5];
+        expect(outline.kind, _OutlineKind.FUNCTION);
+        expect(outline.name, "fA");
+        expect(outline.nameOffset, testCode.indexOf("fA("));
+        expect(outline.nameLength, "fA".length);
+        expect(outline.parameters, "(int i, String s)");
+        expect(outline.returnType, "String");
+      }
+      // fB
+      {
+        _Outline outline = topOutlines[6];
+        expect(outline.kind, _OutlineKind.FUNCTION);
+        expect(outline.name, "fB");
+        expect(outline.nameOffset, testCode.indexOf("fB("));
+        expect(outline.nameLength, "fB".length);
+        expect(outline.parameters, "(int p)");
+        expect(outline.returnType, "");
+      }
+      // propA
+      {
+        _Outline outline = topOutlines[7];
+        expect(outline.kind, _OutlineKind.GETTER);
+        expect(outline.name, "propA");
+        expect(outline.nameOffset, testCode.indexOf("propA => null;"));
+        expect(outline.nameLength, "propA".length);
+        expect(outline.parameters, "");
+        expect(outline.returnType, "String");
+      }
+      // propB
+      {
+        _Outline outline = topOutlines[8];
+        expect(outline.kind, _OutlineKind.SETTER);
+        expect(outline.name, "propB");
+        expect(outline.nameOffset, testCode.indexOf("propB(int v) {}"));
+        expect(outline.nameLength, "propB".length);
+        expect(outline.parameters, "(int v)");
+        expect(outline.returnType, "");
+      }
+    });
+  }
+}
+
+
+/**
+ * Element outline kinds.
+ */
+class _OutlineKind {
+  static const _OutlineKind CLASS = const _OutlineKind('CLASS');
+  static const _OutlineKind CLASS_TYPE_ALIAS = const _OutlineKind('CLASS_TYPE_ALIAS');
+  static const _OutlineKind COMPILATION_UNIT = const _OutlineKind('COMPILATION_UNIT');
+  static const _OutlineKind CONSTRUCTOR = const _OutlineKind('CONSTRUCTOR');
+  static const _OutlineKind GETTER = const _OutlineKind('GETTER');
+  static const _OutlineKind FIELD = const _OutlineKind('FIELD');
+  static const _OutlineKind FUNCTION = const _OutlineKind('FUNCTION');
+  static const _OutlineKind FUNCTION_TYPE_ALIAS = const _OutlineKind('FUNCTION_TYPE_ALIAS');
+  static const _OutlineKind LIBRARY = const _OutlineKind('LIBRARY');
+  static const _OutlineKind METHOD = const _OutlineKind('METHOD');
+  static const _OutlineKind SETTER = const _OutlineKind('SETTER');
+  static const _OutlineKind TOP_LEVEL_VARIABLE = const _OutlineKind('TOP_LEVEL_VARIABLE');
+  static const _OutlineKind UNKNOWN = const _OutlineKind('UNKNOWN');
+  static const _OutlineKind UNIT_TEST_CASE = const _OutlineKind('UNIT_TEST_CASE');
+  static const _OutlineKind UNIT_TEST_GROUP = const _OutlineKind('UNIT_TEST_GROUP');
+
+  final String name;
+
+  const _OutlineKind(this.name);
+
+  static _OutlineKind valueOf(String name) {
+    ClassMirror classMirror = reflectClass(_OutlineKind);
+    return classMirror.getField(new Symbol(name)).reflectee;
+  }
+}
+
+
+/**
+ * An element outline.
+ */
+class _Outline {
+  static const List<_Outline> EMPTY_ARRAY = const <_Outline>[];
+
+  final _OutlineKind kind;
+  final String name;
+  final int nameOffset;
+  final int nameLength;
+  final int elementOffset;
+  final int elementLength;
+  final bool isAbstract;
+  final bool isStatic;
+  final String parameters;
+  final String returnType;
+  final List<_Outline> children = <_Outline>[];
+
+  _Outline(this.kind, this.name,
+           this.nameOffset, this.nameLength,
+           this.elementOffset, this.elementLength,
+           this.isAbstract, this.isStatic,
+           this.parameters, this.returnType);
+
+  factory _Outline.fromJson(Map<String, Object> map) {
+    _Outline outline = new _Outline(
+        _OutlineKind.valueOf(map[KIND]), map[NAME],
+        map[NAME_OFFSET], map[NAME_LENGTH],
+        map[ELEMENT_OFFSET], map[ELEMENT_LENGTH],
+        map[IS_ABSTRACT], map[IS_STATIC],
+        map[PARAMETERS], map[RETURN_TYPE]);
+    List<Map<String, Object>> childrenMaps = map[CHILDREN];
+    if (childrenMaps != null) {
+      childrenMaps.forEach((childMap) {
+        outline.children.add(new _Outline.fromJson(childMap));
+      });
+    }
+    return outline;
+  }
+}
diff --git a/pkg/http_multi_server/LICENSE b/pkg/http_multi_server/LICENSE
new file mode 100644
index 0000000..5c60afe
--- /dev/null
+++ b/pkg/http_multi_server/LICENSE
@@ -0,0 +1,26 @@
+Copyright 2014, the Dart project 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/pkg/http_multi_server/README.md b/pkg/http_multi_server/README.md
new file mode 100644
index 0000000..e12c8d1
--- /dev/null
+++ b/pkg/http_multi_server/README.md
@@ -0,0 +1,25 @@
+An implementation of `dart:io`'s [HttpServer][] that wraps multiple servers and
+forwards methods to all of them. It's useful for serving the same application on
+multiple network interfaces while still having a unified way of controlling the
+servers. In particular, it supports serving on both the IPv4 and IPv6 loopback
+addresses using [HttpMultiServer.loopback][].
+
+```dart
+import 'package:http_multi_server/http_multi_server.dart';
+import 'package:shelf/shelf.dart' as shelf;
+import 'package:shelf/shelf_io.dart' as shelf_io;
+
+void main() {
+  // Both http://127.0.0.1:8080 and http://[::1]:8080 will be bound to the same
+  // server.
+  HttpMultiServer.loopback(8080).then((server) {
+    shelf_io.serveRequests(server, (request) {
+      return new shelf.Response.ok("Hello, world!");
+    });
+  });
+}
+```
+
+[HttpServer]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/dart-io.HttpServer
+
+[HttpMultiServer.loopback]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/http_multi_server/http_multi_server.HttpMultiServer#id_loopback
diff --git a/pkg/http_multi_server/lib/http_multi_server.dart b/pkg/http_multi_server/lib/http_multi_server.dart
new file mode 100644
index 0000000..c055367
--- /dev/null
+++ b/pkg/http_multi_server/lib/http_multi_server.dart
@@ -0,0 +1,136 @@
+// 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 http_multi_server;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'src/utils.dart';
+
+/// An implementation of `dart:io`'s [HttpServer] that wraps multiple servers
+/// and forwards methods to all of them.
+///
+/// This is useful for serving the same application on multiple network
+/// interfaces while still having a unified way of controlling the servers. In
+/// particular, it supports serving on both the IPv4 and IPv6 loopback addresses
+/// using [HttpMultiServer.loopback].
+class HttpMultiServer extends StreamView<HttpRequest> implements HttpServer {
+  /// The wrapped servers.
+  final Set<HttpServer> _servers;
+
+  String get serverHeader => _servers.first.serverHeader;
+  set serverHeader(String value) {
+    for (var server in _servers) {
+      server.serverHeader = value;
+    }
+  }
+
+  Duration get idleTimeout => _servers.first.idleTimeout;
+  set idleTimeout(Duration value) {
+    for (var server in _servers) {
+      server.idleTimeout = value;
+    }
+  }
+
+  /// Returns the port that one of the wrapped servers is listening on.
+  ///
+  /// If the wrapped servers are listening on different ports, it's not defined
+  /// which port is returned.
+  int get port => _servers.first.port;
+
+  /// Returns the address that one of the wrapped servers is listening on.
+  ///
+  /// If the wrapped servers are listening on different addresses, it's not
+  /// defined which address is returned.
+  InternetAddress get address => _servers.first.address;
+
+  set sessionTimeout(int value) {
+    for (var server in _servers) {
+      server.sessionTimeout = value;
+    }
+  }
+
+  /// Creates an [HttpMultiServer] wrapping [servers].
+  ///
+  /// All [servers] should have the same configuration and none should be
+  /// listened to when this is called.
+  HttpMultiServer(Iterable<HttpServer> servers)
+      : _servers = servers.toSet(),
+        super(mergeStreams(servers));
+
+  /// Creates an [HttpServer] listening on all available loopback addresses for
+  /// this computer.
+  ///
+  /// If this computer supports both IPv4 and IPv6, this returns an
+  /// [HttpMultiServer] listening to [port] on both loopback addresses.
+  /// Otherwise, it returns a normal [HttpServer] listening only on the IPv4
+  /// address.
+  ///
+  /// If [port] is 0, the same ephemeral port is used for both the IPv4 and IPv6
+  /// addresses.
+  static Future<HttpServer> loopback(int port, {int backlog}) {
+    if (backlog == null) backlog = 0;
+
+    return _loopback(port, (address, port) =>
+        HttpServer.bind(address, port, backlog: backlog));
+  }
+
+  /// Like [loopback], but supports HTTPS requests.
+  ///
+  /// The certificate with nickname or distinguished name (DN) [certificateName]
+  /// is looked up in the certificate database, and is used as the server
+  /// certificate. If [requestClientCertificate] is true, the server will
+  /// request clients to authenticate with a client certificate.
+  static Future<HttpServer> loopbackSecure(int port, {int backlog,
+      String certificateName, bool requestClientCertificate: false}) {
+    if (backlog == null) backlog = 0;
+
+    return _loopback(port, (address, port) =>
+        HttpServer.bindSecure(address, port, backlog: backlog,
+            certificateName: certificateName,
+            requestClientCertificate: requestClientCertificate));
+  }
+
+  /// A helper method for initializing loopback servers.
+  ///
+  /// [bind] should forward to either [HttpServer.bind] or
+  /// [HttpServer.bindSecure].
+  static Future<HttpServer> _loopback(int port,
+      Future<HttpServer> bind(InternetAddress address, int port)) {
+    return Future.wait([
+      supportsIpV6,
+      bind(InternetAddress.LOOPBACK_IP_V4, port)
+    ]).then((results) {
+      var supportsIpV6 = results[0];
+      var v4Server = results[1];
+
+      if (!supportsIpV6) return v4Server;
+
+      // Reuse the IPv4 server's port so that if [port] is 0, both servers use
+      // the same ephemeral port.
+      return bind(InternetAddress.LOOPBACK_IP_V6, v4Server.port)
+          .then((v6Server) {
+        return new HttpMultiServer([v4Server, v6Server]);
+      });
+    });
+  }
+
+  Future close({bool force: false}) =>
+      Future.wait(_servers.map((server) => server.close(force: force)));
+
+  /// Returns an HttpConnectionsInfo object summarizing the total number of
+  /// current connections handled by all the servers.
+  HttpConnectionsInfo connectionsInfo() {
+    var info = new HttpConnectionsInfo();
+    for (var server in _servers) {
+      var subInfo = server.connectionsInfo();
+      info.total += subInfo.total;
+      info.active += subInfo.active;
+      info.idle += subInfo.idle;
+      info.closing += subInfo.closing;
+    }
+    return info;
+  }
+}
diff --git a/pkg/http_multi_server/lib/src/utils.dart b/pkg/http_multi_server/lib/src/utils.dart
new file mode 100644
index 0000000..9e95aba
--- /dev/null
+++ b/pkg/http_multi_server/lib/src/utils.dart
@@ -0,0 +1,62 @@
+// 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 http_multi_server.utils;
+
+import 'dart:async';
+import 'dart:io';
+
+/// Merges all streams in [streams] into a single stream that emits all of their
+/// values.
+///
+/// The returned stream will be closed only when every stream in [streams] is
+/// closed.
+Stream mergeStreams(Iterable<Stream> streams) {
+  var subscriptions = new Set();
+  var controller;
+  controller = new StreamController(onListen: () {
+    for (var stream in streams) {
+      var subscription;
+      subscription = stream.listen(controller.add,
+          onError: controller.addError,
+          onDone: () {
+        subscriptions.remove(subscription);
+        if (subscriptions.isEmpty) controller.close();
+      });
+      subscriptions.add(subscription);
+    }
+  }, onCancel: () {
+    for (var subscription in subscriptions) {
+      subscription.cancel();
+    }
+  }, onPause: () {
+    for (var subscription in subscriptions) {
+      subscription.pause();
+    }
+  }, onResume: () {
+    for (var subscription in subscriptions) {
+      subscription.resume();
+    }
+  }, sync: true);
+
+  return controller.stream;
+}
+
+/// A cache for [supportsIpV6].
+bool _supportsIpV6;
+
+/// Returns whether this computer supports binding to IPv6 addresses.
+Future<bool> get supportsIpV6 {
+  if (_supportsIpV6 != null) return new Future.value(_supportsIpV6);
+
+  return ServerSocket.bind(InternetAddress.LOOPBACK_IP_V6, 0).then((socket) {
+    _supportsIpV6 = true;
+    socket.close();
+    return true;
+  }).catchError((error) {
+    if (error is! SocketException) throw error;
+    _supportsIpV6 = false;
+    return false;
+  });
+}
diff --git a/pkg/http_multi_server/pubspec.yaml b/pkg/http_multi_server/pubspec.yaml
new file mode 100644
index 0000000..4199c58
--- /dev/null
+++ b/pkg/http_multi_server/pubspec.yaml
@@ -0,0 +1,11 @@
+name: http_multi_server
+version: 1.0.0
+author: "Dart Team <misc@dartlang.org>"
+homepage: http://www.dartlang.org
+description:
+  A dart:io HttpServer wrapper that handles requests from multiple servers.
+dev_dependencies:
+  unittest: ">=0.11.0 <0.12.0"
+  http: ">=0.11.0 <0.12.0"
+environment:
+  sdk: ">=1.4.0 <2.0.0"
diff --git a/pkg/http_multi_server/test/http_multi_server_test.dart b/pkg/http_multi_server/test/http_multi_server_test.dart
new file mode 100644
index 0000000..a3d9062
--- /dev/null
+++ b/pkg/http_multi_server/test/http_multi_server_test.dart
@@ -0,0 +1,128 @@
+// 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 http_multi_server.test;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:http/http.dart' as http;
+import 'package:http_multi_server/http_multi_server.dart';
+import 'package:http_multi_server/src/utils.dart';
+import 'package:unittest/unittest.dart';
+
+void main() {
+  group("with multiple HttpServers", () {
+    var multiServer;
+    var subServer1;
+    var subServer2;
+    var subServer3;
+    setUp(() {
+      return Future.wait([
+        HttpServer.bind("127.0.0.1", 0).then((server) => subServer1 = server),
+        HttpServer.bind("127.0.0.1", 0).then((server) => subServer2 = server),
+        HttpServer.bind("127.0.0.1", 0).then((server) => subServer3 = server)
+      ]).then((servers) => multiServer = new HttpMultiServer(servers));
+    });
+
+    tearDown(() => multiServer.close());
+
+    test("listen listens to all servers", () {
+      multiServer.listen((request) {
+        request.response.write("got request");
+        request.response.close();
+      });
+
+      expect(_read(subServer1), completion(equals("got request")));
+      expect(_read(subServer2), completion(equals("got request")));
+      expect(_read(subServer3), completion(equals("got request")));
+    });
+
+    test("serverHeader= sets the value for all servers", () {
+      multiServer.serverHeader = "http_multi_server test";
+
+      multiServer.listen((request) {
+        request.response.write("got request");
+        request.response.close();
+      });
+
+      expect(_get(subServer1).then((response) {
+        expect(response.headers['server'], equals("http_multi_server test"));
+      }), completes);
+
+      expect(_get(subServer2).then((response) {
+        expect(response.headers['server'], equals("http_multi_server test"));
+      }), completes);
+
+      expect(_get(subServer3).then((response) {
+        expect(response.headers['server'], equals("http_multi_server test"));
+      }), completes);
+    });
+
+    test("connectionsInfo sums the values for all servers", () {
+      var pendingRequests = 0;
+      var awaitingResponseCompleter = new Completer();
+      var sendResponseCompleter = new Completer();
+      multiServer.listen((request) {
+        sendResponseCompleter.future.then((_) {
+          request.response.write("got request");
+          request.response.close();
+        });
+
+        pendingRequests++;
+        if (pendingRequests == 2) awaitingResponseCompleter.complete();
+      });
+
+      // Queue up some requests, then wait on [awaitingResponseCompleter] to
+      // make sure they're in-flight before we check [connectionsInfo].
+      expect(_get(subServer1), completes);
+      expect(_get(subServer2), completes);
+
+      return awaitingResponseCompleter.future.then((_) {
+        var info = multiServer.connectionsInfo();
+        expect(info.total, equals(2));
+        expect(info.active, equals(2));
+        expect(info.idle, equals(0));
+        expect(info.closing, equals(0));
+
+        sendResponseCompleter.complete();
+      });
+    });
+  });
+
+  group("HttpMultiServer.loopback", () {
+    var server;
+    setUp(() {
+      return HttpMultiServer.loopback(0).then((server_) => server = server_);
+    });
+
+    tearDown(() => server.close());
+
+    test("listens on all localhost interfaces", () {
+      server.listen((request) {
+        request.response.write("got request");
+        request.response.close();
+      });
+
+      expect(http.read("http://127.0.0.1:${server.port}/"),
+          completion(equals("got request")));
+
+      return supportsIpV6.then((supportsIpV6) {
+        if (!supportsIpV6) return;
+        expect(http.read("http://[::1]:${server.port}/"),
+            completion(equals("got request")));
+      });
+    });
+  });
+}
+
+/// Makes a GET request to the root of [server] and returns the response.
+Future<http.Response> _get(HttpServer server) => http.get(_urlFor(server));
+
+/// Makes a GET request to the root of [server] and returns the response body.
+Future<String> _read(HttpServer server) => http.read(_urlFor(server));
+
+/// Returns the URL for the root of [server].
+String _urlFor(HttpServer server) =>
+    "http://${server.address.host}:${server.port}/";
diff --git a/pkg/http_server/lib/src/http_multi_server.dart b/pkg/http_server/lib/src/http_multi_server.dart
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pkg/http_server/lib/src/http_multi_server.dart
diff --git a/pkg/json_rpc_2/test/server/stream_test.dart b/pkg/json_rpc_2/test/server/stream_test.dart
new file mode 100644
index 0000000..5459e3e
--- /dev/null
+++ b/pkg/json_rpc_2/test/server/stream_test.dart
@@ -0,0 +1,98 @@
+// 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 json_rpc_2.test.server.stream_test;
+
+import 'dart:async';
+
+import 'package:unittest/unittest.dart';
+import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
+
+import 'utils.dart';
+
+void main() {
+  test(".withoutJson supports decoded stream and sink", () {
+    var requestController = new StreamController();
+    var responseController = new StreamController();
+    var server = new json_rpc.Server.withoutJson(
+        requestController.stream, responseController.sink);
+    server.listen();
+
+    server.registerMethod('foo', (params) {
+      return {'params': params.value};
+    });
+
+    requestController.add({
+      'jsonrpc': '2.0',
+      'method': 'foo',
+      'params': {'param': 'value'},
+      'id': 1234
+    });
+
+    expect(responseController.stream.first, completion(equals({
+      'jsonrpc': '2.0',
+      'result': {'params': {'param': 'value'}},
+      'id': 1234
+    })));
+  });
+
+  test(".listen returns when the controller is closed", () {
+    var requestController = new StreamController();
+    var responseController = new StreamController();
+    var server = new json_rpc.Server(
+        requestController.stream, responseController.sink);
+
+    var hasListenCompeted = false;
+    expect(server.listen().then((_) => hasListenCompeted = true), completes);
+
+    return pumpEventQueue().then((_) {
+      expect(hasListenCompeted, isFalse);
+
+      // This should cause listen to complete.
+      return requestController.close();
+    });
+  });
+
+  test(".listen returns a stream error", () {
+    var requestController = new StreamController();
+    var responseController = new StreamController();
+    var server = new json_rpc.Server(
+        requestController.stream, responseController.sink);
+
+    expect(server.listen(), throwsA('oh no'));
+    requestController.addError('oh no');
+  });
+
+  test(".listen can't be called twice", () {
+    var requestController = new StreamController();
+    var responseController = new StreamController();
+    var server = new json_rpc.Server(
+        requestController.stream, responseController.sink);
+    server.listen();
+
+    expect(() => server.listen(), throwsStateError);
+  });
+
+  test(".close cancels the stream subscription and closes the sink", () {
+    var requestController = new StreamController();
+    var responseController = new StreamController();
+    var server = new json_rpc.Server(
+        requestController.stream, responseController.sink);
+
+    expect(server.listen(), completes);
+    expect(server.close(), completes);
+
+    expect(() => requestController.stream.listen((_) {}), throwsStateError);
+    expect(responseController.isClosed, isTrue);
+  });
+
+  test(".close can't be called before .listen", () {
+    var requestController = new StreamController();
+    var responseController = new StreamController();
+    var server = new json_rpc.Server(
+        requestController.stream, responseController.sink);
+
+    expect(() => server.close(), throwsStateError);
+  });
+}
diff --git a/pkg/matcher/CHANGELOG.md b/pkg/matcher/CHANGELOG.md
new file mode 100644
index 0000000..98cd9f4
--- /dev/null
+++ b/pkg/matcher/CHANGELOG.md
@@ -0,0 +1,21 @@
+## 0.10.0+3
+
+* Removed `@deprecated` annotation on matchers due to 
+[Issue 19173](https://code.google.com/p/dart/issues/detail?id=19173)
+
+## 0.10.0+2
+
+* Added types to a number of constants.
+
+## 0.10.0+1
+
+* Matchers related to bad language use have been removed. These represent code
+structure that should rarely or never be validated in tests.
+    * `isAbstractClassInstantiationError`
+    * `throwsAbstractClassInstantiationError`
+    * `isFallThroughError`
+    * `throwsFallThroughError`
+
+* Added types to a number of method arguments.
+
+* The structure of the library and test code has been updated.
diff --git a/pkg/matcher/README.md b/pkg/matcher/README.md
new file mode 100644
index 0000000..cf165c1
--- /dev/null
+++ b/pkg/matcher/README.md
@@ -0,0 +1,7 @@
+Support for specifying test expectations, such as for unit tests.
+
+The matcher library provides a third-generation assertion mechanism, drawing
+inspiration from [Hamcrest](http://code.google.com/p/hamcrest/).
+
+For more information, see
+[Unit Testing with Dart](http://www.dartlang.org/articles/dart-unit-tests/).
diff --git a/pkg/matcher/lib/src/error_matchers.dart b/pkg/matcher/lib/src/error_matchers.dart
new file mode 100644
index 0000000..cb716f5
--- /dev/null
+++ b/pkg/matcher/lib/src/error_matchers.dart
@@ -0,0 +1,173 @@
+// 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';
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher isAbstractClassInstantiationError =
+  const _AbstractClassInstantiationError();
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher throwsAbstractClassInstantiationError =
+  const Throws(isAbstractClassInstantiationError);
+
+class _AbstractClassInstantiationError extends TypeMatcher {
+  const _AbstractClassInstantiationError() :
+  super("AbstractClassInstantiationError");
+  bool matches(item, Map matchState) => item is AbstractClassInstantiationError;
+}
+
+/// A matcher for ArgumentErrors.
+const Matcher isArgumentError = const _ArgumentError();
+
+/// A matcher for functions that throw ArgumentError.
+const Matcher throwsArgumentError = const Throws(isArgumentError);
+
+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();
+
+/// A matcher for functions that throw ConcurrentModificationError.
+const Matcher throwsConcurrentModificationError =
+  const Throws(isConcurrentModificationError);
+
+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();
+
+/// A matcher for functions that throw CyclicInitializationError.
+const Matcher throwsCyclicInitializationError =
+  const Throws(isCyclicInitializationError);
+
+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();
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsException = const Throws(isException);
+
+class _Exception extends TypeMatcher {
+  const _Exception(): super("Exception");
+  bool matches(item, Map matchState) => item is Exception;
+}
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher isFallThroughError = const _FallThroughError();
+
+/// **DEPRECATED**
+///
+/// Will be removed in the next major release.
+// TODO(kevmoo): re-deprecate once 19173 is resolved
+//@deprecated
+const Matcher throwsFallThroughError = const Throws(isFallThroughError);
+
+class _FallThroughError extends TypeMatcher {
+  const _FallThroughError(): super("FallThroughError");
+  bool matches(item, Map matchState) => item is FallThroughError;
+}
+
+/// A matcher for FormatExceptions.
+const Matcher isFormatException = const _FormatException();
+
+/// A matcher for functions that throw FormatException.
+const Matcher throwsFormatException = const Throws(isFormatException);
+
+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();
+
+/// A matcher for functions that throw NoSuchMethodError.
+const Matcher throwsNoSuchMethodError = const Throws(isNoSuchMethodError);
+
+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();
+
+/// A matcher for functions that throw NullThrownError.
+const Matcher throwsNullThrownError = const Throws(isNullThrownError);
+
+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();
+
+/// A matcher for functions that throw RangeError.
+const Matcher throwsRangeError = const Throws(isRangeError);
+
+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();
+
+/// A matcher for functions that throw StateError.
+const Matcher throwsStateError = const Throws(isStateError);
+
+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();
+
+/// A matcher for functions that throw Exception.
+const Matcher throwsUnimplementedError = const Throws(isUnimplementedError);
+
+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();
+
+/// A matcher for functions that throw UnsupportedError.
+const Matcher throwsUnsupportedError = const Throws(isUnsupportedError);
+
+class _UnsupportedError extends TypeMatcher {
+  const _UnsupportedError(): super("UnsupportedError");
+  bool matches(item, Map matchState) => item is UnsupportedError;
+}
diff --git a/pkg/matcher/test/core_matchers_test.dart b/pkg/matcher/test/core_matchers_test.dart
new file mode 100644
index 0000000..3f694f3
--- /dev/null
+++ b/pkg/matcher/test/core_matchers_test.dart
@@ -0,0 +1,211 @@
+// 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_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+  test('isTrue', () {
+    shouldPass(true, isTrue);
+    shouldFail(false, isTrue, "Expected: true Actual: <false>");
+  });
+
+  test('isFalse', () {
+    shouldPass(false, isFalse);
+    shouldFail(10, isFalse, "Expected: false Actual: <10>");
+    shouldFail(true, isFalse, "Expected: false Actual: <true>");
+  });
+
+  test('isNull', () {
+    shouldPass(null, isNull);
+    shouldFail(false, isNull, "Expected: null Actual: <false>");
+  });
+
+  test('isNotNull', () {
+    shouldPass(false, isNotNull);
+    shouldFail(null, isNotNull, "Expected: not null Actual: <null>");
+  });
+
+  test('same', () {
+    var a = new Map();
+    var b = new Map();
+    shouldPass(a, same(a));
+    shouldFail(b, same(a), "Expected: same instance as {} Actual: {}");
+  });
+
+  test('equals', () {
+    var a = new Map();
+    var b = new Map();
+    shouldPass(a, equals(a));
+    shouldPass(a, equals(b));
+  });
+
+  test('anything', () {
+    var a = new Map();
+    shouldPass(0, anything);
+    shouldPass(null, anything);
+    shouldPass(a, anything);
+    shouldFail(a, isNot(anything), "Expected: not anything Actual: {}");
+  });
+
+  test('throws', () {
+    shouldFail(doesNotThrow, throws,
+        matches(
+            r"Expected: throws"
+            r"  Actual: <Closure(: \(\) => dynamic "
+            r"from Function 'doesNotThrow': static\.)?>"
+            r"   Which: did not throw"));
+    shouldPass(doesThrow, throws);
+    shouldFail(true, throws,
+        "Expected: throws"
+        "  Actual: <true>"
+        "   Which: is not a Function or Future");
+  });
+
+  test('throwsA', () {
+    shouldPass(doesThrow, throwsA(equals('X')));
+    shouldFail(doesThrow, throwsA(equals('Y')),
+        matches(
+            r"Expected: throws 'Y'"
+            r"  Actual: <Closure(: \(\) => dynamic "
+            r"from Function 'doesThrow': static\.)?>"
+            r"   Which: threw 'X'"));
+  });
+
+  test('throwsA', () {
+    shouldPass(doesThrow, throwsA(equals('X')));
+    shouldFail(doesThrow, throwsA(equals('Y')),
+        matches("Expected: throws 'Y'.*"
+        "Actual: <Closure.*"
+        "Which: threw 'X'"));
+  });
+
+  test('returnsNormally', () {
+    shouldPass(doesNotThrow, returnsNormally);
+    shouldFail(doesThrow, returnsNormally,
+        matches(
+            r"Expected: return normally"
+            r"  Actual: <Closure(: \(\) => dynamic "
+            r"from Function 'doesThrow': static\.)?>"
+            r"   Which: threw 'X'"));
+  });
+
+
+  test('hasLength', () {
+    var a = new Map();
+    var b = new List();
+    shouldPass(a, hasLength(0));
+    shouldPass(b, hasLength(0));
+    shouldPass('a', hasLength(1));
+    shouldFail(0, hasLength(0), new PrefixMatcher(
+        "Expected: an object with length of <0> "
+        "Actual: <0> "
+        "Which: has no length property"));
+
+    b.add(0);
+    shouldPass(b, hasLength(1));
+    shouldFail(b, hasLength(2),
+        "Expected: an object with length of <2> "
+        "Actual: [0] "
+        "Which: has length of <1>");
+
+    b.add(0);
+    shouldFail(b, hasLength(1),
+        "Expected: an object with length of <1> "
+        "Actual: [0, 0] "
+        "Which: has length of <2>");
+    shouldPass(b, hasLength(2));
+  });
+
+  test('scalar type mismatch', () {
+    shouldFail('error', equals(5.1),
+        "Expected: <5.1> "
+        "Actual: 'error'");
+  });
+
+  test('nested type mismatch', () {
+    shouldFail(['error'], equals([5.1]),
+        "Expected: [5.1] "
+        "Actual: ['error'] "
+        "Which: was 'error' instead of <5.1> at location [0]");
+  });
+
+  test('doubly-nested type mismatch', () {
+    shouldFail([['error']], equals([[5.1]]),
+        "Expected: [[5.1]] "
+        "Actual: [['error']] "
+        "Which: was 'error' instead of <5.1> at location [0][0]");
+  });
+
+  test('doubly nested inequality', () {
+    var actual1 = [['foo', 'bar'], ['foo'], 3, []];
+    var expected1 = [['foo', 'bar'], ['foo'], 4, []];
+    var reason1 = "Expected: [['foo', 'bar'], ['foo'], 4, []] "
+        "Actual: [['foo', 'bar'], ['foo'], 3, []] "
+        "Which: was <3> instead of <4> at location [2]";
+
+    var actual2 = [['foo', 'barry'], ['foo'], 4, []];
+    var expected2 = [['foo', 'bar'], ['foo'], 4, []];
+    var reason2 = "Expected: [['foo', 'bar'], ['foo'], 4, []] "
+        "Actual: [['foo', 'barry'], ['foo'], 4, []] "
+        "Which: was 'barry' instead of 'bar' at location [0][1]";
+
+    var actual3 = [['foo', 'bar'], ['foo'], 4, {'foo':'bar'}];
+    var expected3 = [['foo', 'bar'], ['foo'], 4, {'foo':'barry'}];
+    var reason3 = "Expected: [['foo', 'bar'], ['foo'], 4, {'foo': 'barry'}] "
+        "Actual: [['foo', 'bar'], ['foo'], 4, {'foo': 'bar'}] "
+        "Which: was 'bar' instead of 'barry' at location [3]['foo']";
+
+    shouldFail(actual1, equals(expected1), reason1);
+    shouldFail(actual2, equals(expected2), reason2);
+    shouldFail(actual3, equals(expected3), reason3);
+  });
+
+  test('isInstanceOf', () {
+    shouldFail(0, new isInstanceOf<String>('String'),
+        "Expected: an instance of String Actual: <0>");
+    shouldPass('cow', new isInstanceOf<String>('String'));
+  });
+
+  group('Predicate Matchers', () {
+    test('isInstanceOf', () {
+      shouldFail(0, predicate((x) => x is String, "an instance of String"),
+          "Expected: an instance of String Actual: <0>");
+      shouldPass('cow', predicate((x) => x is String, "an instance of String"));
+    });
+  });
+
+  group('exception/error matchers', () {
+    test('throwsCyclicInitializationError', () {
+      expect(() => _Bicycle.foo, throwsCyclicInitializationError);
+    });
+
+    test('throwsConcurrentModificationError', () {
+      expect(() {
+        var a = { 'foo': 'bar' };
+        for (var k in a.keys) {
+          a.remove(k);
+        }
+      }, throwsConcurrentModificationError);
+    });
+
+    test('throwsNullThrownError', () {
+      expect(() => throw null, throwsNullThrownError);
+    });
+  });
+}
+
+class _Bicycle {
+  static final foo = bar();
+
+  static bar() {
+    return foo + 1;
+  }
+}
diff --git a/pkg/matcher/test/deprecated_matchers_test.dart b/pkg/matcher/test/deprecated_matchers_test.dart
new file mode 100644
index 0000000..b6acc1c
--- /dev/null
+++ b/pkg/matcher/test/deprecated_matchers_test.dart
@@ -0,0 +1,33 @@
+// 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.deprecated_matchers_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+  test('throwsAbstractClassInstantiationError', () {
+    expect(() => new _AbstractClass(), throwsAbstractClassInstantiationError);
+  });
+
+  test('throwsFallThroughError', () {
+    expect(() {
+      var a = 0;
+      switch (a) {
+        case 0:
+          a += 1;
+        case 1:
+          return;
+      }
+    }, throwsFallThroughError);
+  });
+}
+
+abstract class _AbstractClass {
+}
diff --git a/pkg/matcher/test/future_matchers_test.dart b/pkg/matcher/test/future_matchers_test.dart
new file mode 100644
index 0000000..6fac0de
--- /dev/null
+++ b/pkg/matcher/test/future_matchers_test.dart
@@ -0,0 +1,76 @@
+// 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_test;
+
+import 'dart:async';
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+  test('completes - unexpected error', () {
+    var completer = new Completer();
+    completer.completeError('X');
+    shouldFail(completer.future, completes,
+        contains('Expected future to complete successfully, '
+                 'but it failed with X'),
+        isAsync: true);
+  });
+
+  test('completes - successfully', () {
+    var completer = new Completer();
+    completer.complete('1');
+    shouldPass(completer.future, completes, isAsync: true);
+  });
+
+  test('throws - unexpected to see normal completion', () {
+    var completer = new Completer();
+    completer.complete('1');
+    shouldFail(completer.future, throws,
+      contains("Expected future to fail, but succeeded with '1'"),
+      isAsync: true);
+  });
+
+  test('throws - expected to see exception', () {
+    var completer = new Completer();
+    completer.completeError('X');
+    shouldPass(completer.future, throws, isAsync: true);
+  });
+
+  test('throws - expected to see exception thrown later on', () {
+    var completer = new Completer();
+    var chained = completer.future.then((_) { throw 'X'; });
+    shouldPass(chained, throws, isAsync: true);
+    completer.complete('1');
+  });
+
+  test('throwsA - unexpected normal completion', () {
+    var completer = new Completer();
+    completer.complete('1');
+    shouldFail(completer.future, throwsA(equals('X')),
+      contains("Expected future to fail, but succeeded with '1'"),
+      isAsync: true);
+  });
+
+  test('throwsA - correct error', () {
+    var completer = new Completer();
+    completer.completeError('X');
+    shouldPass(completer.future, throwsA(equals('X')), isAsync: true);
+  });
+
+  test('throwsA - wrong error', () {
+    var completer = new Completer();
+    completer.completeError('X');
+    shouldFail(completer.future, throwsA(equals('Y')),
+        "Expected: 'Y' Actual: 'X' "
+        "Which: is different. "
+        "Expected: Y Actual: X ^ Differ at offset 0",
+        isAsync: true);
+  });
+}
diff --git a/pkg/matcher/test/iterable_matchers_test.dart b/pkg/matcher/test/iterable_matchers_test.dart
new file mode 100644
index 0000000..4570d526
--- /dev/null
+++ b/pkg/matcher/test/iterable_matchers_test.dart
@@ -0,0 +1,166 @@
+// 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_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+  test('isEmpty', () {
+    shouldPass([], isEmpty);
+    shouldFail([1], isEmpty, "Expected: empty Actual: [1]");
+  });
+
+  test('contains', () {
+    var d = [1, 2];
+    shouldPass(d, contains(1));
+    shouldFail(d, contains(0), "Expected: contains <0> "
+        "Actual: [1, 2]");
+  });
+
+  test('equals with matcher element', () {
+    var d = ['foo', 'bar'];
+    shouldPass(d, equals(['foo', startsWith('ba')]));
+    shouldFail(d, equals(['foo', endsWith('ba')]),
+        "Expected: ['foo', <a string ending with 'ba'>] "
+        "Actual: ['foo', 'bar'] "
+        "Which: does not match a string ending with 'ba' at location [1]");
+  });
+
+  test('isIn', () {
+    var d = [1, 2];
+    shouldPass(1, isIn(d));
+    shouldFail(0, isIn(d), "Expected: is in [1, 2] Actual: <0>");
+  });
+
+  test('everyElement', () {
+    var d = [1, 2];
+    var e = [1, 1, 1];
+    shouldFail(d, everyElement(1),
+        "Expected: every element(<1>) "
+        "Actual: [1, 2] "
+        "Which: has value <2> which doesn't match <1> at index 1");
+    shouldPass(e, everyElement(1));
+  });
+
+  test('nested everyElement', () {
+    var d = [['foo', 'bar'], ['foo'], []];
+    var e = [['foo', 'bar'], ['foo'], 3, []];
+    shouldPass(d, everyElement(anyOf(isEmpty, contains('foo'))));
+    shouldFail(d, everyElement(everyElement(equals('foo'))),
+        "Expected: every element(every element('foo')) "
+        "Actual: [['foo', 'bar'], ['foo'], []] "
+        "Which: has value ['foo', 'bar'] which has value 'bar' "
+        "which is different. Expected: foo Actual: bar ^ "
+        "Differ at offset 0 at index 1 at index 0");
+    shouldFail(d, everyElement(allOf(hasLength(greaterThan(0)),
+        contains('foo'))),
+         "Expected: every element((an object with length of a value "
+         "greater than <0> and contains 'foo')) "
+         "Actual: [['foo', 'bar'], ['foo'], []] "
+         "Which: has value [] which has length of <0> at index 2");
+    shouldFail(d, everyElement(allOf(contains('foo'),
+        hasLength(greaterThan(0)))),
+        "Expected: every element((contains 'foo' and "
+        "an object with length of a value greater than <0>)) "
+        "Actual: [['foo', 'bar'], ['foo'], []] "
+        "Which: has value [] which doesn't match (contains 'foo' and "
+        "an object with length of a value greater than <0>) at index 2");
+    shouldFail(e, everyElement(allOf(contains('foo'),
+        hasLength(greaterThan(0)))),
+        "Expected: every element((contains 'foo' and an object with "
+        "length of a value greater than <0>)) "
+        "Actual: [['foo', 'bar'], ['foo'], 3, []] "
+        "Which: has value <3> which is not a string, map or iterable "
+        "at index 2");
+  });
+
+  test('anyElement', () {
+    var d = [1, 2];
+    var e = [1, 1, 1];
+    shouldPass(d, anyElement(2));
+    shouldFail(e, anyElement(2),
+        "Expected: some element <2> Actual: [1, 1, 1]");
+  });
+
+  test('orderedEquals', () {
+    shouldPass([null], orderedEquals([null]));
+    var d = [1, 2];
+    shouldPass(d, orderedEquals([1, 2]));
+    shouldFail(d, orderedEquals([2, 1]),
+        "Expected: equals [2, 1] ordered "
+        "Actual: [1, 2] "
+        "Which: was <1> instead of <2> at location [0]");
+  });
+
+  test('unorderedEquals', () {
+    var d = [1, 2];
+    shouldPass(d, unorderedEquals([2, 1]));
+    shouldFail(d, unorderedEquals([1]),
+        "Expected: equals [1] unordered "
+        "Actual: [1, 2] "
+        "Which: has too many elements (2 > 1)");
+    shouldFail(d, unorderedEquals([3, 2, 1]),
+        "Expected: equals [3, 2, 1] unordered "
+        "Actual: [1, 2] "
+        "Which: has too few elements (2 < 3)");
+    shouldFail(d, unorderedEquals([3, 1]),
+        "Expected: equals [3, 1] unordered "
+        "Actual: [1, 2] "
+        "Which: has no match for <3> at index 0");
+  });
+
+  test('unorderedMatchess', () {
+    var d = [1, 2];
+    shouldPass(d, unorderedMatches([2, 1]));
+    shouldPass(d, unorderedMatches([greaterThan(1), greaterThan(0)]));
+    shouldFail(d, unorderedMatches([greaterThan(0)]),
+        "Expected: matches [a value greater than <0>] unordered "
+        "Actual: [1, 2] "
+        "Which: has too many elements (2 > 1)");
+    shouldFail(d, unorderedMatches([3, 2, 1]),
+        "Expected: matches [<3>, <2>, <1>] unordered "
+        "Actual: [1, 2] "
+        "Which: has too few elements (2 < 3)");
+    shouldFail(d, unorderedMatches([3, 1]),
+        "Expected: matches [<3>, <1>] unordered "
+        "Actual: [1, 2] "
+        "Which: has no match for <3> at index 0");
+    shouldFail(d, unorderedMatches([greaterThan(3), greaterThan(0)]),
+        "Expected: matches [a value greater than <3>, a value greater than "
+            "<0>] unordered "
+        "Actual: [1, 2] "
+        "Which: has no match for a value greater than <3> at index 0");
+  });
+
+  test('pairwise compare', () {
+    var c = [1, 2];
+    var d = [1, 2, 3];
+    var e = [1, 4, 9];
+    shouldFail('x', pairwiseCompare(e, (e,a) => a <= e,
+        "less than or equal"),
+        "Expected: pairwise less than or equal [1, 4, 9] "
+        "Actual: 'x' "
+        "Which: is not an Iterable");
+    shouldFail(c, pairwiseCompare(e, (e,a) => a <= e, "less than or equal"),
+        "Expected: pairwise less than or equal [1, 4, 9] "
+        "Actual: [1, 2] "
+        "Which: has length 2 instead of 3");
+    shouldPass(d, pairwiseCompare(e, (e,a) => a <= e, "less than or equal"));
+    shouldFail(d, pairwiseCompare(e, (e,a) => a < e, "less than"),
+        "Expected: pairwise less than [1, 4, 9] "
+        "Actual: [1, 2, 3] "
+        "Which: has <1> which is not less than <1> at index 0");
+    shouldPass(d, pairwiseCompare(e, (e,a) => a * a == e, "square root of"));
+    shouldFail(d, pairwiseCompare(e, (e,a) => a + a == e, "double"),
+        "Expected: pairwise double [1, 4, 9] "
+        "Actual: [1, 2, 3] "
+        "Which: has <1> which is not double <1> at index 0");
+  });
+}
diff --git a/pkg/matcher/test/numeric_matchers_test.dart b/pkg/matcher/test/numeric_matchers_test.dart
new file mode 100644
index 0000000..60347bd
--- /dev/null
+++ b/pkg/matcher/test/numeric_matchers_test.dart
@@ -0,0 +1,152 @@
+// 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_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+  test('greaterThan', () {
+    shouldPass(10, greaterThan(9));
+    shouldFail(9, greaterThan(10),
+      "Expected: a value greater than <10> "
+      "Actual: <9> "
+      "Which: is not a value greater than <10>");
+  });
+
+  test('greaterThanOrEqualTo', () {
+    shouldPass(10, greaterThanOrEqualTo(10));
+    shouldFail(9, greaterThanOrEqualTo(10),
+      "Expected: a value greater than or equal to <10> "
+      "Actual: <9> "
+      "Which: is not a value greater than or equal to <10>");
+  });
+
+  test('lessThan', () {
+    shouldFail(10, lessThan(9),
+        "Expected: a value less than <9> "
+        "Actual: <10> "
+        "Which: is not a value less than <9>");
+    shouldPass(9, lessThan(10));
+  });
+
+  test('lessThanOrEqualTo', () {
+    shouldPass(10, lessThanOrEqualTo(10));
+    shouldFail(11, lessThanOrEqualTo(10),
+      "Expected: a value less than or equal to <10> "
+      "Actual: <11> "
+      "Which: is not a value less than or equal to <10>");
+  });
+
+  test('isZero', () {
+    shouldPass(0, isZero);
+    shouldFail(1, isZero,
+        "Expected: a value equal to <0> "
+        "Actual: <1> "
+        "Which: is not a value equal to <0>");
+  });
+
+  test('isNonZero', () {
+    shouldFail(0, isNonZero,
+        "Expected: a value not equal to <0> "
+        "Actual: <0> "
+        "Which: is not a value not equal to <0>");
+    shouldPass(1, isNonZero);
+  });
+
+  test('isPositive', () {
+    shouldFail(-1, isPositive,
+        "Expected: a positive value "
+        "Actual: <-1> "
+        "Which: is not a positive value");
+    shouldFail(0, isPositive,
+        "Expected: a positive value "
+        "Actual: <0> "
+        "Which: is not a positive value");
+    shouldPass(1, isPositive);
+  });
+
+  test('isNegative', () {
+    shouldPass(-1, isNegative);
+    shouldFail(0, isNegative,
+        "Expected: a negative value "
+        "Actual: <0> "
+        "Which: is not a negative value");
+  });
+
+  test('isNonPositive', () {
+    shouldPass(-1, isNonPositive);
+    shouldPass(0, isNonPositive);
+    shouldFail(1, isNonPositive,
+        "Expected: a non-positive value "
+        "Actual: <1> "
+        "Which: is not a non-positive value");
+  });
+
+  test('isNonNegative', () {
+    shouldPass(1, isNonNegative);
+    shouldPass(0, isNonNegative);
+    shouldFail(-1, isNonNegative,
+      "Expected: a non-negative value "
+      "Actual: <-1> "
+      "Which: is not a non-negative value");
+  });
+
+  test('closeTo', () {
+    shouldPass(0, closeTo(0, 1));
+    shouldPass(-1, closeTo(0, 1));
+    shouldPass(1, closeTo(0, 1));
+    shouldFail(1.001, closeTo(0, 1),
+        "Expected: a numeric value within <1> of <0> "
+        "Actual: <1.001> "
+        "Which: differs by <1.001>");
+    shouldFail(-1.001, closeTo(0, 1),
+        "Expected: a numeric value within <1> of <0> "
+        "Actual: <-1.001> "
+        "Which: differs by <1.001>");
+  });
+
+  test('inInclusiveRange', () {
+    shouldFail(-1, inInclusiveRange(0,2),
+        "Expected: be in range from 0 (inclusive) to 2 (inclusive) "
+        "Actual: <-1>");
+    shouldPass(0, inInclusiveRange(0,2));
+    shouldPass(1, inInclusiveRange(0,2));
+    shouldPass(2, inInclusiveRange(0,2));
+    shouldFail(3, inInclusiveRange(0,2),
+        "Expected: be in range from 0 (inclusive) to 2 (inclusive) "
+        "Actual: <3>");
+  });
+
+  test('inExclusiveRange', () {
+    shouldFail(0, inExclusiveRange(0,2),
+        "Expected: be in range from 0 (exclusive) to 2 (exclusive) "
+        "Actual: <0>");
+    shouldPass(1, inExclusiveRange(0,2));
+    shouldFail(2, inExclusiveRange(0,2),
+        "Expected: be in range from 0 (exclusive) to 2 (exclusive) "
+        "Actual: <2>");
+  });
+
+  test('inOpenClosedRange', () {
+    shouldFail(0, inOpenClosedRange(0,2),
+        "Expected: be in range from 0 (exclusive) to 2 (inclusive) "
+        "Actual: <0>");
+    shouldPass(1, inOpenClosedRange(0,2));
+    shouldPass(2, inOpenClosedRange(0,2));
+  });
+
+  test('inClosedOpenRange', () {
+    shouldPass(0, inClosedOpenRange(0,2));
+    shouldPass(1, inClosedOpenRange(0,2));
+    shouldFail(2, inClosedOpenRange(0,2),
+        "Expected: be in range from 0 (inclusive) to 2 (exclusive) "
+        "Actual: <2>");
+  });
+}
diff --git a/pkg/matcher/test/operator_matchers_test.dart b/pkg/matcher/test/operator_matchers_test.dart
new file mode 100644
index 0000000..fd9627a
--- /dev/null
+++ b/pkg/matcher/test/operator_matchers_test.dart
@@ -0,0 +1,28 @@
+// 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_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+  test('anyOf', () {
+    shouldFail(0, anyOf([equals(1), equals(2)]),
+        "Expected: (<1> or <2>) Actual: <0>");
+    shouldPass(1, anyOf([equals(1), equals(2)]));
+  });
+
+  test('allOf', () {
+    shouldPass(1, allOf([lessThan(10), greaterThan(0)]));
+    shouldFail(-1, allOf([lessThan(10), greaterThan(0)]),
+        "Expected: (a value less than <10> and a value greater than <0>) "
+        "Actual: <-1> "
+        "Which: is not a value greater than <0>");
+  });
+}
diff --git a/pkg/matcher/test/string_matchers_test.dart b/pkg/matcher/test/string_matchers_test.dart
new file mode 100644
index 0000000..1303641
--- /dev/null
+++ b/pkg/matcher/test/string_matchers_test.dart
@@ -0,0 +1,94 @@
+// 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_test;
+
+import 'package:matcher/matcher.dart';
+import 'package:unittest/unittest.dart' show test, group;
+
+import 'test_utils.dart';
+
+void main() {
+  initUtils();
+
+  test('collapseWhitespace', () {
+    var source = '\t\r\n hello\t\r\n world\r\t \n';
+    expect(collapseWhitespace(source), 'hello world');
+  });
+
+  test('isEmpty', () {
+    shouldPass('', isEmpty);
+    shouldFail(null, isEmpty, "Expected: empty Actual: <null>");
+    shouldFail(0, isEmpty, "Expected: empty Actual: <0>");
+    shouldFail('a', isEmpty, "Expected: empty Actual: 'a'");
+  });
+
+  test('equalsIgnoringCase', () {
+    shouldPass('hello', equalsIgnoringCase('HELLO'));
+    shouldFail('hi', equalsIgnoringCase('HELLO'),
+        "Expected: 'HELLO' ignoring case Actual: 'hi'");
+  });
+
+  test('equalsIgnoringWhitespace', () {
+    shouldPass(' hello   world  ', equalsIgnoringWhitespace('hello world'));
+    shouldFail(' helloworld  ', equalsIgnoringWhitespace('hello world'),
+        "Expected: 'hello world' ignoring whitespace "
+        "Actual: ' helloworld ' "
+        "Which: is 'helloworld' with whitespace compressed");
+  });
+
+  test('startsWith', () {
+    shouldPass('hello', startsWith(''));
+    shouldPass('hello', startsWith('hell'));
+    shouldPass('hello', startsWith('hello'));
+    shouldFail('hello', startsWith('hello '),
+        "Expected: a string starting with 'hello ' "
+        "Actual: 'hello'");
+  });
+
+  test('endsWith', () {
+    shouldPass('hello', endsWith(''));
+    shouldPass('hello', endsWith('lo'));
+    shouldPass('hello', endsWith('hello'));
+    shouldFail('hello', endsWith(' hello'),
+        "Expected: a string ending with ' hello' "
+        "Actual: 'hello'");
+  });
+
+  test('contains', () {
+    shouldPass('hello', contains(''));
+    shouldPass('hello', contains('h'));
+    shouldPass('hello', contains('o'));
+    shouldPass('hello', contains('hell'));
+    shouldPass('hello', contains('hello'));
+    shouldFail('hello', contains(' '),
+        "Expected: contains ' ' Actual: 'hello'");
+  });
+
+  test('stringContainsInOrder', () {
+    shouldPass('goodbye cruel world', stringContainsInOrder(['']));
+    shouldPass('goodbye cruel world', stringContainsInOrder(['goodbye']));
+    shouldPass('goodbye cruel world', stringContainsInOrder(['cruel']));
+    shouldPass('goodbye cruel world', stringContainsInOrder(['world']));
+    shouldPass('goodbye cruel world',
+               stringContainsInOrder(['good', 'bye', 'world']));
+    shouldPass('goodbye cruel world',
+               stringContainsInOrder(['goodbye', 'cruel']));
+    shouldPass('goodbye cruel world',
+               stringContainsInOrder(['cruel', 'world']));
+    shouldPass('goodbye cruel world',
+      stringContainsInOrder(['goodbye', 'cruel', 'world']));
+    shouldFail('goodbye cruel world',
+      stringContainsInOrder(['goo', 'cruel', 'bye']),
+      "Expected: a string containing 'goo', 'cruel', 'bye' in order "
+      "Actual: 'goodbye cruel world'");
+  });
+
+  test('matches', () {
+    shouldPass('c0d', matches('[a-z][0-9][a-z]'));
+    shouldPass('c0d', matches(new RegExp('[a-z][0-9][a-z]')));
+    shouldFail('cOd', matches('[a-z][0-9][a-z]'),
+        "Expected: match '[a-z][0-9][a-z]' Actual: 'cOd'");
+  });
+}
diff --git a/pkg/polymer/lib/auto_binding.dart b/pkg/polymer/lib/auto_binding.dart
new file mode 100644
index 0000000..44dc1ca
--- /dev/null
+++ b/pkg/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 `d-auto-binding` 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 `d-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="d-auto-binding">
+ *       <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('template');
+ *         template.model = new MyModel();
+ *       }
+ *
+ *       class MyModel {
+ *         String value = 'something';
+ *         buttonTap() => console.log('tap!');
+ *       }
+ *     </script>
+ *
+ */
+// Dart note: renamed to d-auto-binding 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);
+}
+
+// Dart note: this is implemented a little differently to keep it in classic
+// OOP style. Instead of monkeypatching findController, we override
+// getEventHandler to do the right thing.
+class _AutoBindingSyntax extends PolymerExpressions {
+  final AutoBindingElement _node;
+
+  _AutoBindingSyntax(this._node) : super();
+
+  EventListener getEventHandler(controller, target, String method) => (e) {
+    if (controller == null || controller is! Polymer) controller = _node;
+
+    if (controller is Polymer) {
+      var args = [e, e.detail, e.currentTarget];
+
+      // Dart note: make sure we dispatch to the model, not the
+      // AutoBindingElement instance.
+      var obj = controller == _node ? _node.model : controller;
+      controller.dispatchMethod(obj, method, args);
+    } else {
+      throw new StateError('controller $controller is not a '
+          'Dart polymer-element.');
+    }
+  };
+}
diff --git a/pkg/polymer/lib/src/events.dart b/pkg/polymer/lib/src/events.dart
new file mode 100644
index 0000000..503445f
--- /dev/null
+++ b/pkg/polymer/lib/src/events.dart
@@ -0,0 +1,149 @@
+// 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 BindingDelegate _delegate;
+
+  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);
+}
+
+/// 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;
+      }
+      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 args = [e, 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 handler = getEventHandler(null, node, path);
+      node.addEventListener(eventType, handler);
+
+      if (oneTime) return null;
+      return new _EventBindable(node, eventType, handler, path);
+    };
+  }
+}
+
+
+class _EventBindable extends Bindable {
+  final Node _node;
+  final String _eventType;
+  final Function _handler;
+  final String _path;
+
+  _EventBindable(this._node, this._eventType, this._handler, this._path);
+
+  // 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() => _node.removeEventListener(_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/pkg/polymer/lib/src/js/polymer/layout.html b/pkg/polymer/lib/src/js/polymer/layout.html
new file mode 100644
index 0000000..4e1284b
--- /dev/null
+++ b/pkg/polymer/lib/src/js/polymer/layout.html
@@ -0,0 +1,278 @@
+<!--
+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;
+  -webkit-flex: 1;
+  flex: 1;
+}
+
+html /deep/ [flex][auto] {
+  -ms-flex: 1 1 auto;
+  -webkit-flex: 1 1 auto;
+  flex: 1 1 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] {
+  -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] {
+  -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: around;
+  -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;
+}
+
+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/pkg/polymer/test/auto_binding_test.dart b/pkg/polymer/test/auto_binding_test.dart
new file mode 100644
index 0000000..b563814
--- /dev/null
+++ b/pkg/polymer/test/auto_binding_test.dart
@@ -0,0 +1,47 @@
+// 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 'dart:html';
+import 'package:polymer/auto_binding.dart';
+import 'package:polymer/polymer.dart';
+import 'package:unittest/unittest.dart';
+import 'package:unittest/html_config.dart';
+
+class TestModel {
+  var greeting = 'Hi';
+  eventAction(e) {
+    e.detail.add('handled');
+  }
+}
+
+main() => initPolymer().run(() {
+  useHtmlConfiguration();
+
+  setUp(() => Polymer.onReady);
+
+  test('elements upgraded', () {
+    AutoBindingElement template = document.getElementById('one');
+    template.model = new TestModel();
+
+    var completer = new Completer();
+    var events = 0;
+    window.addEventListener('template-bound', (e) {
+      events++;
+      if (e.target.id == 'one') {
+        expect(e.target, template);
+
+        var t = template;
+        var h = t.$['h'];
+        expect(h.text, t.model.greeting, reason: 'binding applied');
+        var ce = t.fire('tap', onNode: h, detail: []);
+        expect(ce.detail, ['handled'], reason: 'element event handler fired');
+      }
+
+      if (events == 2) completer.complete();
+    });
+
+    return completer.future;
+  });
+});
diff --git a/pkg/polymer/test/auto_binding_test.html b/pkg/polymer/test/auto_binding_test.html
new file mode 100644
index 0000000..822d290
--- /dev/null
+++ b/pkg/polymer/test/auto_binding_test.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<!--
+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.
+-->
+<html>
+  <head>
+  <!--polymer-test: this comment is needed for test_suite.dart-->
+    <title>auto-binding test</title>
+    <link rel="import" href="packages/polymer/polymer.html">
+    <script src="/root_dart/tools/testing/dart/test_controller.js"></script>
+  </head>
+  <body unresolved>
+
+    <div>top</div>
+
+    <template id="one" is="d-auto-binding">
+      <h2 id="h" on-tap="{{eventAction}}">{{greeting}}</h2>
+    </template>
+
+    <template id="two" is="d-auto-binding">
+      <div>Say something: <input value="{{value}}"></div>
+      <div>You said: {{value}}</div>
+    </template>
+
+    <div>bottom</div>
+
+    <script type="application/dart" src="auto_binding_test.dart"></script>
+
+  </body>
+</html>
diff --git a/runtime/bin/net/os_windows.c b/runtime/bin/net/os_windows.c
new file mode 100644
index 0000000..07ccdc5
--- /dev/null
+++ b/runtime/bin/net/os_windows.c
@@ -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.
+
+#if !defined(_MSC_VER)
+#error "This file should only be compiled under MSVC"
+#endif
+
+#if defined(_X86_)
+#include "../../../third_party/nss/nss/lib/freebl/mpi/mpi_x86_asm.c"
+#endif
diff --git a/runtime/bin/vmservice/client/deployed/web/packages/observatory/src/elements/class_tree.html b/runtime/bin/vmservice/client/deployed/web/packages/observatory/src/elements/class_tree.html
new file mode 100644
index 0000000..8ede1e6
--- /dev/null
+++ b/runtime/bin/vmservice/client/deployed/web/packages/observatory/src/elements/class_tree.html
@@ -0,0 +1,119 @@
+<link rel="import" href="nav_bar.html">
+<link rel="import" href="observatory_element.html">
+<link rel="import" href="class_ref.html">
+
+<polymer-element name="class-tree" extends="observatory-element">
+  <template>
+    <link rel="stylesheet" href="css/shared.css">
+    <style>
+      .table {
+        border-collapse: collapse!important;
+        width: 100%;
+        margin-bottom: 20px
+      }
+      .table thead > tr > th,
+      .table tbody > tr > th,
+      .table tfoot > tr > th,
+      .table thead > tr > td,
+      .table tbody > tr > td,
+      .table tfoot > tr > td {
+        padding: 8px;
+        vertical-align: top;
+      }
+      .table thead > tr > th {
+        vertical-align: bottom;
+        text-align: left;
+        border-bottom:2px solid #ddd;
+      }
+
+      tr:hover > td {
+        background-color: #FFF3E3;
+      }
+      .rowColor0 {
+        background-color: #FFE9CC;
+      }
+      .rowColor1 {
+        background-color: #FFDEB2;
+      }
+      .rowColor2 {
+        background-color: #FFD399;
+      }
+      .rowColor3 {
+        background-color: #FFC87F;
+      }
+      .rowColor4 {
+        background-color: #FFBD66;
+      }
+      .rowColor5 {
+        background-color: #FFB24C;
+      }
+      .rowColor6 {
+        background-color: #FFA733;
+      }
+      .rowColor7 {
+        background-color: #FF9C19;
+      }
+      .rowColor8 {
+        background-color: #FF9100;
+      }
+
+      .tooltip {
+        display: block;
+        position: absolute;
+        visibility: hidden;
+        opacity: 0;
+        transition: visibility 0s linear 0.5s;
+        transition: opacity .4s ease-in-out;
+      }
+
+      tr:hover .tooltip {
+        display: block;
+        position: absolute;
+        top: 100%;
+        right: 100%;
+        visibility: visible;
+        z-index: 999;
+        width: 400px;
+        color: #ffffff;
+        background-color: #0489c3;
+        border-top-right-radius: 8px;
+        border-top-left-radius: 8px;
+        border-bottom-right-radius: 8px;
+        border-bottom-left-radius: 8px;
+        transition: visibility 0s linear 0.5s;
+        transition: opacity .4s ease-in-out;
+        opacity: 1;
+      }
+
+      .white {
+        color: #ffffff;
+      }
+    </style>
+    <nav-bar>
+      <top-nav-menu last="{{ true }}"></top-nav-menu>
+      <nav-control></nav-control>
+    </nav-bar>
+    <div class="content-centered">
+      <h1>Class Hierarchy</h1>
+      <table id="tableTree" class="table">
+        <thead>
+          <tr>
+            <th>Class</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr template repeat="{{row in tree.rows }}">
+            <td on-click="{{toggleExpanded}}"
+                class="{{ coloring(row) }}"
+                style="{{ padding(row) }}">
+              <span id="expand" style="{{ row.expanderStyle }}">{{ row.expander }}</span>
+              <class-ref ref="{{ row.cls }}"></class-ref>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </template>
+</polymer-element>
+
+<script type="application/dart" src="class_tree.dart"></script>
diff --git a/runtime/bin/vmservice/client/lib/src/elements/class_tree.dart b/runtime/bin/vmservice/client/lib/src/elements/class_tree.dart
new file mode 100644
index 0000000..aeb29fa
--- /dev/null
+++ b/runtime/bin/vmservice/client/lib/src/elements/class_tree.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.
+
+library class_tree_element;
+
+import 'observatory_element.dart';
+import 'dart:html';
+import 'package:logging/logging.dart';
+import 'package:observatory/app.dart';
+import 'package:observatory/service.dart';
+import 'package:polymer/polymer.dart';
+
+class ClassTreeRow extends TableTreeRow {
+  @reflectable final Isolate isolate;
+  @reflectable final Class cls;
+  ClassTreeRow(this.isolate, this.cls, ClassTreeRow parent) : super(parent) {
+    assert(isolate != null);
+    assert(cls != null);
+  }
+
+  void onShow() {
+    if (children.length > 0) {
+      // Child rows already created.
+      return;
+    }
+    for (var subClass in cls.children) {
+      if (subClass.isPatch) {
+        continue;
+      }
+      var row = new ClassTreeRow(isolate, subClass, this);
+      children.add(row);
+    }
+  }
+
+  void onHide() {
+  }
+
+  bool hasChildren() {
+    return cls.children.length > 0;
+  }
+}
+
+
+@CustomTag('class-tree')
+class ClassTreeElement extends ObservatoryElement {
+  @observable Isolate isolate;
+
+  TableTree tree;
+
+  ClassTreeElement.created() : super.created();
+
+  void enteredView() {
+    tree = new TableTree();
+    if (isolate != null) {
+      _update(isolate.objectClass);
+    }
+  }
+
+  isolateChanged(oldValue) {
+    isolate.getClassHierarchy().then((objectClass) {
+      _update(objectClass);
+    });
+  }
+
+  void _update(Class root) {
+    try {
+      var rootRow = new ClassTreeRow(isolate, root, null);
+      rootRow.children.add(new ClassTreeRow(isolate, root, rootRow));
+      tree.initialize(rootRow);
+    } catch (e, stackTrace) {
+      Logger.root.warning('_update', e, stackTrace);
+    }
+    // Check if we only have one node at the root and expand it.
+    if (tree.rows.length == 1) {
+      tree.toggle(0);
+    }
+    notifyPropertyChange(#tree, null, tree);
+  }
+
+  @observable String padding(TableTreeRow row) {
+    return 'padding-left: ${row.depth * 16}px;';
+  }
+
+  @observable String coloring(TableTreeRow row) {
+    const colors = const ['rowColor0', 'rowColor1', 'rowColor2', 'rowColor3',
+                          'rowColor4', 'rowColor5', 'rowColor6', 'rowColor7',
+                          'rowColor8'];
+    var index = (row.depth - 1) % colors.length;
+    return colors[index];
+  }
+
+  @observable void toggleExpanded(Event e, var detail, Element target) {
+    // We only want to expand a tree row if the target of the click is
+    // the table cell (passed in as target) or the span containing the
+    // expander symbol (#expand).
+    var eventTarget = e.target;
+    if ((eventTarget.id != 'expand') && (e.target != target)) {
+      // Target of click was not the expander span or the table cell.
+      return;
+    }
+    var row = target.parent;
+    if (row is TableRowElement) {
+      try {
+        // Subtract 1 to get 0 based indexing.
+        tree.toggle(row.rowIndex - 1);
+      }  catch (e, stackTrace) {
+        Logger.root.warning('toggleExpanded', e, stackTrace);
+      }
+    }
+  }
+
+}
diff --git a/runtime/bin/vmservice/client/lib/src/elements/class_tree.html b/runtime/bin/vmservice/client/lib/src/elements/class_tree.html
new file mode 100644
index 0000000..8ede1e6
--- /dev/null
+++ b/runtime/bin/vmservice/client/lib/src/elements/class_tree.html
@@ -0,0 +1,119 @@
+<link rel="import" href="nav_bar.html">
+<link rel="import" href="observatory_element.html">
+<link rel="import" href="class_ref.html">
+
+<polymer-element name="class-tree" extends="observatory-element">
+  <template>
+    <link rel="stylesheet" href="css/shared.css">
+    <style>
+      .table {
+        border-collapse: collapse!important;
+        width: 100%;
+        margin-bottom: 20px
+      }
+      .table thead > tr > th,
+      .table tbody > tr > th,
+      .table tfoot > tr > th,
+      .table thead > tr > td,
+      .table tbody > tr > td,
+      .table tfoot > tr > td {
+        padding: 8px;
+        vertical-align: top;
+      }
+      .table thead > tr > th {
+        vertical-align: bottom;
+        text-align: left;
+        border-bottom:2px solid #ddd;
+      }
+
+      tr:hover > td {
+        background-color: #FFF3E3;
+      }
+      .rowColor0 {
+        background-color: #FFE9CC;
+      }
+      .rowColor1 {
+        background-color: #FFDEB2;
+      }
+      .rowColor2 {
+        background-color: #FFD399;
+      }
+      .rowColor3 {
+        background-color: #FFC87F;
+      }
+      .rowColor4 {
+        background-color: #FFBD66;
+      }
+      .rowColor5 {
+        background-color: #FFB24C;
+      }
+      .rowColor6 {
+        background-color: #FFA733;
+      }
+      .rowColor7 {
+        background-color: #FF9C19;
+      }
+      .rowColor8 {
+        background-color: #FF9100;
+      }
+
+      .tooltip {
+        display: block;
+        position: absolute;
+        visibility: hidden;
+        opacity: 0;
+        transition: visibility 0s linear 0.5s;
+        transition: opacity .4s ease-in-out;
+      }
+
+      tr:hover .tooltip {
+        display: block;
+        position: absolute;
+        top: 100%;
+        right: 100%;
+        visibility: visible;
+        z-index: 999;
+        width: 400px;
+        color: #ffffff;
+        background-color: #0489c3;
+        border-top-right-radius: 8px;
+        border-top-left-radius: 8px;
+        border-bottom-right-radius: 8px;
+        border-bottom-left-radius: 8px;
+        transition: visibility 0s linear 0.5s;
+        transition: opacity .4s ease-in-out;
+        opacity: 1;
+      }
+
+      .white {
+        color: #ffffff;
+      }
+    </style>
+    <nav-bar>
+      <top-nav-menu last="{{ true }}"></top-nav-menu>
+      <nav-control></nav-control>
+    </nav-bar>
+    <div class="content-centered">
+      <h1>Class Hierarchy</h1>
+      <table id="tableTree" class="table">
+        <thead>
+          <tr>
+            <th>Class</th>
+          </tr>
+        </thead>
+        <tbody>
+          <tr template repeat="{{row in tree.rows }}">
+            <td on-click="{{toggleExpanded}}"
+                class="{{ coloring(row) }}"
+                style="{{ padding(row) }}">
+              <span id="expand" style="{{ row.expanderStyle }}">{{ row.expander }}</span>
+              <class-ref ref="{{ row.cls }}"></class-ref>
+            </td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+  </template>
+</polymer-element>
+
+<script type="application/dart" src="class_tree.dart"></script>
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index 417a905..72706ac 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -633,12 +633,10 @@
     caller_code.SetStaticCallTargetCodeAt(caller_frame->pc(), target_code);
   }
   if (FLAG_trace_patching) {
-    OS::PrintErr("PatchStaticCall: patching caller pc %#" Px ""
-        " to '%s' new entry point %#" Px " (%s)\n",
+    OS::PrintErr("PatchStaticCall: patching from %#" Px " to '%s' %#" Px "\n",
         caller_frame->pc(),
         target_function.ToFullyQualifiedCString(),
-        target_code.EntryPoint(),
-        target_code.is_optimized() ? "optimized" : "unoptimized");
+        target_code.EntryPoint());
   }
   arguments.SetReturn(target_code);
 }
@@ -1295,11 +1293,9 @@
     caller_code.SetStaticCallTargetCodeAt(frame->pc(), current_target_code);
   }
   if (FLAG_trace_patching) {
-    OS::PrintErr("FixCallersTarget: caller %#" Px " "
-        "target '%s' %#" Px " -> %#" Px "\n",
+    OS::PrintErr("FixCallersTarget: patching from %#" Px " to '%s' %#" Px "\n",
         frame->pc(),
         target_function.ToFullyQualifiedCString(),
-        target_code.EntryPoint(),
         current_target_code.EntryPoint());
   }
   arguments.SetReturn(current_target_code);
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index b8805f8..da3feb1 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -59,7 +59,6 @@
     "Enable compiler verification assertions");
 
 DECLARE_FLAG(bool, trace_failed_optimization_attempts);
-DECLARE_FLAG(bool, trace_patching);
 DECLARE_FLAG(bool, warn_on_javascript_compatibility);
 DECLARE_FLAG(bool, warning_as_error);
 
@@ -557,12 +556,8 @@
         if (optimized) {
           if (osr_id == Isolate::kNoDeoptId) {
             CodePatcher::PatchEntry(Code::Handle(function.CurrentCode()));
-            if (FLAG_trace_compiler || FLAG_trace_patching) {
-              if (FLAG_trace_compiler) {
-                OS::Print("  ");
-              }
-              OS::Print("Patch unoptimized '%s' entry point %#" Px "\n",
-                  function.ToFullyQualifiedCString(),
+            if (FLAG_trace_compiler) {
+              OS::Print("--> patching entry %#" Px "\n",
                   Code::Handle(function.unoptimized_code()).EntryPoint());
             }
           }
@@ -580,10 +575,11 @@
           ASSERT(CodePatcher::CodeIsPatchable(code));
         }
         if (parsed_function->HasDeferredPrefixes()) {
-          ZoneGrowableArray<const LibraryPrefix*>* prefixes =
-              parsed_function->deferred_prefixes();
-          for (intptr_t i = 0; i < prefixes->length(); i++) {
-            (*prefixes)[i]->RegisterDependentCode(code);
+          GrowableObjectArray* prefixes = parsed_function->DeferredPrefixes();
+          LibraryPrefix& prefix = LibraryPrefix::Handle();
+          for (intptr_t i = 0; i < prefixes->Length(); i++) {
+            prefix ^= prefixes->At(i);
+            prefix.RegisterDependentCode(code);
           }
         }
       }
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc
index c0c74a6..afaffa1 100644
--- a/runtime/vm/flow_graph.cc
+++ b/runtime/vm/flow_graph.cc
@@ -42,8 +42,7 @@
     use_far_branches_(false),
     loop_headers_(NULL),
     loop_invariant_loads_(NULL),
-    guarded_fields_(builder.guarded_fields()),
-    deferred_prefixes_(builder.deferred_prefixes()) {
+    guarded_fields_(builder.guarded_fields()) {
   DiscoverBlocks();
 }
 
@@ -64,21 +63,6 @@
 }
 
 
-void FlowGraph::AddToDeferredPrefixes(
-    ZoneGrowableArray<const LibraryPrefix*>* from) {
-  ZoneGrowableArray<const LibraryPrefix*>* to = deferred_prefixes();
-  for (intptr_t i = 0; i < from->length(); i++) {
-    const  LibraryPrefix* prefix = (*from)[i];
-    for (intptr_t j = 0; j < to->length(); j++) {
-      if ((*to)[j]->raw() == prefix->raw()) {
-        return;
-      }
-    }
-    to->Add(prefix);
-  }
-}
-
-
 bool FlowGraph::ShouldReorderBlocks(const Function& function,
                                     bool is_optimized) {
   return is_optimized && FLAG_reorder_basic_blocks && !function.is_intrinsic();
diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h
index 1868d0a..9d2d42e 100644
--- a/runtime/vm/flow_graph.h
+++ b/runtime/vm/flow_graph.h
@@ -216,16 +216,11 @@
 
   static void AddToGuardedFields(ZoneGrowableArray<const Field*>* array,
                                  const Field* field);
-  void AddToDeferredPrefixes(ZoneGrowableArray<const LibraryPrefix*>* from);
 
   ZoneGrowableArray<const Field*>* guarded_fields() const {
     return guarded_fields_;
   }
 
-  ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes() const {
-    return deferred_prefixes_;
-  }
-
  private:
   friend class IfConverter;
   friend class BranchSimplifier;
@@ -305,7 +300,6 @@
   ZoneGrowableArray<BlockEntryInstr*>* loop_headers_;
   ZoneGrowableArray<BitVector*>* loop_invariant_loads_;
   ZoneGrowableArray<const Field*>* guarded_fields_;
-  ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
 };
 
 
diff --git a/runtime/vm/flow_graph_builder.h b/runtime/vm/flow_graph_builder.h
index 8467cb5..7632971 100644
--- a/runtime/vm/flow_graph_builder.h
+++ b/runtime/vm/flow_graph_builder.h
@@ -199,10 +199,6 @@
     return guarded_fields_;
   }
 
-  ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes() const {
-    return parsed_function_->deferred_prefixes();
-  }
-
   intptr_t temp_count() const { return temp_count_; }
   intptr_t AllocateTemp() { return temp_count_++; }
   void DeallocateTemps(intptr_t count) {
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index 11fedc1..b07c635 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -802,9 +802,6 @@
         FlowGraph::AddToGuardedFields(caller_graph_->guarded_fields(),
                                       (*callee_graph->guarded_fields())[i]);
       }
-      // When inlined, we add the deferred prefixes of the callee to the
-      // caller's list of deferred prefixes.
-      caller_graph()->AddToDeferredPrefixes(callee_graph->deferred_prefixes());
 
       // We allocate a ZoneHandle for the unoptimized code so that it cannot be
       // disconnected from its function during the rest of compilation.
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index cc02d9a..2f9559b 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2523,13 +2523,6 @@
         if (!CodePatcher::IsEntryPatched(code)) {
           CodePatcher::PatchEntry(code);
         }
-      } else if (!function.HasCode() && (code.GetEntryPatchPc() != 0)) {
-        // The code has already been disconnected, make it invalid. Do not
-        // bother with OSR compiled code that has no valid entry-patch.
-        ReportSwitchingCode(code);
-        if (!CodePatcher::IsEntryPatched(code)) {
-          CodePatcher::PatchEntry(code);
-        }
       }
     }
   }
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index c41e5c1..8667c20 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -135,12 +135,16 @@
 void ParsedFunction::AddDeferredPrefix(const LibraryPrefix& prefix) {
   ASSERT(prefix.is_deferred_load());
   ASSERT(!prefix.is_loaded());
-  for (intptr_t i = 0; i < deferred_prefixes_->length(); i++) {
-    if ((*deferred_prefixes_)[i]->raw() == prefix.raw()) {
+  if (deferred_prefixes_ == NULL) {
+    deferred_prefixes_ =
+        &GrowableObjectArray::ZoneHandle(GrowableObjectArray::New());
+  }
+  for (intptr_t i = 0; i < deferred_prefixes_->Length(); i++) {
+    if (deferred_prefixes_->At(i) == prefix.raw()) {
       return;
     }
   }
-  deferred_prefixes_->Add(&LibraryPrefix::ZoneHandle(I, prefix.raw()));
+  deferred_prefixes_->Add(prefix);
 }
 
 
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index 15de41f..f187032 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -47,7 +47,7 @@
         saved_current_context_var_(NULL),
         saved_entry_context_var_(NULL),
         expression_temp_var_(NULL),
-        deferred_prefixes_(new ZoneGrowableArray<const LibraryPrefix*>()),
+        deferred_prefixes_(NULL),
         first_parameter_index_(0),
         first_stack_local_index_(0),
         num_copied_params_(0),
@@ -110,10 +110,8 @@
   static LocalVariable* CreateExpressionTempVar(intptr_t token_pos);
   LocalVariable* EnsureExpressionTemp();
 
-  bool HasDeferredPrefixes() const { return deferred_prefixes_->length() != 0; }
-  ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes() const {
-    return deferred_prefixes_;
-  }
+  bool HasDeferredPrefixes() const { return deferred_prefixes_ != NULL; }
+  GrowableObjectArray* DeferredPrefixes() const { return deferred_prefixes_; }
   void AddDeferredPrefix(const LibraryPrefix& prefix);
 
   int first_parameter_index() const { return first_parameter_index_; }
@@ -134,7 +132,7 @@
   LocalVariable* saved_current_context_var_;
   LocalVariable* saved_entry_context_var_;
   LocalVariable* expression_temp_var_;
-  ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
+  GrowableObjectArray* deferred_prefixes_;
 
   int first_parameter_index_;
   int first_stack_local_index_;
diff --git a/sdk/lib/internal/lru.dart b/sdk/lib/internal/lru.dart
new file mode 100644
index 0000000..8f8a64a
--- /dev/null
+++ b/sdk/lib/internal/lru.dart
@@ -0,0 +1,125 @@
+// 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.
+
+part of dart._internal;
+
+class LRUAssociation<K,V> {
+  K key;
+  V value;
+  LRUAssociation previous;
+  LRUAssociation next;
+
+  void insertBetween(before, after) {
+    after.previous = this;
+    before.next = this;
+    this.next = after;
+    this.previous = before;
+  }
+
+  void remove() {
+    var after = next;
+    var before = previous;
+    after.previous = before;
+    before.next = after;
+  }
+}
+
+/**
+ * A map with a fixed capacity that evicts associations when capacity is reached
+ * on a least-recently-used basis. Implemented as an open addressing hash table
+ * with doubly-linked entries forming the LRU queue.
+ */
+class LRUMap<K,V> {
+  final LRUAssociation<K,V> _head;
+  final List _table;
+  final int _mask;
+  final int _capacity;  // Max number of associations before we start evicting.
+  int _size = 0;  // Current number of associations.
+
+  /**
+   * Create an LRUMap whose capacity is 75% of 2^shift.
+   */
+  LRUMap.withShift(int shift)
+      : this._mask = (1 << shift) - 1
+      , this._capacity = (1 << shift) * 3 ~/ 4
+      , this._table = new List(1 << shift)
+      , this._head = new LRUAssociation() {
+    // The scheme used here for handling collisions relies on there always
+    // being at least one empty slot.
+    if (shift < 1) throw new Exception("LRUMap requires a shift >= 1");
+    assert(_table.length > _capacity);
+    _head.insertBetween(_head, _head);
+  }
+
+  int _scanFor(K key) {
+    var start = key.hashCode & _mask;
+    var index = start;
+    do {
+      var assoc = _table[index];
+      if (null == assoc || assoc.key == key) {
+        return index;
+      }
+      index = (index + 1) & _mask;
+    } while (index != start);
+    // Should never happen because we start evicting associations before the
+    // table is full.
+    throw new Exception("Internal error: LRUMap table full");
+  }
+
+  void _fixCollisionsAfter(start) {
+    var assoc;
+    var index = (start + 1) & _mask;
+    while (null != (assoc = _table[index])) {
+      var newIndex = _scanFor(assoc.key);
+      if (newIndex != index) {
+        assert(_table[newIndex] == null);
+        _table[newIndex] = assoc;
+        _table[index] = null;
+      }
+      index = (index + 1) & _mask;
+    }
+  }
+
+  operator []=(K key, V value) {
+    int index = _scanFor(key);
+    var assoc = _table[index];
+    if (null != assoc) {
+      // Existing key, replace value.
+      assert(assoc.key == key);
+      assoc.value = value;
+      assoc.remove();
+      assoc.insertBetween(_head, _head.next);
+    } else {
+      // New key.
+      var newAssoc;
+      if (_size == _capacity) {
+        // Knock out the oldest association.
+        var lru = _head.previous;
+        lru.remove();
+        index = _scanFor(lru.key);
+        _table[index] = null;
+        _fixCollisionsAfter(index);
+        index = _scanFor(key);
+        newAssoc = lru;  // Recycle the association.
+      } else {
+        newAssoc = new LRUAssociation();
+        _size++;
+      }
+      newAssoc.key = key;
+      newAssoc.value = value;
+      newAssoc.insertBetween(_head, _head.next);
+      _table[index] = newAssoc;
+    }
+  }
+
+  V operator [](K key) {
+    var index = _scanFor(key);
+    var assoc = _table[index];
+    if (null == assoc) return null;
+    // Move to front of LRU queue.
+    assoc.remove();
+    assoc.insertBetween(_head, _head.next);
+    return assoc.value;
+  }
+}
diff --git a/tests/lib/mirrors/lru_test.dart b/tests/lib/mirrors/lru_test.dart
new file mode 100644
index 0000000..141fc98
--- /dev/null
+++ b/tests/lib/mirrors/lru_test.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.
+
+import "dart:mirrors";
+import "package:expect/expect.dart";
+
+newLRUMapWithShift(int shift) {
+  var lib = currentMirrorSystem().libraries[Uri.parse("dart:_internal")];
+  var cls = lib.declarations[#LRUMap];
+  return cls.newInstance(#withShift, [shift]).reflectee;
+}
+
+main() {
+  Expect.throws(() => newLRUMapWithShift(0), (e) => e is Exception);
+
+  for (int shift = 1; shift < 5; shift++) {
+    var map = newLRUMapWithShift(shift);
+    var capacity = (1 << shift) * 3 ~/ 4;
+    for (int value = 0; value < 100; value++) {
+      var key = "$value";
+      map[key] = value;
+      Expect.equals(value, map[key]);
+    }
+    for (int value = 0; value < 100 - capacity - 1; value++) {
+      var key = "$value";
+      Expect.equals(null, map[key]);
+    }
+    for (int value = 100 - capacity; value < 100; value++) {
+      var key = "$value";
+      Expect.equals(value, map[key]);
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/VERSION b/tools/VERSION
index dbc0a4a..cd798b5c 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 5
 PATCH 0
 PRERELEASE 4
-PRERELEASE_PATCH 3
+PRERELEASE_PATCH 4