Version 1.4.0-dev.3.0

svn merge -r 34916:35259 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@35275 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/AUTHORS b/AUTHORS
index dc0e928..c2622b3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -28,4 +28,6 @@
 Alexandre Ardhuin <alexandre.ardhuin@gmail.com>
 Victor Berchet <victor.berchet@gmail.com>
 Roel Spilker <r.spilker@gmail.com>
-Martin Charles <martincharles07@gmail.com>
\ No newline at end of file
+Martin Charles <martincharles07@gmail.com>
+Anders Holmgren <andersmholmgren@gmail.com>
+K. Alex Gann <k.alexgann@gmail.com>
\ No newline at end of file
diff --git a/pkg/analysis_server/bin/server.dart b/pkg/analysis_server/bin/server.dart
index ef37d27..84b7702 100644
--- a/pkg/analysis_server/bin/server.dart
+++ b/pkg/analysis_server/bin/server.dart
@@ -2,12 +2,12 @@
 // for 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 'package:analysis_server/http_server.dart';
+import 'package:analysis_server/driver.dart';
 
 /**
  * Create and run an HTTP-based analysis server.
  */
 void main(List<String> args) {
-  HttpAnalysisServer server = new HttpAnalysisServer();
-  server.start(args);
+  Driver driver = new Driver();
+  driver.start(args);
 }
diff --git a/pkg/analysis_server/lib/driver.dart b/pkg/analysis_server/lib/driver.dart
new file mode 100644
index 0000000..911cbc6
--- /dev/null
+++ b/pkg/analysis_server/lib/driver.dart
@@ -0,0 +1,97 @@
+// 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 driver;
+
+import 'dart:io';
+
+import 'package:analysis_server/http_server.dart';
+import 'package:analysis_server/src/socket_server.dart';
+import 'package:analysis_server/stdio_server.dart';
+import 'package:args/args.dart';
+
+/**
+ * The [Driver] class represents a single running instance of the analysis
+ * server application.  It is responsible for parsing command line options
+ * and starting the HTTP and/or stdio servers.
+ */
+class Driver {
+  /**
+   * The name of the application that is used to start a server.
+   */
+  static const BINARY_NAME = 'server';
+
+  /**
+   * The name of the option used to print usage information.
+   */
+  static const String HELP_OPTION = "help";
+
+  /**
+   * The name of the option used to specify the port to which the server will
+   * connect.
+   */
+  static const String PORT_OPTION = "port";
+
+  SocketServer socketServer = new SocketServer();
+
+  HttpAnalysisServer httpServer;
+
+  StdioAnalysisServer stdioServer;
+
+  Driver() {
+    httpServer = new HttpAnalysisServer(socketServer);
+    stdioServer = new StdioAnalysisServer(socketServer);
+  }
+
+  /**
+   * Use the given command-line arguments to start this server.
+   */
+  void start(List<String> args) {
+    ArgParser parser = new ArgParser();
+    parser.addFlag(HELP_OPTION, help:
+        "print this help message without starting a server", defaultsTo: false,
+        negatable: false);
+    parser.addOption(PORT_OPTION, help:
+        "[port] the port on which the server will listen");
+
+    ArgResults results = parser.parse(args);
+    if (results[HELP_OPTION]) {
+      _printUsage(parser);
+      return;
+    }
+    int port;
+    bool serve_http = false;
+    if (results[PORT_OPTION] != null) {
+      serve_http = true;
+      try {
+        port = int.parse(results[PORT_OPTION]);
+      } on FormatException {
+        print('Invalid port number: ${results[PORT_OPTION]}');
+        print('');
+        _printUsage(parser);
+        exitCode = 1;
+        return;
+      }
+    }
+
+    if (serve_http) {
+      httpServer.serveHttp(port);
+    }
+    stdioServer.serveStdio().then((_) {
+      if (serve_http) {
+        httpServer.close();
+      }
+    });
+  }
+
+  /**
+   * Print information about how to use the server.
+   */
+  void _printUsage(ArgParser parser) {
+    print('Usage: $BINARY_NAME [flags]');
+    print('');
+    print('Supported flags are:');
+    print(parser.getUsage());
+  }
+}
diff --git a/pkg/analysis_server/lib/http_server.dart b/pkg/analysis_server/lib/http_server.dart
index fb88f79..af63b70 100644
--- a/pkg/analysis_server/lib/http_server.dart
+++ b/pkg/analysis_server/lib/http_server.dart
@@ -4,14 +4,12 @@
 
 library http.server;
 
+import 'dart:async';
 import 'dart:io';
 
-import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/channel.dart';
-import 'package:analysis_server/src/domain_context.dart';
-import 'package:analysis_server/src/domain_server.dart';
 import 'package:analysis_server/src/get_handler.dart';
-import 'package:args/args.dart';
+import 'package:analysis_server/src/socket_server.dart';
 
 /**
  * Instances of the class [HttpServer] implement a simple HTTP server. The
@@ -20,26 +18,10 @@
  */
 class HttpAnalysisServer {
   /**
-   * The name of the application that is used to start a server.
+   * An object that can handle either a WebSocket connection or a connection
+   * to the client over stdio.
    */
-  static const BINARY_NAME = 'server';
-
-  /**
-   * The name of the option used to print usage information.
-   */
-  static const String HELP_OPTION = "help";
-
-  /**
-   * The name of the option used to specify the port to which the server will
-   * connect.
-   */
-  static const String PORT_OPTION = "port";
-
-  /**
-   * The analysis server that was created when an UPGRADE request was received,
-   * or `null` if no such request has yet been received.
-   */
-  AnalysisServer analysisServer;
+  SocketServer socketServer;
 
   /**
    * An object that can handle GET requests.
@@ -49,47 +31,12 @@
   /**
    * Initialize a newly created HTTP server.
    */
-  HttpAnalysisServer();
+  HttpAnalysisServer(this.socketServer);
 
   /**
-   * Use the given command-line arguments to start this server.
+   * Future that is completed with the HTTP server once it is running.
    */
-  void start(List<String> args) {
-    ArgParser parser = new ArgParser();
-    parser.addFlag(
-        HELP_OPTION,
-        help: "print this help message without starting a server",
-        defaultsTo: false,
-        negatable: false);
-    parser.addOption(
-        PORT_OPTION,
-        help: "[port] the port on which the server will listen");
-
-    ArgResults results = parser.parse(args);
-    if (results[HELP_OPTION]) {
-      _printUsage(parser);
-      return;
-    }
-    if (results[PORT_OPTION] == null) {
-      print('Missing required port number');
-      print('');
-      _printUsage(parser);
-      exitCode = 1;
-      return;
-    }
-
-    try {
-      int port = int.parse(results[PORT_OPTION]);
-      HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port).then(_handleServer);
-      print('Listening on port $port');
-    } on FormatException {
-      print('Invalid port number: ${results[PORT_OPTION]}');
-      print('');
-      _printUsage(parser);
-      exitCode = 1;
-      return;
-    }
-  }
+  Future<HttpServer> _server;
 
   /**
    * Attach a listener to a newly created HTTP server.
@@ -98,10 +45,6 @@
     httServer.listen((HttpRequest request) {
       List<String> updateValues = request.headers[HttpHeaders.UPGRADE];
       if (updateValues != null && updateValues.indexOf('websocket') >= 0) {
-        if (analysisServer != null) {
-          _returnServerAlreadyStarted(request);
-          return;
-        }
         WebSocketTransformer.upgrade(request).then((WebSocket websocket) {
           _handleWebSocket(websocket);
         });
@@ -118,8 +61,7 @@
    */
   void _handleGetRequest(HttpRequest request) {
     if (getHandler == null) {
-      getHandler = new GetHandler();
-      getHandler.server = analysisServer;
+      getHandler = new GetHandler(socketServer);
     }
     getHandler.handleGetRequest(request);
   }
@@ -129,44 +71,7 @@
    * running an analysis server on a [WebSocket]-based communication channel.
    */
   void _handleWebSocket(WebSocket socket) {
-    analysisServer = new AnalysisServer(new WebSocketServerChannel(socket));
-    _initializeHandlers(analysisServer);
-    if (getHandler != null) {
-      getHandler.server = analysisServer;
-    }
-    analysisServer.run();
-  }
-
-  /**
-   * Initialize the handlers to be used by the given [server].
-   */
-  void _initializeHandlers(AnalysisServer server) {
-    server.handlers = [
-        new ServerDomainHandler(server),
-        new ContextDomainHandler(server),
-    ];
-  }
-
-  /**
-   * Print information about how to use the server.
-   */
-  void _printUsage(ArgParser parser) {
-    print('Usage: $BINARY_NAME [flags]');
-    print('');
-    print('Supported flags are:');
-    print(parser.getUsage());
-  }
-
-  /**
-   * Return an error in response to an UPGRADE request received after the server
-   * has already been started by a previous UPGRADE request.
-   */
-  void _returnServerAlreadyStarted(HttpRequest request) {
-    HttpResponse response = request.response;
-    response.statusCode = HttpStatus.SERVICE_UNAVAILABLE;
-    response.headers.add(HttpHeaders.CONTENT_TYPE, "text/plain");
-    response.write('The server has already been started');
-    response.close();
+    socketServer.createAnalysisServer(new WebSocketServerChannel(socket));
   }
 
   /**
@@ -180,4 +85,18 @@
     response.write('Not found');
     response.close();
   }
+
+  /**
+   * Begin serving HTTP requests over the given port.
+   */
+  void serveHttp(int port) {
+    _server = HttpServer.bind(InternetAddress.LOOPBACK_IP_V4, port);
+    _server.then(_handleServer);
+  }
+
+  void close() {
+    _server.then((HttpServer server) {
+      server.close();
+    });
+  }
 }
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 1c9004f..f63bd6e 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -32,6 +32,11 @@
   static const String SOURCE_PARAM = 'source';
 
   /**
+   * The event name of the connected notification.
+   */
+  static const String CONNECTED_NOTIFICATION = 'server.connected';
+
+  /**
    * The channel from which requests are received and to which responses should
    * be sent.
    */
@@ -66,6 +71,8 @@
   AnalysisServer(this.channel) {
     AnalysisEngine.instance.logger = new AnalysisLogger();
     running = true;
+    Notification notification = new Notification(CONNECTED_NOTIFICATION);
+    channel.sendNotification(notification);
     channel.listen(handleRequest, onDone: done, onError: error);
   }
 
diff --git a/pkg/analysis_server/lib/src/channel.dart b/pkg/analysis_server/lib/src/channel.dart
index 6d60abd..f6b3230 100644
--- a/pkg/analysis_server/lib/src/channel.dart
+++ b/pkg/analysis_server/lib/src/channel.dart
@@ -170,6 +170,66 @@
 }
 
 /**
+ * Instances of the class [ByteStreamServerChannel] implement a
+ * [ClientCommunicationChannel] that uses a stream and a sink (typically,
+ * standard input and standard output) to communicate with servers.
+ */
+class ByteStreamServerChannel implements ServerCommunicationChannel {
+  final Stream input;
+  final IOSink output;
+
+  /**
+   * Completer that will be signalled when the input stream is closed.
+   */
+  final Completer _closed = new Completer();
+
+  ByteStreamServerChannel(this.input, this.output);
+
+  /**
+   * Future that will be completed when the input stream is closed.
+   */
+  Future get closed {
+    return _closed.future;
+  }
+
+  @override
+  void listen(void onRequest(Request request), {Function onError, void
+      onDone()}) {
+    input.transform((new Utf8Codec()).decoder).transform(new LineSplitter()
+        ).listen((String data) => _readRequest(data, onRequest), onError: onError,
+        onDone: () {
+      _closed.complete();
+      onDone();
+    });
+  }
+
+  @override
+  void sendNotification(Notification notification) {
+    output.writeln(JSON.encode(notification.toJson()));
+  }
+
+  @override
+  void sendResponse(Response response) {
+    output.writeln(JSON.encode(response.toJson()));
+  }
+
+  /**
+   * Read a request from the given [data] and use the given function to handle
+   * the request.
+   */
+  void _readRequest(Object data, void onRequest(Request request)) {
+    // Parse the string as a JSON descriptor and process the resulting
+    // structure as a request.
+    Request request = new Request.fromString(data);
+    if (request == null) {
+      sendResponse(new Response.invalidRequestFormat());
+      return;
+    }
+    onRequest(request);
+  }
+}
+
+/**
  * Instances of the class [JsonStreamDecoder] convert JSON strings to JSON
  * maps.
  */
diff --git a/pkg/analysis_server/lib/src/domain_context.dart b/pkg/analysis_server/lib/src/domain_context.dart
index 0fc8759..0126866 100644
--- a/pkg/analysis_server/lib/src/domain_context.dart
+++ b/pkg/analysis_server/lib/src/domain_context.dart
@@ -135,7 +135,7 @@
    */
   Response applyChanges(Request request) {
     AnalysisContext context = getAnalysisContext(request);
-    Map<String, Object> changesData = request.getRequiredParameter(CHANGES_PARAM);
+    RequestDatum changesData = request.getRequiredParameter(CHANGES_PARAM);
     ChangeSet changeSet = createChangeSet(
         request,
         context.sourceFactory,
@@ -150,7 +150,8 @@
    * Convert the given JSON object into a [ChangeSet], using the given
    * [sourceFactory] to convert the embedded strings into sources.
    */
-  ChangeSet createChangeSet(Request request, SourceFactory sourceFactory, Map<String, Object> jsonData) {
+  ChangeSet createChangeSet(Request request, SourceFactory sourceFactory,
+                            RequestDatum jsonData) {
     ChangeSet changeSet = new ChangeSet();
     convertSources(request, sourceFactory, jsonData[ADDED_PARAM], (Source source) {
       changeSet.addedSource(source);
@@ -170,11 +171,8 @@
    * [handler]. Otherwise, throw an exception indicating that the data in the
    * request was not valid.
    */
-  void convertSources(Request request, SourceFactory sourceFactory, Object sources, void handler(Source source)) {
-    if (sources is! List<String>) {
-      throw new RequestFailure(new Response(request.id, new RequestError(1, 'Invalid sources')));
-    }
-    convertToSources(sourceFactory, sources).forEach(handler);
+  void convertSources(Request request, SourceFactory sourceFactory, RequestDatum sources, void handler(Source source)) {
+    convertToSources(sourceFactory, sources.asStringList()).forEach(handler);
   }
 
   /**
@@ -204,21 +202,21 @@
    * throw a [RequestFailure] exception if the analysis options are not valid.
    */
   AnalysisOptions createAnalysisOptions(Request request) {
-    Map<String, Object> optionsData = request.getRequiredParameter(OPTIONS_PARAM);
+    RequestDatum optionsData = request.getRequiredParameter(OPTIONS_PARAM);
     AnalysisOptionsImpl options = new AnalysisOptionsImpl();
-    optionsData.forEach((String key, Object value) {
+    optionsData.forEachMap((String key, RequestDatum value) {
       if (key == CACHE_SIZE_OPTION) {
-        options.cacheSize = request.toInt(value);
+        options.cacheSize = value.asInt();
       } else if (key == GENERATE_HINTS_OPTION) {
-        options.hint = request.toBool(value);
+        options.hint = value.asBool();
       } else if (key == GENERATE_DART2JS_OPTION) {
-        options.dart2jsHint = request.toBool(value);
+        options.dart2jsHint = value.asBool();
       } else if (key == PROVIDE_ERRORS_OPTION) {
-//        options.provideErrors = toBool(request, value);
+//        options.provideErrors = value.asBool();
       } else if (key == PROVIDE_NAVIGATION_OPTION) {
-//        options.provideNavigation = toBool(request, value);
+//        options.provideNavigation = value.asBool();
       } else if (key == PROVIDE_OUTLINE_OPTION) {
-//        options.provideOutline = toBool(request, value);
+//        options.provideOutline = value.asBool();
       } else {
         throw new RequestFailure(new Response.unknownAnalysisOption(request, key));
       }
@@ -232,7 +230,7 @@
    */
   Response setPrioritySources(Request request) {
     AnalysisContext context = getAnalysisContext(request);
-    List<String> sourcesData = request.getRequiredParameter(SOURCES_PARAM);
+    List<String> sourcesData = request.getRequiredParameter(SOURCES_PARAM).asStringList();
     List<Source> sources = convertToSources(context.sourceFactory, sourcesData);
 
     context.analysisPriorityOrder = sources;
@@ -258,7 +256,7 @@
    * the specified context does not exist.
    */
   AnalysisContext getAnalysisContext(Request request) {
-    String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM);
+    String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM).asString();
     AnalysisContext context = server.contextMap[contextId];
     if (context == null) {
       throw new RequestFailure(new Response.contextDoesNotExist(request));
diff --git a/pkg/analysis_server/lib/src/domain_server.dart b/pkg/analysis_server/lib/src/domain_server.dart
index 40b0205..102a79c 100644
--- a/pkg/analysis_server/lib/src/domain_server.dart
+++ b/pkg/analysis_server/lib/src/domain_server.dart
@@ -53,11 +53,6 @@
   static const String SDK_DIRECTORY_PARAM = 'sdkDirectory';
 
   /**
-   * The name of the contextId result value.
-   */
-  static const String CONTEXT_ID_RESULT = 'contextId';
-
-  /**
    * The name of the version result value.
    */
   static const String VERSION_RESULT = 'version';
@@ -97,14 +92,12 @@
    * Clients, therefore, are responsible for managing the lifetime of contexts.
    */
   Response createContext(Request request) {
-    String sdkDirectory = request.getRequiredParameter(SDK_DIRECTORY_PARAM);
-    Map<String, String> packageMap = request.getParameter(PACKAGE_MAP_PARAM);
+    String sdkDirectory = request.getRequiredParameter(SDK_DIRECTORY_PARAM).asString();
+    Map<String, String> packageMap = request.getParameter(PACKAGE_MAP_PARAM, {}).asStringMap();
 
-    String baseContextId = new DateTime.now().millisecondsSinceEpoch.toRadixString(16);
-    String contextId = baseContextId;
-    int index = 1;
-    while (server.contextMap.containsKey(contextId)) {
-      contextId = '$baseContextId-$index';
+    String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM).asString();
+    if (server.contextMap.containsKey(contextId)) {
+      return new Response.contextAlreadyExists(request);
     }
     AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
     // TODO(brianwilkerson) Use the information from the request to set the
@@ -125,7 +118,6 @@
     server.contextMap[contextId] = context;
 
     Response response = new Response(request.id);
-    response.setResult(CONTEXT_ID_RESULT, contextId);
     return response;
   }
 
@@ -134,7 +126,7 @@
    * will result in an error being returned.
    */
   Response deleteContext(Request request) {
-    String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM);
+    String contextId = request.getRequiredParameter(CONTEXT_ID_PARAM).asString();
 
     AnalysisContext removedContext = server.contextMap.remove(contextId);
     if (removedContext == null) {
diff --git a/pkg/analysis_server/lib/src/generated/service_computers.dart b/pkg/analysis_server/lib/src/generated/service_computers.dart
new file mode 100644
index 0000000..9c57ba0
--- /dev/null
+++ b/pkg/analysis_server/lib/src/generated/service_computers.dart
@@ -0,0 +1,400 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library service.computers;
+
+import 'package:analyzer/src/generated/java_core.dart' show JavaStringBuilder, StringUtils;
+import 'package:analyzer/src/generated/source.dart' show Source;
+import 'package:analyzer/src/generated/scanner.dart' show Token;
+import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart' show Element;
+import 'service_interfaces.dart';
+
+/**
+ * A concrete implementation of [SourceRegion].
+ */
+class SourceRegionImpl implements SourceRegion {
+  final int offset;
+
+  final int length;
+
+  SourceRegionImpl(this.offset, this.length);
+
+  @override
+  bool containsInclusive(int x) => offset <= x && x <= offset + length;
+
+  @override
+  String toString() {
+    JavaStringBuilder builder = new JavaStringBuilder();
+    builder.append("[offset=");
+    builder.append(offset);
+    builder.append(", length=");
+    builder.append(length);
+    builder.append("]");
+    return builder.toString();
+  }
+}
+
+/**
+ * A concrete implementation of [Outline].
+ */
+class OutlineImpl implements Outline {
+  final Outline parent;
+
+  final OutlineKind kind;
+
+  final String name;
+
+  final int offset;
+
+  final int length;
+
+  final String arguments;
+
+  final String returnType;
+
+  List<Outline> children = Outline.EMPTY_ARRAY;
+
+  OutlineImpl(this.parent, this.kind, this.name, this.offset, this.length, this.arguments, this.returnType);
+
+  @override
+  String toString() {
+    JavaStringBuilder builder = new JavaStringBuilder();
+    builder.append("[name=");
+    builder.append(name);
+    builder.append(", kind=");
+    builder.append(kind);
+    builder.append(", offset=");
+    builder.append(offset);
+    builder.append(", length=");
+    builder.append(length);
+    builder.append(", arguments=");
+    builder.append(arguments);
+    builder.append(", return=");
+    builder.append(returnType);
+    builder.append(", children=[");
+    builder.append(StringUtils.join(children, ", "));
+    builder.append("]]");
+    return builder.toString();
+  }
+}
+
+/**
+ * A computer for [NavigationRegion]s in a Dart [CompilationUnit].
+ */
+class DartUnitNavigationComputer {
+  final CompilationUnit _unit;
+
+  List<NavigationRegion> _regions = [];
+
+  DartUnitNavigationComputer(this._unit);
+
+  /**
+   * Returns the computed [NavigationRegion]s, not `null`.
+   */
+  List<NavigationRegion> compute() {
+    _unit.accept(new RecursiveAstVisitor_DartUnitNavigationComputer_compute(this));
+    return new List.from(_regions);
+  }
+
+  /**
+   * If the given [Element] is not `null`, then creates a corresponding
+   * [NavigationRegion].
+   */
+  void _addRegionForNode(AstNode node, Element element) {
+    int offset = node.offset;
+    int length = node.length;
+    _addRegion(offset, length, element);
+  }
+
+  /**
+   * If the given [Element] is not `null`, then creates a corresponding
+   * [NavigationRegion].
+   */
+  void _addRegion(int offset, int length, Element element) {
+    NavigationTarget target = _createTarget(element);
+    if (target == null) {
+      return;
+    }
+    _regions.add(new NavigationRegionImpl(offset, length, <NavigationTarget> [target]));
+  }
+
+  /**
+   * If the given [Element] is not `null`, then creates a corresponding
+   * [NavigationRegion].
+   */
+  void _addRegionForToken(Token token, Element element) {
+    int offset = token.offset;
+    int length = token.length;
+    _addRegion(offset, length, element);
+  }
+
+  /**
+   * Returns the [NavigationTarget] for the given [Element], maybe `null` if
+   * `null` was given.
+   */
+  NavigationTarget _createTarget(Element element) {
+    if (element == null) {
+      return null;
+    }
+    return new NavigationTargetImpl(element.source, _getElementId(element), element.nameOffset, element.displayName.length);
+  }
+
+  String _getElementId(Element element) => element.location.encoding;
+}
+
+class RecursiveAstVisitor_DartUnitNavigationComputer_compute extends RecursiveAstVisitor<Object> {
+  final DartUnitNavigationComputer DartUnitNavigationComputer_this;
+
+  RecursiveAstVisitor_DartUnitNavigationComputer_compute(this.DartUnitNavigationComputer_this) : super();
+
+  @override
+  Object visitAssignmentExpression(AssignmentExpression node) {
+    DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+    return super.visitAssignmentExpression(node);
+  }
+
+  @override
+  Object visitBinaryExpression(BinaryExpression node) {
+    DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+    return super.visitBinaryExpression(node);
+  }
+
+  @override
+  Object visitIndexExpression(IndexExpression node) {
+    DartUnitNavigationComputer_this._addRegionForToken(node.rightBracket, node.bestElement);
+    return super.visitIndexExpression(node);
+  }
+
+  @override
+  Object visitPostfixExpression(PostfixExpression node) {
+    DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+    return super.visitPostfixExpression(node);
+  }
+
+  @override
+  Object visitPrefixExpression(PrefixExpression node) {
+    DartUnitNavigationComputer_this._addRegionForToken(node.operator, node.bestElement);
+    return super.visitPrefixExpression(node);
+  }
+
+  @override
+  Object visitSimpleIdentifier(SimpleIdentifier node) {
+    DartUnitNavigationComputer_this._addRegionForNode(node, node.bestElement);
+    return super.visitSimpleIdentifier(node);
+  }
+}
+
+/**
+ * A concrete implementation of [NavigationRegion].
+ */
+class NavigationRegionImpl extends SourceRegionImpl implements NavigationRegion {
+  final List<NavigationTarget> targets;
+
+  NavigationRegionImpl(int offset, int length, this.targets) : super(offset, length);
+
+  @override
+  String toString() {
+    JavaStringBuilder builder = new JavaStringBuilder();
+    builder.append(super.toString());
+    builder.append(" -> [");
+    builder.append(StringUtils.join(targets, ", "));
+    builder.append("]");
+    return builder.toString();
+  }
+}
+
+/**
+ * A concrete implementation of [NavigationTarget].
+ */
+class NavigationTargetImpl implements NavigationTarget {
+  final Source source;
+
+  final String elementId;
+
+  final int offset;
+
+  final int length;
+
+  NavigationTargetImpl(this.source, this.elementId, this.offset, this.length);
+
+  @override
+  String toString() {
+    JavaStringBuilder builder = new JavaStringBuilder();
+    builder.append("[offset=");
+    builder.append(offset);
+    builder.append(", length=");
+    builder.append(length);
+    builder.append(", source=");
+    builder.append(source);
+    builder.append(", element=");
+    builder.append(elementId);
+    builder.append("]");
+    return builder.toString();
+  }
+}
+
+/**
+ * A computer for [Outline]s in a Dart [CompilationUnit].
+ */
+class DartUnitOutlineComputer {
+  final CompilationUnit _unit;
+
+  DartUnitOutlineComputer(this._unit);
+
+  /**
+   * Returns the computed [Outline]s, not `null`.
+   */
+  Outline compute() {
+    OutlineImpl unitOutline = _newUnitOutline();
+    List<Outline> unitChildren = [];
+    for (CompilationUnitMember unitMember in _unit.declarations) {
+      if (unitMember is ClassDeclaration) {
+        ClassDeclaration classDeclartion = unitMember;
+        OutlineImpl classOutline = _newClassOutline(unitOutline, unitChildren, classDeclartion);
+        List<Outline> classChildren = [];
+        for (ClassMember classMember in classDeclartion.members) {
+          if (classMember is ConstructorDeclaration) {
+            ConstructorDeclaration constructorDeclaration = classMember;
+            _newConstructorOutline(classOutline, classChildren, 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) {
+                _newField(classOutline, classChildren, fieldTypeName, field);
+              }
+            }
+          }
+          if (classMember is MethodDeclaration) {
+            MethodDeclaration methodDeclaration = classMember;
+            _newMethodOutline(classOutline, classChildren, methodDeclaration);
+          }
+        }
+        classOutline.children = new List.from(classChildren);
+      }
+      if (unitMember is FunctionDeclaration) {
+        FunctionDeclaration functionDeclaration = unitMember;
+        _newFunctionOutline(unitOutline, unitChildren, functionDeclaration);
+      }
+      if (unitMember is ClassTypeAlias) {
+        ClassTypeAlias alias = unitMember;
+        _newClassTypeAlias(unitOutline, unitChildren, alias);
+      }
+      if (unitMember is FunctionTypeAlias) {
+        FunctionTypeAlias alias = unitMember;
+        _newFunctionTypeAliasOutline(unitOutline, unitChildren, alias);
+      }
+    }
+    unitOutline.children = new List.from(unitChildren);
+    return unitOutline;
+  }
+
+  void _addLocalFunctionOutlines(OutlineImpl parenet, FunctionBody body) {
+    List<Outline> localOutlines = [];
+    body.accept(new RecursiveAstVisitor_DartUnitOutlineComputer_addLocalFunctionOutlines(this, parenet, localOutlines));
+    parenet.children = new List.from(localOutlines);
+  }
+
+  OutlineImpl _newClassOutline(Outline unitOutline, List<Outline> unitChildren, ClassDeclaration classDeclartion) {
+    SimpleIdentifier nameNode = classDeclartion.name;
+    String name = nameNode.name;
+    OutlineImpl outline = new OutlineImpl(unitOutline, OutlineKind.CLASS, name, nameNode.offset, name.length, null, null);
+    unitChildren.add(outline);
+    return outline;
+  }
+
+  void _newClassTypeAlias(Outline unitOutline, List<Outline> unitChildren, ClassTypeAlias alias) {
+    SimpleIdentifier nameNode = alias.name;
+    unitChildren.add(new OutlineImpl(unitOutline, OutlineKind.CLASS_TYPE_ALIAS, nameNode.name, nameNode.offset, nameNode.length, null, null));
+  }
+
+  void _newConstructorOutline(OutlineImpl classOutline, List<Outline> children, ConstructorDeclaration constructorDeclaration) {
+    Identifier returnType = constructorDeclaration.returnType;
+    String name = returnType.name;
+    int offset = returnType.offset;
+    int length = returnType.length;
+    SimpleIdentifier constructorNameNode = constructorDeclaration.name;
+    if (constructorNameNode != null) {
+      name += ".${constructorNameNode.name}";
+      offset = constructorNameNode.offset;
+      length = constructorNameNode.length;
+    }
+    FormalParameterList parameters = constructorDeclaration.parameters;
+    OutlineImpl outline = new OutlineImpl(classOutline, OutlineKind.CONSTRUCTOR, name, offset, length, parameters != null ? parameters.toSource() : "", null);
+    children.add(outline);
+    _addLocalFunctionOutlines(outline, constructorDeclaration.body);
+  }
+
+  void _newField(OutlineImpl classOutline, List<Outline> children, String fieldTypeName, VariableDeclaration field) {
+    SimpleIdentifier nameNode = field.name;
+    children.add(new OutlineImpl(classOutline, OutlineKind.FIELD, nameNode.name, nameNode.offset, nameNode.length, null, fieldTypeName));
+  }
+
+  void _newFunctionOutline(Outline unitOutline, List<Outline> unitChildren, FunctionDeclaration functionDeclaration) {
+    TypeName returnType = functionDeclaration.returnType;
+    SimpleIdentifier nameNode = functionDeclaration.name;
+    FunctionExpression functionExpression = functionDeclaration.functionExpression;
+    FormalParameterList parameters = functionExpression.parameters;
+    OutlineKind kind;
+    if (functionDeclaration.isGetter) {
+      kind = OutlineKind.GETTER;
+    } else if (functionDeclaration.isSetter) {
+      kind = OutlineKind.SETTER;
+    } else {
+      kind = OutlineKind.FUNCTION;
+    }
+    OutlineImpl outline = new OutlineImpl(unitOutline, kind, nameNode.name, nameNode.offset, nameNode.length, parameters != null ? parameters.toSource() : "", returnType != null ? returnType.toSource() : "");
+    unitChildren.add(outline);
+    _addLocalFunctionOutlines(outline, functionExpression.body);
+  }
+
+  void _newFunctionTypeAliasOutline(Outline unitOutline, List<Outline> unitChildren, FunctionTypeAlias alias) {
+    TypeName returnType = alias.returnType;
+    SimpleIdentifier nameNode = alias.name;
+    FormalParameterList parameters = alias.parameters;
+    unitChildren.add(new OutlineImpl(unitOutline, OutlineKind.FUNCTION_TYPE_ALIAS, nameNode.name, nameNode.offset, nameNode.length, parameters != null ? parameters.toSource() : "", returnType != null ? returnType.toSource() : ""));
+  }
+
+  void _newMethodOutline(OutlineImpl classOutline, List<Outline> children, MethodDeclaration methodDeclaration) {
+    TypeName returnType = methodDeclaration.returnType;
+    SimpleIdentifier nameNode = methodDeclaration.name;
+    FormalParameterList parameters = methodDeclaration.parameters;
+    OutlineKind kind;
+    if (methodDeclaration.isGetter) {
+      kind = OutlineKind.GETTER;
+    } else if (methodDeclaration.isSetter) {
+      kind = OutlineKind.SETTER;
+    } else {
+      kind = OutlineKind.METHOD;
+    }
+    OutlineImpl outline = new OutlineImpl(classOutline, kind, nameNode.name, nameNode.offset, nameNode.length, parameters != null ? parameters.toSource() : "", returnType != null ? returnType.toSource() : "");
+    children.add(outline);
+    _addLocalFunctionOutlines(outline, methodDeclaration.body);
+  }
+
+  OutlineImpl _newUnitOutline() => new OutlineImpl(null, OutlineKind.COMPILATION_UNIT, null, 0, 0, null, null);
+}
+
+class RecursiveAstVisitor_DartUnitOutlineComputer_addLocalFunctionOutlines extends RecursiveAstVisitor<Object> {
+  final DartUnitOutlineComputer DartUnitOutlineComputer_this;
+
+  OutlineImpl parenet;
+
+  List<Outline> localOutlines;
+
+  RecursiveAstVisitor_DartUnitOutlineComputer_addLocalFunctionOutlines(this.DartUnitOutlineComputer_this, this.parenet, this.localOutlines) : super();
+
+  @override
+  Object visitFunctionDeclaration(FunctionDeclaration node) {
+    DartUnitOutlineComputer_this._newFunctionOutline(parenet, localOutlines, node);
+    return null;
+  }
+}
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/src/generated/service_interfaces.dart b/pkg/analysis_server/lib/src/generated/service_interfaces.dart
new file mode 100644
index 0000000..fc3007f
--- /dev/null
+++ b/pkg/analysis_server/lib/src/generated/service_interfaces.dart
@@ -0,0 +1,337 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This code was auto-generated, is not intended to be edited, and is subject to
+// significant change. Please see the README file for more information.
+
+library service.interfaces;
+
+import 'package:analyzer/src/generated/java_core.dart' show Enum;
+import 'package:analyzer/src/generated/source.dart' show Source;
+
+/**
+ * The interface `HighlightRegion` defines the behavior of objects representing a particular
+ * syntactic or semantic meaning associated with a source region.
+ */
+abstract class HighlightRegion implements SourceRegion {
+  /**
+   * Return the type of highlight associated with the region.
+   *
+   * @return the type of highlight associated with the region
+   */
+  HighlightType get type;
+}
+
+/**
+ * The interface `NavigationTarget` defines the behavior of objects that provide information
+ * about the target of a navigation region.
+ */
+abstract class NavigationTarget {
+  /**
+   * Return the id of the element to which this target will navigate.
+   *
+   * @return the id of the element to which this target will navigate
+   */
+  String get elementId;
+
+  /**
+   * Return the length of the region to which the target will navigate.
+   *
+   * @return the length of the region to which the target will navigate
+   */
+  int get length;
+
+  /**
+   * Return the offset to the region to which the target will navigate.
+   *
+   * @return the offset to the region to which the target will navigate
+   */
+  int get offset;
+
+  /**
+   * Return the source containing the element to which this target will navigate.
+   *
+   * @return the source containing the element to which this target will navigate
+   */
+  Source get source;
+}
+
+/**
+ * The enumeration `OutlineKind` defines the various kinds of [Outline] items.
+ */
+class OutlineKind extends Enum<OutlineKind> {
+  static const OutlineKind CLASS = const OutlineKind('CLASS', 0);
+
+  static const OutlineKind CLASS_TYPE_ALIAS = const OutlineKind('CLASS_TYPE_ALIAS', 1);
+
+  static const OutlineKind CONSTRUCTOR = const OutlineKind('CONSTRUCTOR', 2);
+
+  static const OutlineKind GETTER = const OutlineKind('GETTER', 3);
+
+  static const OutlineKind FIELD = const OutlineKind('FIELD', 4);
+
+  static const OutlineKind FUNCTION = const OutlineKind('FUNCTION', 5);
+
+  static const OutlineKind FUNCTION_TYPE_ALIAS = const OutlineKind('FUNCTION_TYPE_ALIAS', 6);
+
+  static const OutlineKind METHOD = const OutlineKind('METHOD', 7);
+
+  static const OutlineKind SETTER = const OutlineKind('SETTER', 8);
+
+  static const OutlineKind TOP_LEVEL_VARIABLE = const OutlineKind('TOP_LEVEL_VARIABLE', 9);
+
+  static const OutlineKind COMPILATION_UNIT = const OutlineKind('COMPILATION_UNIT', 10);
+
+  static const List<OutlineKind> values = const [
+      CLASS,
+      CLASS_TYPE_ALIAS,
+      CONSTRUCTOR,
+      GETTER,
+      FIELD,
+      FUNCTION,
+      FUNCTION_TYPE_ALIAS,
+      METHOD,
+      SETTER,
+      TOP_LEVEL_VARIABLE,
+      COMPILATION_UNIT];
+
+  const OutlineKind(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `SourceSet` defines the behavior of objects that represent a set of
+ * [Source]s.
+ */
+abstract class SourceSet {
+  /**
+   * An instance of [SourceSet] for [SourceSetKind#ALL].
+   */
+  static final SourceSet ALL = new _ImplicitSourceSet(SourceSetKind.ALL);
+
+  /**
+   * An instance of [SourceSet] for [SourceSetKind#NON_SDK].
+   */
+  static final SourceSet NON_SDK = new _ImplicitSourceSet(SourceSetKind.NON_SDK);
+
+  /**
+   * An instance of [SourceSet] for [SourceSetKind#EXPLICITLY_ADDED].
+   */
+  static final SourceSet EXPLICITLY_ADDED = new _ImplicitSourceSet(SourceSetKind.EXPLICITLY_ADDED);
+
+  /**
+   * Return the kind of the this source set.
+   */
+  SourceSetKind get kind;
+
+  /**
+   * Returns [Source]s that belong to this source set, if [SourceSetKind#LIST] is used;
+   * an empty array otherwise.
+   */
+  List<Source> get sources;
+}
+
+/**
+ * The enumeration `SourceSetKind` defines the kinds of [SourceSet]s.
+ */
+class SourceSetKind extends Enum<SourceSetKind> {
+  static const SourceSetKind ALL = const SourceSetKind('ALL', 0);
+
+  static const SourceSetKind NON_SDK = const SourceSetKind('NON_SDK', 1);
+
+  static const SourceSetKind EXPLICITLY_ADDED = const SourceSetKind('EXPLICITLY_ADDED', 2);
+
+  static const SourceSetKind LIST = const SourceSetKind('LIST', 3);
+
+  static const List<SourceSetKind> values = const [ALL, NON_SDK, EXPLICITLY_ADDED, LIST];
+
+  const SourceSetKind(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `SourceRegion` defines the behavior of objects representing a range of
+ * characters within a [Source].
+ */
+abstract class SourceRegion {
+  /**
+   * Check if <code>x</code> is in [offset, offset + length] interval.
+   */
+  bool containsInclusive(int x);
+
+  /**
+   * Return the length of the region.
+   *
+   * @return the length of the region
+   */
+  int get length;
+
+  /**
+   * Return the offset to the beginning of the region.
+   *
+   * @return the offset to the beginning of the region
+   */
+  int get offset;
+}
+
+/**
+ * The enumeration `NotificationKind` defines the kinds of notification clients may subscribe
+ * for.
+ */
+class NotificationKind extends Enum<NotificationKind> {
+  static const NotificationKind ERRORS = const NotificationKind('ERRORS', 0);
+
+  static const NotificationKind HIGHLIGHT = const NotificationKind('HIGHLIGHT', 1);
+
+  static const NotificationKind NAVIGATION = const NotificationKind('NAVIGATION', 2);
+
+  static const NotificationKind OUTLINE = const NotificationKind('OUTLINE', 3);
+
+  static const List<NotificationKind> values = const [ERRORS, HIGHLIGHT, NAVIGATION, OUTLINE];
+
+  const NotificationKind(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `NavigationRegion` defines the behavior of objects representing a list of
+ * elements with which a source region is associated.
+ */
+abstract class NavigationRegion implements SourceRegion {
+  /**
+   * An empty array of navigation regions.
+   */
+  static final List<NavigationRegion> EMPTY_ARRAY = new List<NavigationRegion>(0);
+
+  /**
+   * Return the identifiers of the elements associated with the region.
+   *
+   * @return the identifiers of the elements associated with the region
+   */
+  List<NavigationTarget> get targets;
+}
+
+/**
+ * An implementation of [SourceSet] for some [SourceSetKind].
+ */
+class _ImplicitSourceSet implements SourceSet {
+  final SourceSetKind kind;
+
+  _ImplicitSourceSet(this.kind);
+
+  @override
+  List<Source> get sources => Source.EMPTY_ARRAY;
+
+  @override
+  String toString() => kind.toString();
+}
+
+/**
+ * The enumeration `HighlightType` defines the kinds of highlighting that can be associated
+ * with a region of text.
+ */
+class HighlightType extends Enum<HighlightType> {
+  static const HighlightType COMMENT_BLOCK = const HighlightType('COMMENT_BLOCK', 0);
+
+  static const HighlightType COMMENT_DOCUMENTATION = const HighlightType('COMMENT_DOCUMENTATION', 1);
+
+  static const HighlightType COMMENT_END_OF_LINE = const HighlightType('COMMENT_END_OF_LINE', 2);
+
+  static const HighlightType KEYWORD = const HighlightType('KEYWORD', 3);
+
+  static const HighlightType LITERAL_BOOLEAN = const HighlightType('LITERAL_BOOLEAN', 4);
+
+  static const HighlightType LITERAL_DOUBLE = const HighlightType('LITERAL_DOUBLE', 5);
+
+  static const HighlightType LITERAL_INTEGER = const HighlightType('LITERAL_INTEGER', 6);
+
+  static const HighlightType LITERAL_LIST = const HighlightType('LITERAL_LIST', 7);
+
+  static const HighlightType LITERAL_MAP = const HighlightType('LITERAL_MAP', 8);
+
+  static const HighlightType LITERAL_STRING = const HighlightType('LITERAL_STRING', 9);
+
+  static const List<HighlightType> values = const [
+      COMMENT_BLOCK,
+      COMMENT_DOCUMENTATION,
+      COMMENT_END_OF_LINE,
+      KEYWORD,
+      LITERAL_BOOLEAN,
+      LITERAL_DOUBLE,
+      LITERAL_INTEGER,
+      LITERAL_LIST,
+      LITERAL_MAP,
+      LITERAL_STRING];
+
+  const HighlightType(String name, int ordinal) : super(name, ordinal);
+}
+
+/**
+ * The interface `Outline` defines the behavior of objects that represent an outline for a
+ * single source.
+ */
+abstract class Outline {
+  /**
+   * An empty array of outlines.
+   */
+  static final List<Outline> EMPTY_ARRAY = new List<Outline>(0);
+
+  /**
+   * Return the argument list for the element, or `null` if the element is not a method or
+   * function. If the element has zero arguments, the string `"()"` will be returned.
+   *
+   * @return the argument list for the element
+   */
+  String get arguments;
+
+  /**
+   * Return an array containing the children of the element. The array will be empty if the element
+   * has no children.
+   *
+   * @return an array containing the children of the element
+   */
+  List<Outline> get children;
+
+  /**
+   * Return the kind of the element.
+   *
+   * @return the kind of the element
+   */
+  OutlineKind get kind;
+
+  /**
+   * Return the length of the element's name.
+   *
+   * @return the length of the element's name
+   */
+  int get length;
+
+  /**
+   * Return the name of the element.
+   *
+   * @return the name of the element
+   */
+  String get name;
+
+  /**
+   * Return the offset to the beginning of the element's name.
+   *
+   * @return the offset to the beginning of the element's name
+   */
+  int get offset;
+
+  /**
+   * Return the outline that either physically or logically encloses this outline. This will be
+   * `null` if this outline is a unit outline.
+   *
+   * @return the outline that encloses this outline
+   */
+  Outline get parent;
+
+  /**
+   * Return the return type of the element, or `null` if the element is not a method or
+   * function. If the element does not have a declared return type then an empty string will be
+   * returned.
+   *
+   * @return the return type of the element
+   */
+  String get returnType;
+}
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/src/get_handler.dart b/pkg/analysis_server/lib/src/get_handler.dart
index c93e9bb..be7d47b 100644
--- a/pkg/analysis_server/lib/src/get_handler.dart
+++ b/pkg/analysis_server/lib/src/get_handler.dart
@@ -8,7 +8,7 @@
 
 import 'package:analyzer/src/generated/engine.dart';
 
-import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/socket_server.dart';
 
 /**
  * Instances of the class [GetHandler] handle GET requests.
@@ -20,15 +20,14 @@
   static const String STATUS_PATH = '/status';
 
   /**
-   * The analysis server whose status is to be reported on, or `null` if the
-   * server has not yet been created.
+   * The socket server whose status is to be reported on.
    */
-  AnalysisServer server;
+  SocketServer _server;
 
   /**
    * Initialize a newly created handler for GET requests.
    */
-  GetHandler();
+  GetHandler(SocketServer this._server);
 
   /**
    * Handle a GET request received by the HTTP server.
@@ -55,7 +54,7 @@
     response.write('</head>');
     response.write('<body>');
     response.write('<h1>Analysis Server</h1>');
-    if (server == null) {
+    if (_server.analysisServer == null) {
       response.write('<p>Not running</p>');
     } else {
       response.write('<p>Running</p>');
@@ -66,7 +65,7 @@
           response,
           ['Context', 'ERROR', 'FLUSHED', 'IN_PROCESS', 'INVALID', 'VALID'],
           true);
-      server.contextMap.forEach((String key, AnalysisContext context) {
+      _server.analysisServer.contextMap.forEach((String key, AnalysisContext context) {
         AnalysisContentStatistics statistics =
             (context as AnalysisContextImpl).statistics;
         int errorCount = 0;
@@ -90,8 +89,8 @@
             validCount]);
       });
       response.write('</table>');
-      server.contextMap.forEach((String key, AnalysisContext context) {
-        response.write('<h2><a name="context_$key">Analysis Context: $key}</a></h2>');
+      _server.analysisServer.contextMap.forEach((String key, AnalysisContext context) {
+        response.write('<h2><a name="context_$key">Analysis Context: $key</a></h2>');
         AnalysisContentStatistics statistics = (context as AnalysisContextImpl).statistics;
         response.write('<table>');
         _writeRow(
diff --git a/pkg/analysis_server/lib/src/protocol.dart b/pkg/analysis_server/lib/src/protocol.dart
index 4b5a561..ef58a8a 100644
--- a/pkg/analysis_server/lib/src/protocol.dart
+++ b/pkg/analysis_server/lib/src/protocol.dart
@@ -94,22 +94,28 @@
   }
 
   /**
-   * Return the value of the parameter with the given [name], or `null` if there
-   * is no such parameter associated with this request.
+   * Return the value of the parameter with the given [name], or [defaultValue]
+   * if there is no such parameter associated with this request.
    */
-  Object getParameter(String name) => params[name];
+  RequestDatum getParameter(String name, dynamic defaultValue) {
+    Object value = params[name];
+    if (value == null) {
+      return new RequestDatum(this, "default for $name", defaultValue);
+    }
+    return new RequestDatum(this, name, params[name]);
+  }
 
   /**
    * Return the value of the parameter with the given [name], or throw a
    * [RequestFailure] exception with an appropriate error message if there is no
    * such parameter associated with this request.
    */
-  Object getRequiredParameter(String name) {
+  RequestDatum getRequiredParameter(String name) {
     Object value = params[name];
     if (value == null) {
       throw new RequestFailure(new Response.missingRequiredParameter(this, name));
     }
-    return value;
+    return new RequestDatum(this, name, value);
   }
 
   /**
@@ -120,40 +126,6 @@
   }
 
   /**
-   * Convert the given [value] to a boolean, or throw a [RequestFailure]
-   * exception if the [value] could not be converted.
-   *
-   * The value is typically the result of invoking either [getParameter] or
-   * [getRequiredParameter].
-   */
-  bool toBool(Object value) {
-    if (value is bool) {
-      return value;
-    } else if (value is String) {
-      return value == 'true';
-    }
-    throw new RequestFailure(new Response.expectedBoolean(this, value));
-  }
-
-  /**
-   * Convert the given [value] to an integer, or throw a [RequestFailure]
-   * exception if the [value] could not be converted.
-   *
-   * The value is typically the result of invoking either [getParameter] or
-   * [getRequiredParameter].
-   */
-  int toInt(Object value) {
-    if (value is int) {
-      return value;
-    } else if (value is String) {
-      return int.parse(value, onError: (String value) {
-        throw new RequestFailure(new Response.expectedInteger(this, value));
-      });
-    }
-    throw new RequestFailure(new Response.expectedInteger(this, value));
-  }
-
-  /**
    * Return a table representing the structure of the Json object that will be
    * sent to the client to represent this response.
    */
@@ -169,6 +141,134 @@
 }
 
 /**
+ * Instances of the class [RequestDatum] wrap a piece of data from a
+ * request parameter, and contain accessor methods which automatically validate
+ * and convert the data into the appropriate form.
+ */
+class RequestDatum {
+  /**
+   * Request object that should be referred to in any errors that are
+   * generated.
+   */
+  final Request request;
+
+  /**
+   * String description of how [datum] was obtained from the request.
+   */
+  final String path;
+
+  /**
+   * Value to be decoded and validated.
+   */
+  final dynamic datum;
+
+  /**
+   * Create a RequestDatum for decoding and validating [datum], which refers to
+   * [request] in any errors it reports.
+   */
+  RequestDatum(this.request, this.path, this.datum);
+
+  /**
+   * Validate that the datum is a Map containing the given [key], and return
+   * a [RequestDatum] containing the corresponding value.
+   */
+  RequestDatum operator [](String key) {
+    if (datum is! Map<String, dynamic>) {
+      throw new RequestFailure(new Response.invalidParameter(request, path,
+          "be a map"));
+    }
+    if (!datum.containsKey(key)) {
+      throw new RequestFailure(new Response.invalidParameter(request, path,
+          "contain key '$key'"));
+    }
+    return new RequestDatum(request, "$path.$key", datum[key]);
+  }
+
+  /**
+   * Validate that the datum is a Map whose keys are strings, and call [f] on
+   * each key/value pair in the map.
+   */
+  void forEachMap(void f(String key, RequestDatum value)) {
+    if (datum is! Map<String, dynamic>) {
+      throw new RequestFailure(new Response.invalidParameter(request, path,
+          "be a map"));
+    }
+    datum.forEach((String key, dynamic value) {
+      f(key, new RequestDatum(request, "$path.$key", value));
+    });
+  }
+
+  /**
+   * Validate that the datum is an integer (or a string that can be parsed
+   * as an integer), and return the int.
+   */
+  int asInt() {
+    if (datum is int) {
+      return datum;
+    } else if (datum is String) {
+      return int.parse(datum, onError: (String value) {
+        throw new RequestFailure(new Response.invalidParameter(request, path,
+            "be an integer"));
+      });
+    }
+    throw new RequestFailure(new Response.invalidParameter(request, path,
+        "be an integer"));
+  }
+
+  /**
+   * Validate that the datum is a boolean (or a string that can be parsed
+   * as a boolean), and return the bool.
+   *
+   * The value is typically the result of invoking either [getParameter] or
+   * [getRequiredParameter].
+   */
+  bool asBool() {
+    if (datum is bool) {
+      return datum;
+    } else if (datum == 'true') {
+      return datum == 'true';
+    } else if (datum == 'false') {
+      return datum == 'false';
+    }
+    throw new RequestFailure(new Response.invalidParameter(request, datum,
+        "be a boolean"));
+  }
+
+  /**
+   * Validate that the datum is a string, and return it.
+   */
+  String asString() {
+    if (datum is! String) {
+      throw new RequestFailure(new Response.invalidParameter(request, path,
+          "be a string"));
+    }
+    return datum;
+  }
+
+  /**
+   * Validate that the datum is a list of strings, and return it.
+   */
+  List<String> asStringList() {
+    if (datum is! List<String>) {
+      throw new RequestFailure(new Response.invalidParameter(request, path,
+          "be a list of strings"));
+    }
+    return datum;
+  }
+
+  /**
+   * Validate that the datum is a map from strings to strings, and return it.
+   */
+  Map<String, String> asStringMap() {
+    if (datum is! Map<String, String>) {
+      throw new RequestFailure(new Response.invalidParameter(request, path,
+          "be a string map"));
+    }
+    return datum;
+  }
+}
+
+/**
  * Instances of the class [Response] represent a response to a request.
  */
 class Response {
@@ -222,19 +322,14 @@
 
   /**
    * Initialize a newly created instance to represent an error condition caused
-   * by a [request] that was expected to have a boolean-valued parameter but was
-   * passed a non-boolean value.
+   * by a [request] that had invalid parameter.  [path] is the path to the
+   * invalid parameter, in Javascript notation (e.g. "foo.bar" means that the
+   * parameter "foo" contained a key "bar" whose value was the wrong type).
+   * [expectation] is a description of the type of data that was expected.
    */
-  Response.expectedBoolean(Request request, Object value)
-    : this(request.id, new RequestError(-2, 'Expected a boolean value, but found "$value"'));
-
-  /**
-   * Initialize a newly created instance to represent an error condition caused
-   * by a [request] that was expected to have a integer-valued parameter but was
-   * passed a non-integer value.
-   */
-  Response.expectedInteger(Request request, Object value)
-    : this(request.id, new RequestError(-3, 'Expected an integer value, but found "$value"'));
+  Response.invalidParameter(Request request, String path, String expectation)
+      : this(request.id, new RequestError(-2,
+          "Expected parameter $path to $expectation"));
 
   /**
    * Initialize a newly created instance to represent an error condition caused
@@ -265,6 +360,9 @@
   Response.unknownRequest(Request request)
     : this(request.id, new RequestError(-7, 'Unknown request'));
 
+  Response.contextAlreadyExists(Request request)
+    : this(request.id, new RequestError(-8, 'Context already exists'));
+
   /**
    * Initialize a newly created instance based upon the given JSON data
    */
@@ -353,6 +451,12 @@
    * server. An error occurred on the server while parsing the JSON text.
    */
   static const int CODE_PARSE_ERROR = -32700;
+  
+  /**
+   * An error code indicating that the analysis server has already been
+   * started (and hence won't accept new connections).
+   */
+  static const int CODE_SERVER_ALREADY_STARTED = -32701;
 
   /**
    * An error code indicating an invalid request. The JSON sent is not a valid
@@ -414,6 +518,13 @@
   RequestError.parseError() : this(CODE_PARSE_ERROR, "Parse error");
 
   /**
+   * Initialize a newly created [Error] to indicate that the analysis server
+   * has already been started (and hence won't accept new connections).
+   */
+  RequestError.serverAlreadyStarted()
+    : this(CODE_SERVER_ALREADY_STARTED, "Server already started");
+  
+  /**
    * Initialize a newly created [Error] to indicate an invalid request. The
    * JSON sent is not a valid [Request] object.
    */
diff --git a/pkg/analysis_server/lib/src/socket_server.dart b/pkg/analysis_server/lib/src/socket_server.dart
new file mode 100644
index 0000000..1d74d62
--- /dev/null
+++ b/pkg/analysis_server/lib/src/socket_server.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library socket.server;
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/channel.dart';
+import 'package:analysis_server/src/domain_context.dart';
+import 'package:analysis_server/src/domain_server.dart';
+import 'package:analysis_server/src/protocol.dart';
+
+/**
+ * Instances of the class [SocketServer] implement the common parts of
+ * http-based and stdio-based analysis servers.  The primary responsibility of
+ * the SocketServer is to manage the lifetime of the AnalysisServer and to
+ * encode and decode the JSON messages exchanged with the client.
+ */
+class SocketServer {
+  /**
+   * The analysis server that was created when a client established a
+   * connection, or `null` if no such connection has yet been established.
+   */
+  AnalysisServer analysisServer;
+
+  /**
+   * Create an analysis server which will communicate with the client using the
+   * given serverChannel.
+   */
+  void createAnalysisServer(ServerCommunicationChannel serverChannel) {
+    if (analysisServer != null) {
+      var error = new RequestError.serverAlreadyStarted();
+      serverChannel.sendResponse(new Response('', error));
+      serverChannel.listen((Request request) {
+        serverChannel.sendResponse(new Response(request.id, error));
+      });
+      return;
+    }
+    analysisServer = new AnalysisServer(serverChannel);
+    _initializeHandlers(analysisServer);
+    analysisServer.run();
+  }
+
+  /**
+   * Initialize the handlers to be used by the given [server].
+   */
+  void _initializeHandlers(AnalysisServer server) {
+    server.handlers = [
+        new ServerDomainHandler(server),
+        new ContextDomainHandler(server),
+    ];
+  }
+
+}
\ No newline at end of file
diff --git a/pkg/analysis_server/lib/stdio_server.dart b/pkg/analysis_server/lib/stdio_server.dart
new file mode 100644
index 0000000..a0cc7e3
--- /dev/null
+++ b/pkg/analysis_server/lib/stdio_server.dart
@@ -0,0 +1,42 @@
+// 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 stdio.server;
+
+import 'dart:async';
+import 'dart:io';
+
+import 'package:analysis_server/src/channel.dart';
+import 'package:analysis_server/src/socket_server.dart';
+
+/**
+ * Instances of the class [StdioServer] implement a simple server operating
+ * over standard input and output. The primary responsibility of this server
+ * is to split incoming messages on newlines and pass them along to the
+ * analysis server.
+ */
+class StdioAnalysisServer {
+  /**
+   * An object that can handle either a WebSocket connection or a connection
+   * to the client over stdio.
+   */
+  SocketServer socketServer;
+
+  /**
+   * Initialize a newly created stdio server.
+   */
+  StdioAnalysisServer(this.socketServer);
+
+  /**
+   * Begin serving requests over stdio.
+   *
+   * Return a future that will be completed when stdin closes.
+   */
+  Future serveStdio() {
+    ByteStreamServerChannel serverChannel = new ByteStreamServerChannel(stdin,
+        stdout);
+    socketServer.createAnalysisServer(serverChannel);
+    return serverChannel.closed;
+  }
+}
diff --git a/pkg/analysis_server/pubspec.yaml b/pkg/analysis_server/pubspec.yaml
index 5af2c46..031fd6e 100644
--- a/pkg/analysis_server/pubspec.yaml
+++ b/pkg/analysis_server/pubspec.yaml
@@ -6,7 +6,7 @@
 environment:
   sdk: '>=1.0.0 <2.0.0'
 dependencies:
-  analyzer: 0.13.0-dev.6
+  analyzer: 0.13.6
   args: any
   logging: any
 dev_dependencies:
diff --git a/pkg/analysis_server/test/analysis_server_test.dart b/pkg/analysis_server/test/analysis_server_test.dart
index d1cc597..eeaebdf 100644
--- a/pkg/analysis_server/test/analysis_server_test.dart
+++ b/pkg/analysis_server/test/analysis_server_test.dart
@@ -16,7 +16,7 @@
 main() {
   group('AnalysisServer', () {
     setUp(AnalysisServerTest.setUp);
-//    test('createContext', AnalysisServerTest.createContext);
+    test('createContext', AnalysisServerTest.createContext);
     test('echo', AnalysisServerTest.echo);
     test('shutdown', AnalysisServerTest.shutdown);
     test('unknownRequest', AnalysisServerTest.unknownRequest);
@@ -36,13 +36,11 @@
     server.handlers = [new ServerDomainHandler(server)];
     var request = new Request('my27', ServerDomainHandler.CREATE_CONTEXT_METHOD);
     request.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+    request.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, 'ctx');
     return channel.sendRequest(request)
-        .timeout(new Duration(seconds: 1))
         .then((Response response) {
           expect(response.id, equals('my27'));
           expect(response.error, isNull);
-          var contextId = response.result[ServerDomainHandler.CONTEXT_ID_RESULT];
-          expect(contextId is String, isTrue);
         });
   }
 
@@ -50,7 +48,6 @@
     server.handlers = [new EchoHandler()];
     var request = new Request('my22', 'echo');
     return channel.sendRequest(request)
-        .timeout(new Duration(seconds: 1))
         .then((Response response) {
           expect(response.id, equals('my22'));
           expect(response.error, isNull);
@@ -62,7 +59,6 @@
     var request = new Request('my28', ServerDomainHandler.SHUTDOWN_METHOD);
     request.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, '');
     return channel.sendRequest(request)
-        .timeout(new Duration(seconds: 1))
         .then((Response response) {
           expect(response.id, equals('my28'));
           expect(response.error, isNull);
@@ -73,7 +69,6 @@
     server.handlers = [new EchoHandler()];
     var request = new Request('my22', 'randomRequest');
     return channel.sendRequest(request)
-        .timeout(new Duration(seconds: 1))
         .then((Response response) {
           expect(response.id, equals('my22'));
           expect(response.error, isNotNull);
diff --git a/pkg/analysis_server/test/channel_test.dart b/pkg/analysis_server/test/channel_test.dart
index 6d58887..1f23745 100644
--- a/pkg/analysis_server/test/channel_test.dart
+++ b/pkg/analysis_server/test/channel_test.dart
@@ -5,6 +5,8 @@
 library test.channel;
 
 import 'dart:async';
+import 'dart:convert';
+import 'dart:io';
 
 import 'package:analysis_server/src/channel.dart';
 import 'package:analysis_server/src/protocol.dart';
@@ -24,6 +26,19 @@
     test('requestResponse', WebSocketChannelTest.requestResponse);
     test('response', WebSocketChannelTest.response);
   });
+  group('ByteStreamServerChannel', () {
+    setUp(ByteStreamServerChannelTest.setUp);
+    test('closed', ByteStreamServerChannelTest.closed);
+    test('listen_wellFormedRequest',
+        ByteStreamServerChannelTest.listen_wellFormedRequest);
+    test('listen_invalidRequest',
+        ByteStreamServerChannelTest.listen_invalidRequest);
+    test('listen_invalidJson', ByteStreamServerChannelTest.listen_invalidJson);
+    test('listen_streamError', ByteStreamServerChannelTest.listen_streamError);
+    test('listen_streamDone', ByteStreamServerChannelTest.listen_streamDone);
+    test('sendNotification', ByteStreamServerChannelTest.sendNotification);
+    test('sendResponse', ByteStreamServerChannelTest.sendResponse);
+  });
 }
 
 class WebSocketChannelTest {
@@ -156,4 +171,131 @@
     expect(responsesReceived, hasLength(responseCount));
     expect(notificationsReceived, hasLength(notificationCount));
   }
-}
\ No newline at end of file
+}
+
+class ByteStreamServerChannelTest {
+  static ByteStreamServerChannel channel;
+
+  /**
+   * Sink that may be used to deliver data to the channel, as though it's
+   * coming from the client.
+   */
+  static IOSink inputSink;
+
+  /**
+   * Stream of lines sent back to the client by the channel.
+   */
+  static Stream<String> outputLineStream;
+
+  /**
+   * Stream of requests received from the channel via [listen()].
+   */
+  static Stream<Request> requestStream;
+
+  /**
+   * Stream of errors received from the channel via [listen()].
+   */
+  static Stream errorStream;
+
+  /**
+   * Future which is completed when then [listen()] reports [onDone].
+   */
+  static Future doneFuture;
+
+  static void setUp() {
+    StreamController<List<int>> inputStream = new StreamController<List<int>>();
+    inputSink = new IOSink(inputStream);
+    StreamController<List<int>> outputStream = new StreamController<List<int>>(
+        );
+    outputLineStream = outputStream.stream.transform((new Utf8Codec()).decoder
+        ).transform(new LineSplitter());
+    IOSink outputSink = new IOSink(outputStream);
+    channel = new ByteStreamServerChannel(inputStream.stream, outputSink);
+    StreamController<Request> requestStreamController =
+        new StreamController<Request>();
+    requestStream = requestStreamController.stream;
+    StreamController errorStreamController = new StreamController();
+    errorStream = errorStreamController.stream;
+    Completer doneCompleter = new Completer();
+    doneFuture = doneCompleter.future;
+    channel.listen((Request request) {
+      requestStreamController.add(request);
+    }, onError: (error) {
+      errorStreamController.add(error);
+    }, onDone: () {
+      doneCompleter.complete();
+    });
+  }
+
+  static Future closed() {
+    return inputSink.close().then((_) => channel.closed.timeout(new Duration(
+        seconds: 1)));
+  }
+
+  static Future listen_wellFormedRequest() {
+    inputSink.writeln('{"id":"0","method":"server.version"}');
+    return inputSink.flush().then((_) => requestStream.first.timeout(
+        new Duration(seconds: 1))).then((Request request) {
+      expect(request.id, equals("0"));
+      expect(request.method, equals("server.version"));
+    });
+  }
+
+  static Future listen_invalidRequest() {
+    inputSink.writeln('{"id":"0"}');
+    return inputSink.flush().then((_) => outputLineStream.first.timeout(
+        new Duration(seconds: 1))).then((String response) {
+      var jsonResponse = new JsonCodec().decode(response);
+      expect(jsonResponse, isMap);
+      expect(jsonResponse, contains('error'));
+      expect(jsonResponse['error'], isNotNull);
+    });
+  }
+
+  static Future listen_invalidJson() {
+    inputSink.writeln('{"id":');
+    return inputSink.flush().then((_) => outputLineStream.first.timeout(
+        new Duration(seconds: 1))).then((String response) {
+      var jsonResponse = new JsonCodec().decode(response);
+      expect(jsonResponse, isMap);
+      expect(jsonResponse, contains('error'));
+      expect(jsonResponse['error'], isNotNull);
+    });
+  }
+
+  static Future listen_streamError() {
+    var error = new Error();
+    inputSink.addError(error);
+    return inputSink.flush().then((_) => errorStream.first.timeout(new Duration(
+        seconds: 1))).then((var receivedError) {
+      expect(receivedError, same(error));
+    });
+  }
+
+  static Future listen_streamDone() {
+    return inputSink.close().then((_) => doneFuture.timeout(new Duration(
+        seconds: 1)));
+  }
+
+  static Future sendNotification() {
+    channel.sendNotification(new Notification('foo'));
+    return outputLineStream.first.timeout(new Duration(seconds: 1)).then((String
+        notification) {
+      var jsonNotification = new JsonCodec().decode(notification);
+      expect(jsonNotification, isMap);
+      expect(jsonNotification, contains('event'));
+      expect(jsonNotification['event'], equals('foo'));
+    });
+  }
+
+  static Future sendResponse() {
+    channel.sendResponse(new Response('foo'));
+    return outputLineStream.first.timeout(new Duration(seconds: 1)).then((String
+        response) {
+      var jsonResponse = new JsonCodec().decode(response);
+      expect(jsonResponse, isMap);
+      expect(jsonResponse, contains('id'));
+      expect(jsonResponse['id'], equals('foo'));
+    });
+  }
+}
diff --git a/pkg/analysis_server/test/declarative_tests.dart b/pkg/analysis_server/test/declarative_tests.dart
new file mode 100644
index 0000000..f6a6e68
--- /dev/null
+++ b/pkg/analysis_server/test/declarative_tests.dart
@@ -0,0 +1,51 @@
+library declarative_tests;
+
+import 'dart:mirrors';
+
+import 'package:unittest/unittest.dart' show group, test;
+
+/// Use [runTest] annotation to indicate that method is a test method.
+/// Alternatively method name can have the `test` prefix.
+const runTest = const _RunTest();
+
+class _RunTest {
+  const _RunTest();
+}
+
+/// Creates a new named group of tests with the name of the given [Type], then
+/// adds new tests using [addTestMethods].
+addTestSuite(Type type) {
+  group(type.toString(), () {
+    addTestMethods(type);
+  });
+}
+
+/// Creates a new test case for the each static method with the name starting
+/// with `test` or having the [runTest] annotation.
+addTestMethods(Type type) {
+  var typeMirror = reflectClass(type);
+  typeMirror.staticMembers.forEach((methodSymbol, method) {
+    if (_isTestMethod(method)) {
+      var methodName = MirrorSystem.getName(methodSymbol);
+      test(methodName, () {
+        typeMirror.invoke(methodSymbol, []);
+      });
+    }
+  });
+}
+
+bool _isTestMethod(MethodMirror method) {
+  if (method.parameters.isNotEmpty) {
+    return false;
+  }
+  var methodSymbol = method.simpleName;
+  // name starts with "test"
+  var methodName = MirrorSystem.getName(methodSymbol);
+  if (methodName.startsWith('test')) {
+    return true;
+  }
+  // has @testMethod
+  return method.metadata.any((annotation) {
+    return identical(annotation.reflectee, runTest);
+  });
+}
diff --git a/pkg/analysis_server/test/domain_context_test.dart b/pkg/analysis_server/test/domain_context_test.dart
index c66856e..358aea0 100644
--- a/pkg/analysis_server/test/domain_context_test.dart
+++ b/pkg/analysis_server/test/domain_context_test.dart
@@ -26,6 +26,8 @@
 }
 
 class ContextDomainHandlerTest {
+  static int contextIdCounter = 0;
+
   static void applyChanges() {
     AnalysisServer server = new AnalysisServer(new MockServerChannel());
     String contextId = _createContext(server);
@@ -34,6 +36,7 @@
 
     Request request = new Request('0', ContextDomainHandler.APPLY_CHANGES_NAME);
     request.setParameter(ContextDomainHandler.CONTEXT_ID_PARAM, contextId);
+    request.setParameter(ContextDomainHandler.SOURCES_PARAM, []);
     request.setParameter(ContextDomainHandler.CHANGES_PARAM, {
       ContextDomainHandler.ADDED_PARAM : ['ffile:/one.dart'],
       ContextDomainHandler.MODIFIED_PARAM : ['ffile:/two.dart'],
@@ -51,11 +54,13 @@
     Request request = new Request('0', ContextDomainHandler.APPLY_CHANGES_NAME);
     ContextDomainHandler handler = new ContextDomainHandler(server);
     SourceFactory sourceFactory = new SourceFactory([new FileUriResolver()]);
-    ChangeSet changeSet = handler.createChangeSet(request, sourceFactory, {
-      ContextDomainHandler.ADDED_PARAM : ['ffile:/one.dart'],
-      ContextDomainHandler.MODIFIED_PARAM : [],
-      ContextDomainHandler.REMOVED_PARAM : ['ffile:/two.dart', 'ffile:/three.dart']
-    });
+    ChangeSet changeSet = handler.createChangeSet(request, sourceFactory,
+        new RequestDatum(request, ContextDomainHandler.CHANGES_PARAM, {
+      ContextDomainHandler.ADDED_PARAM: ['ffile:/one.dart'],
+      ContextDomainHandler.MODIFIED_PARAM: [],
+      ContextDomainHandler.REMOVED_PARAM: ['ffile:/two.dart',
+          'ffile:/three.dart']
+    }));
     expect(changeSet.addedSources, hasLength(equals(1)));
     expect(changeSet.changedSources, hasLength(equals(0)));
     expect(changeSet.removedSources, hasLength(equals(2)));
@@ -111,14 +116,15 @@
   }
 
   static String _createContext(AnalysisServer server) {
+    String contextId = "context${contextIdCounter++}";
     ServerDomainHandler handler = new ServerDomainHandler(server);
     Request request = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
     request.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+    request.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
     Response response = handler.handleRequest(request);
     if (response.error != null) {
       fail('Unexpected error: ${response.error.toJson()}');
     }
-    expect(response.error, isNull);
-    return response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
+    return contextId;
   }
 }
diff --git a/pkg/analysis_server/test/domain_server_test.dart b/pkg/analysis_server/test/domain_server_test.dart
index 84e290c..cb63f61 100644
--- a/pkg/analysis_server/test/domain_server_test.dart
+++ b/pkg/analysis_server/test/domain_server_test.dart
@@ -13,10 +13,10 @@
 
 main() {
   group('ServerDomainHandler', () {
-//    test('createContext', ServerDomainHandlerTest.createContext);
-//    test('deleteContext_alreadyDeleted', ServerDomainHandlerTest.deleteContext_alreadyDeleted);
+    test('createContext', ServerDomainHandlerTest.createContext);
+    test('deleteContext_alreadyDeleted', ServerDomainHandlerTest.deleteContext_alreadyDeleted);
     test('deleteContext_doesNotExist', ServerDomainHandlerTest.deleteContext_doesNotExist);
-//    test('deleteContext_existing', ServerDomainHandlerTest.deleteContext_existing);
+    test('deleteContext_existing', ServerDomainHandlerTest.deleteContext_existing);
     test('shutdown', ServerDomainHandlerTest.shutdown);
     test('version', ServerDomainHandlerTest.version);
   });
@@ -29,28 +29,42 @@
 
     Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
     createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+    createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, 'ctx');
     Response response = handler.handleRequest(createRequest);
-    String contextId = response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
-    expect(contextId, isNotNull);
+    expect(response.id, equals('0'));
+    expect(response.error, isNull);
+    expect(response.result, isEmpty);
+  }
+  
+  static void createContext_alreadyExists() {
+    AnalysisServer server = new AnalysisServer(new MockServerChannel());
+    ServerDomainHandler handler = new ServerDomainHandler(server);
+
+    Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
+    createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+    createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, 'ctx');
+    Response response = handler.handleRequest(createRequest);
+    expect(response.error, isNull);
+    response = handler.handleRequest(createRequest);
+    expect(response.error, isNotNull);
   }
 
   static void deleteContext_alreadyDeleted() {
     AnalysisServer server = new AnalysisServer(new MockServerChannel());
     ServerDomainHandler handler = new ServerDomainHandler(server);
 
+    String contextId = 'ctx';
     Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
-    createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, '');
-    Response response = handler.handleRequest(createRequest);
-    String contextId = response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
+    createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+    createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
+    handler.handleRequest(createRequest);
 
     Request deleteRequest = new Request('0', ServerDomainHandler.DELETE_CONTEXT_METHOD);
     deleteRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
-    response = handler.handleRequest(deleteRequest);
-    response = handler.handleRequest(deleteRequest);
-    expect(response.toJson(), equals({
-      Response.ID: '0',
-      Response.ERROR: 'Context does not exist'
-    }));
+    handler.handleRequest(deleteRequest);
+    Response response = handler.handleRequest(deleteRequest);
+    expect(response.id, equals('0'));
+    expect(response.error, isNotNull);
   }
 
   static void deleteContext_doesNotExist() {
@@ -68,14 +82,15 @@
     AnalysisServer server = new AnalysisServer(new MockServerChannel());
     ServerDomainHandler handler = new ServerDomainHandler(server);
 
+    String contextId = 'ctx';
     Request createRequest = new Request('0', ServerDomainHandler.CREATE_CONTEXT_METHOD);
-    createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, '');
-    Response response = handler.createContext(createRequest);
-    String contextId = response.getResult(ServerDomainHandler.CONTEXT_ID_RESULT);
+    createRequest.setParameter(ServerDomainHandler.SDK_DIRECTORY_PARAM, sdkPath);
+    createRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
+    handler.createContext(createRequest);
 
     Request deleteRequest = new Request('0', ServerDomainHandler.DELETE_CONTEXT_METHOD);
     deleteRequest.setParameter(ServerDomainHandler.CONTEXT_ID_PARAM, contextId);
-    response = handler.handleRequest(deleteRequest);
+    Response response = handler.handleRequest(deleteRequest);
     expect(response.toJson(), equals({
       Response.ID: '0',
       Response.ERROR: null
diff --git a/pkg/analysis_server/test/mocks.dart b/pkg/analysis_server/test/mocks.dart
index f6e6452..f1c23a9 100644
--- a/pkg/analysis_server/test/mocks.dart
+++ b/pkg/analysis_server/test/mocks.dart
@@ -31,6 +31,20 @@
 }
 
 /**
+ * Returns a [Future] that completes after pumping the event queue [times]
+ * times. By default, this should pump the event queue enough times to allow
+ * any code to run, as long as it's not waiting on some external event.
+ */
+Future pumpEventQueue([int times = 20]) {
+  if (times == 0) return new Future.value();
+  // We use a delayed future to allow microtask events to finish. The
+  // Future.value or Future() constructors use scheduleMicrotask themselves and
+  // would therefore not wait for microtask callbacks that are scheduled after
+  // invoking this method.
+  return new Future.delayed(Duration.ZERO, () => pumpEventQueue(times - 1));
+}
+
+/**
  * A mock [WebSocket] for testing.
  */
 class MockSocket<T> implements WebSocket {
@@ -69,6 +83,17 @@
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
 
+class NoResponseException implements Exception {
+  /// The request that was not responded to.
+  final Request request;
+
+  NoResponseException(this.request);
+
+  String toString() {
+    return "NoResponseException after request ${request.toJson()}";
+  }
+}
+
 /**
  * A mock [ServerCommunicationChannel] for testing [AnalysisServer].
  */
@@ -100,7 +125,10 @@
     var id = request.id;
     // Wrap send request in future to simulate websocket
     new Future(() => requestController.add(request));
-    return responseController.stream.firstWhere((response) => response.id == id);
+    pumpEventQueue().then((_) => responseController.addError(
+        new NoResponseException(request)));
+    return responseController.stream.firstWhere((response) => response.id == id
+        );
   }
 
   @override
diff --git a/pkg/analysis_server/test/protocol_test.dart b/pkg/analysis_server/test/protocol_test.dart
index 6597f11..0869923 100644
--- a/pkg/analysis_server/test/protocol_test.dart
+++ b/pkg/analysis_server/test/protocol_test.dart
@@ -9,52 +9,18 @@
 import 'package:analysis_server/src/protocol.dart';
 import 'package:unittest/unittest.dart';
 
+import 'declarative_tests.dart';
+
 main() {
-  group('Notification', () {
-    test('getParameter_defined', NotificationTest.getParameter_defined);
-    test('getParameter_undefined', NotificationTest.getParameter_undefined);
-    test('fromJson', NotificationTest.fromJson);
-    test('fromJson_withParams', NotificationTest.fromJson_withParams);
-  });
-  group('Request', () {
-    test('getParameter_defined', RequestTest.getParameter_defined);
-    test('getParameter_undefined', RequestTest.getParameter_undefined);
-    test('getRequiredParameter_defined', RequestTest.getRequiredParameter_defined);
-    test('getRequiredParameter_undefined', RequestTest.getRequiredParameter_undefined);
-    test('fromJson', RequestTest.fromJson);
-    test('fromJson_invalidId', RequestTest.fromJson_invalidId);
-    test('fromJson_invalidMethod', RequestTest.fromJson_invalidMethod);
-    test('fromJson_invalidParams', RequestTest.fromJson_invalidParams);
-    test('fromJson_withParams', RequestTest.fromJson_withParams);
-    test('toBool', RequestTest.toBool);
-    test('toInt', RequestTest.toInt);
-    test('toJson', RequestTest.toJson);
-    test('toJson_withParams', RequestTest.toJson_withParams);
-  });
-  group('RequestError', () {
-    test('create', RequestErrorTest.create);
-    test('create_methodNotFound', RequestErrorTest.create_methodNotFound);
-    test('create_invalidParameters', RequestErrorTest.create_invalidParameters);
-    test('create_invalidRequest', RequestErrorTest.create_invalidRequest);
-    test('create_internalError', RequestErrorTest.create_internalError);
-    test('create_parseError', RequestErrorTest.create_parseError);
-    test('fromJson', RequestErrorTest.fromJson);
-    test('toJson', RequestErrorTest.toJson);
-  });
-  group('Response', () {
-    test('create_contextDoesNotExist', ResponseTest.create_contextDoesNotExist);
-    test('create_invalidRequestFormat', ResponseTest.create_invalidRequestFormat);
-    test('create_missingRequiredParameter', ResponseTest.create_missingRequiredParameter);
-    test('create_unknownAnalysisOption', ResponseTest.create_unknownAnalysisOption);
-    test('create_unknownRequest', ResponseTest.create_unknownRequest);
-    test('setResult', ResponseTest.setResult);
-    test('fromJson', ResponseTest.fromJson);
-    test('fromJson_withError', ResponseTest.fromJson_withError);
-    test('fromJson_withResult', ResponseTest.fromJson_withResult);
-  });
+  addTestSuite(NotificationTest);
+  addTestSuite(RequestTest);
+  addTestSuite(RequestErrorTest);
+  addTestSuite(RequestDatumTest);
+  addTestSuite(ResponseTest);
 }
 
 class NotificationTest {
+  @runTest
   static void getParameter_defined() {
     Notification notification = new Notification('foo');
     notification.setParameter('x', 'y');
@@ -67,6 +33,7 @@
     }));
   }
 
+  @runTest
   static void getParameter_undefined() {
     Notification notification = new Notification('foo');
     expect(notification.event, equals('foo'));
@@ -77,6 +44,7 @@
     }));
   }
 
+  @runTest
   static void fromJson() {
     Notification original = new Notification('foo');
     Notification notification = new Notification.fromJson(original.toJson());
@@ -85,6 +53,7 @@
     expect(notification.getParameter('x'), isNull);
   }
 
+  @runTest
   static void fromJson_withParams() {
     Notification original = new Notification('foo');
     original.setParameter('x', 'y');
@@ -96,34 +65,40 @@
 }
 
 class RequestTest {
+  @runTest
   static void getParameter_defined() {
     String name = 'name';
     String value = 'value';
     Request request = new Request('0', '');
     request.setParameter(name, value);
-    expect(request.getParameter(name), equals(value));
+    expect(request.getParameter(name, null).datum, equals(value));
   }
 
+  @runTest
   static void getParameter_undefined() {
     String name = 'name';
+    String defaultValue = 'default value';
     Request request = new Request('0', '');
-    expect(request.getParameter(name), isNull);
+    expect(request.getParameter(name, defaultValue).datum, equals(defaultValue));
   }
 
+  @runTest
   static void getRequiredParameter_defined() {
     String name = 'name';
     String value = 'value';
     Request request = new Request('0', '');
     request.setParameter(name, value);
-    expect(request.getRequiredParameter(name), equals(value));
+    expect(request.getRequiredParameter(name).datum, equals(value));
   }
 
+  @runTest
   static void getRequiredParameter_undefined() {
     String name = 'name';
     Request request = new Request('0', '');
     expect(() => request.getRequiredParameter(name), _throwsRequestFailure);
   }
 
+  @runTest
   static void fromJson() {
     Request original = new Request('one', 'aMethod');
     String json = JSON.encode(original.toJson());
@@ -132,24 +107,28 @@
     expect(request.method, equals('aMethod'));
   }
 
+  @runTest
   static void fromJson_invalidId() {
     String json = '{"id":{"one":"two"},"method":"aMethod","params":{"foo":"bar"}}';
     Request request = new Request.fromString(json);
     expect(request, isNull);
   }
 
+  @runTest
   static void fromJson_invalidMethod() {
     String json = '{"id":"one","method":{"boo":"aMethod"},"params":{"foo":"bar"}}';
     Request request = new Request.fromString(json);
     expect(request, isNull);
   }
 
+  @runTest
   static void fromJson_invalidParams() {
     String json = '{"id":"one","method":"aMethod","params":"foobar"}';
     Request request = new Request.fromString(json);
     expect(request, isNull);
   }
 
+  @runTest
   static void fromJson_withParams() {
     Request original = new Request('one', 'aMethod');
     original.setParameter('foo', 'bar');
@@ -157,27 +136,10 @@
     Request request = new Request.fromString(json);
     expect(request.id, equals('one'));
     expect(request.method, equals('aMethod'));
-    expect(request.getParameter('foo'), equals('bar'));
+    expect(request.getParameter('foo', null).asString(), equals('bar'));
   }
 
-  static void toBool() {
-    Request request = new Request('0', '');
-    expect(request.toBool(true), isTrue);
-    expect(request.toBool(false), isFalse);
-    expect(request.toBool('true'), isTrue);
-    expect(request.toBool('false'), isFalse);
-    expect(request.toBool('abc'), isFalse);
-    expect(() => request.toBool(42), _throwsRequestFailure);
-  }
-
-  static void toInt() {
-    Request request = new Request('0', '');
-    expect(request.toInt(1), equals(1));
-    expect(request.toInt('2'), equals(2));
-    expect(() => request.toInt('xxx'), _throwsRequestFailure);
-    expect(() => request.toInt(request), _throwsRequestFailure);
-  }
-
+  @runTest
   static void toJson() {
     Request request = new Request('one', 'aMethod');
     expect(request.toJson(), equals({
@@ -186,6 +148,7 @@
     }));
   }
 
+  @runTest
   static void toJson_withParams() {
     Request request = new Request('one', 'aMethod');
     request.setParameter('foo', 'bar');
@@ -198,6 +161,7 @@
 }
 
 class RequestErrorTest {
+  @runTest
   static void create() {
     RequestError error = new RequestError(42, 'msg');
     expect(error.code, 42);
@@ -208,36 +172,49 @@
     }));
   }
 
+  @runTest
   static void create_parseError() {
     RequestError error = new RequestError.parseError();
     expect(error.code, RequestError.CODE_PARSE_ERROR);
     expect(error.message, "Parse error");
   }
 
+  @runTest
   static void create_methodNotFound() {
     RequestError error = new RequestError.methodNotFound();
     expect(error.code, RequestError.CODE_METHOD_NOT_FOUND);
     expect(error.message, "Method not found");
   }
 
+  @runTest
   static void create_invalidParameters() {
     RequestError error = new RequestError.invalidParameters();
     expect(error.code, RequestError.CODE_INVALID_PARAMS);
     expect(error.message, "Invalid parameters");
   }
 
+  @runTest
   static void create_invalidRequest() {
     RequestError error = new RequestError.invalidRequest();
     expect(error.code, RequestError.CODE_INVALID_REQUEST);
     expect(error.message, "Invalid request");
   }
 
+  @runTest
   static void create_internalError() {
     RequestError error = new RequestError.internalError();
     expect(error.code, RequestError.CODE_INTERNAL_ERROR);
     expect(error.message, "Internal error");
   }
 
+  @runTest
+  static void create_serverAlreadyStarted() {
+    RequestError error = new RequestError.serverAlreadyStarted();
+    expect(error.code, RequestError.CODE_SERVER_ALREADY_STARTED);
+    expect(error.message, "Server already started");
+  }
+
+  @runTest
   static void fromJson() {
     var json = {
         RequestError.CODE: RequestError.CODE_PARSE_ERROR,
@@ -251,6 +228,7 @@
     expect(error.getData('ints'), [1, 2, 3]);
   }
 
+  @runTest
   static void toJson() {
     RequestError error = new RequestError(0, 'msg');
     error.setData('answer', 42);
@@ -263,7 +241,159 @@
   }
 }
 
+class InvalidParameterResponseMatcher extends Matcher {
+  static const int ERROR_CODE = -2;
+
+  @override
+  Description describe(Description description) =>
+      description.add("an 'invalid parameter' response (code $ERROR_CODE)");
+
+  @override
+  bool matches(item, Map matchState) {
+    if (item is! Response) {
+      return false;
+    }
+    Response response = item;
+    if (response.error is! RequestError) {
+      return false;
+    }
+    RequestError requestError = response.error;
+    if (requestError.code != ERROR_CODE) {
+      return false;
+    }
+    return true;
+  }
+}
+
+class RequestDatumTest {
+  static Request request;
+
+  static Matcher _throwsInvalidParameter = throwsA(
+      new InvalidParameterResponseMatcher());
+  static Matcher isRequestDatum = new isInstanceOf<RequestDatum>("RequestDatum"
+      );
+
+  static void setUp() {
+    request = new Request('myId', 'myMethod');
+  }
+
+  static RequestDatum makeDatum(dynamic datum) {
+    return new RequestDatum(request, 'myPath', datum);
+  }
+
+  static void indexOperator_nonMap() {
+    expect(() => makeDatum(1)['foo'], _throwsInvalidParameter);
+  }
+
+  static void indexOperator_missingKey() {
+    expect(() => makeDatum({
+      'foo': 'bar'
+    })['baz'], _throwsInvalidParameter);
+  }
+
+  static void indexOperator_hasKey() {
+    var indexResult = makeDatum({
+      'foo': 'bar'
+    })['foo'];
+    expect(indexResult, isRequestDatum);
+    expect(indexResult.datum, equals('bar'));
+    expect(indexResult.path, equals('myPath.bar'));
+  }
+
+  static void forEachMap_nonMap() {
+    expect(() => makeDatum(1).forEachMap((key, value) {
+      fail('Non-map should not be iterated');
+    }), _throwsInvalidParameter);
+  }
+
+  static void forEachMap_emptyMap() {
+    makeDatum({}).forEachMap((key, value) {
+      fail('Empty map should not be iterated');
+    });
+  }
+
+  static void forEachMap_oneElementMap() {
+    int callCount = 0;
+    makeDatum({
+      'key': 'value'
+    }).forEachMap((key, value) {
+      callCount++;
+      expect(key, equals('key'));
+      expect(value, isRequestDatum);
+      expect(value.datum, equals('value'));
+    });
+    expect(callCount, equals(1));
+  }
+
+  static void forEachMap_twoElementMap() {
+    int callCount = 0;
+    Map<String, String> map = {
+      'key1': 'value1',
+      'key2': 'value2'
+    };
+    Map iterationResult = {};
+    makeDatum(map).forEachMap((key, value) {
+      callCount++;
+      iterationResult[key] = value;
+    });
+    expect(callCount, equals(2));
+    expect(iterationResult, equals(map));
+  }
+
+  static void asBool() {
+    expect(makeDatum(true).asBool(), isTrue);
+    expect(makeDatum(false).asBool(), isFalse);
+    expect(makeDatum('true').asBool(), isTrue);
+    expect(makeDatum('false').asBool(), isFalse);
+    expect(() => makeDatum('abc').asBool(), _throwsInvalidParameter);
+  }
+
+  static void asInt() {
+    expect(makeDatum(1).asInt(), equals(1));
+    expect(makeDatum('2').asInt(), equals(2));
+    expect(() => makeDatum('xxx').asInt(), _throwsInvalidParameter);
+    expect(() => makeDatum(true).asInt(), _throwsInvalidParameter);
+  }
+
+  static void asString() {
+    expect(makeDatum('foo').asString(), equals('foo'));
+    expect(() => makeDatum(3).asString(), _throwsInvalidParameter);
+  }
+
+  static void asStringList() {
+    expect(makeDatum(['foo', 'bar']).asStringList(), equals(['foo', 'bar']));
+    expect(makeDatum([]).asStringList(), equals([]));
+    expect(() => makeDatum(['foo', 1]).asStringList(), _throwsInvalidParameter);
+    expect(() => makeDatum({}).asStringList(), _throwsInvalidParameter);
+  }
+
+  static void asStringMap() {
+    expect(makeDatum({
+      'key1': 'value1',
+      'key2': 'value2'
+    }).asStringMap(), equals({
+      'key1': 'value1',
+      'key2': 'value2'
+    }));
+    expect(makeDatum({}).asStringMap(), equals({}));
+    expect(() => makeDatum({
+      'key1': 'value1',
+      'key2': 2
+    }).asStringMap(), _throwsInvalidParameter);
+    expect(() => makeDatum({
+      'key1': 1,
+      'key2': 2
+    }).asStringMap(), _throwsInvalidParameter);
+    expect(() => makeDatum({
+      1: 'value1',
+      2: 'value2'
+    }).asStringMap(), _throwsInvalidParameter);
+    expect(() => makeDatum([]).asStringMap(), _throwsInvalidParameter);
+  }
+}
+
 class ResponseTest {
+  @runTest
   static void create_contextDoesNotExist() {
     Response response = new Response.contextDoesNotExist(new Request('0', ''));
     expect(response.id, equals('0'));
@@ -274,6 +404,7 @@
     }));
   }
 
+  @runTest
   static void create_invalidRequestFormat() {
     Response response = new Response.invalidRequestFormat();
     expect(response.id, equals(''));
@@ -284,6 +415,7 @@
     }));
   }
 
+  @runTest
   static void create_missingRequiredParameter() {
     Response response = new Response.missingRequiredParameter(new Request('0', ''), 'x');
     expect(response.id, equals('0'));
@@ -294,6 +426,7 @@
     }));
   }
 
+  @runTest
   static void create_unknownAnalysisOption() {
     Response response = new Response.unknownAnalysisOption(new Request('0', ''), 'x');
     expect(response.id, equals('0'));
@@ -304,6 +437,7 @@
     }));
   }
 
+  @runTest
   static void create_unknownRequest() {
     Response response = new Response.unknownRequest(new Request('0', ''));
     expect(response.id, equals('0'));
@@ -314,6 +448,7 @@
     }));
   }
 
+  @runTest
   static void setResult() {
     String resultName = 'name';
     String resultValue = 'value';
@@ -329,12 +464,14 @@
     }));
   }
 
+  @runTest
   static void fromJson() {
     Response original = new Response('myId');
     Response response = new Response.fromJson(original.toJson());
     expect(response.id, equals('myId'));
   }
 
+  @runTest
   static void fromJson_withError() {
     Response original = new Response.invalidRequestFormat();
     Response response = new Response.fromJson(original.toJson());
@@ -345,6 +482,7 @@
     expect(error.message, equals('Invalid request'));
   }
 
+  @runTest
   static void fromJson_withResult() {
     Response original = new Response('myId');
     original.setResult('foo', 'bar');
diff --git a/pkg/analysis_server/test/socket_server_test.dart b/pkg/analysis_server/test/socket_server_test.dart
new file mode 100644
index 0000000..fbc1512
--- /dev/null
+++ b/pkg/analysis_server/test/socket_server_test.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.socket.server;
+
+import 'dart:async';
+
+import 'mocks.dart';
+
+import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analysis_server/src/domain_server.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/socket_server.dart';
+import 'package:unittest/unittest.dart';
+
+main() {
+  group('SocketServer', () {
+    test('createAnalysisServer_successful',
+        SocketServerTest.createAnalysisServer_successful);
+    test('createAnalysisServer_alreadyStarted',
+        SocketServerTest.createAnalysisServer_alreadyStarted);
+  });
+}
+
+class SocketServerTest {
+  static Future createAnalysisServer_successful() {
+    SocketServer server = new SocketServer();
+    MockServerChannel channel = new MockServerChannel();
+    server.createAnalysisServer(channel);
+    channel.expectMsgCount(notificationCount: 1);
+    expect(channel.notificationsReceived[0].event, equals(
+        AnalysisServer.CONNECTED_NOTIFICATION));
+    expect(channel.notificationsReceived[0].params, isEmpty);
+    return channel.sendRequest(new Request('0',
+        ServerDomainHandler.SHUTDOWN_METHOD)).then((Response response) {
+      expect(response.id, equals('0'));
+      expect(response.error, isNull);
+      channel.expectMsgCount(responseCount: 1, notificationCount: 1);
+    });
+  }
+
+  static void createAnalysisServer_alreadyStarted() {
+    SocketServer server = new SocketServer();
+    MockServerChannel channel1 = new MockServerChannel();
+    MockServerChannel channel2 = new MockServerChannel();
+    server.createAnalysisServer(channel1);
+    expect(channel1.notificationsReceived[0].event, equals(
+        AnalysisServer.CONNECTED_NOTIFICATION));
+    expect(channel1.notificationsReceived[0].params, isEmpty);
+    server.createAnalysisServer(channel2);
+    channel1.expectMsgCount(notificationCount: 1);
+    channel2.expectMsgCount(responseCount: 1);
+    expect(channel2.responsesReceived[0].id, equals(''));
+    expect(channel2.responsesReceived[0].error, isNotNull);
+    expect(channel2.responsesReceived[0].error.code, equals(
+        RequestError.CODE_SERVER_ALREADY_STARTED));
+    channel2.sendRequest(new Request('0', ServerDomainHandler.SHUTDOWN_METHOD)
+        ).then((Response response) {
+      expect(response.id, equals('0'));
+      expect(response.error, isNotNull);
+      expect(response.error.code, equals(
+          RequestError.CODE_SERVER_ALREADY_STARTED));
+      channel2.expectMsgCount(responseCount: 2);
+    });
+  }
+}
diff --git a/pkg/analysis_server/test/test_all.dart b/pkg/analysis_server/test/test_all.dart
index e9a8ad7..e230a51 100644
--- a/pkg/analysis_server/test/test_all.dart
+++ b/pkg/analysis_server/test/test_all.dart
@@ -9,6 +9,7 @@
 import 'domain_context_test.dart' as domain_context_test;
 import 'domain_server_test.dart' as domain_server_test;
 import 'protocol_test.dart' as protocol_test;
+import 'socket_server_test.dart' as socket_server_test;
 
 /// Utility for manually running all tests
 main() {
@@ -18,5 +19,6 @@
     domain_context_test.main();
     domain_server_test.main();
     protocol_test.main();
+    socket_server_test.main();
   });
 }
\ No newline at end of file
diff --git a/pkg/analyzer/lib/src/generated/ast.dart b/pkg/analyzer/lib/src/generated/ast.dart
index 8a91113..0b9fc8f 100644
--- a/pkg/analyzer/lib/src/generated/ast.dart
+++ b/pkg/analyzer/lib/src/generated/ast.dart
@@ -12724,11 +12724,7 @@
         }
       }
     }
-    Element element = node.bestElement;
-    if (element == null) {
-      element = node.staticElement;
-    }
-    return element;
+    return node.bestElement;
   }
 
   @override
@@ -15922,7 +15918,7 @@
   SwitchStatement visitSwitchStatement(SwitchStatement node) => new SwitchStatement(node.keyword, node.leftParenthesis, _cloneNode(node.expression), node.rightParenthesis, node.leftBracket, _cloneNodeList(node.members), node.rightBracket);
 
   @override
-  AstNode visitSymbolLiteral(SymbolLiteral node) => new SymbolLiteral(node.poundSign, node.components);
+  SymbolLiteral visitSymbolLiteral(SymbolLiteral node) => new SymbolLiteral(node.poundSign, node.components);
 
   @override
   ThisExpression visitThisExpression(ThisExpression node) => new ThisExpression(node.keyword);
@@ -15992,7 +15988,7 @@
    * @param second the second node being compared
    * @return `true` if the two AST nodes are equal
    */
-  static bool equalUnits(CompilationUnit first, CompilationUnit second) {
+  static bool equalNodes(AstNode first, AstNode second) {
     AstComparator comparator = new AstComparator();
     return comparator._isEqualNodes(first, second);
   }
@@ -16682,7 +16678,7 @@
       return false;
     }
     for (int i = 0; i < length; i++) {
-      if (_isEqualTokens(first[i], second[i])) {
+      if (!_isEqualTokens(first[i], second[i])) {
         return false;
       }
     }
@@ -16701,6 +16697,8 @@
       return second == null;
     } else if (second == null) {
       return false;
+    } else if (identical(first, second)) {
+      return true;
     }
     return first.offset == second.offset && first.length == second.length && first.lexeme == second.lexeme;
   }
diff --git a/pkg/analyzer/lib/src/generated/constant.dart b/pkg/analyzer/lib/src/generated/constant.dart
index 91815ec..967a31c 100644
--- a/pkg/analyzer/lib/src/generated/constant.dart
+++ b/pkg/analyzer/lib/src/generated/constant.dart
@@ -312,11 +312,11 @@
    */
   void computeValues() {
     _declarationMap = _constantFinder.variableMap;
-    for (MapIterator<VariableElement, VariableDeclaration> iter = SingleMapIterator.forMap(_declarationMap); iter.moveNext();) {
-      VariableElement element = iter.key;
+    for (MapEntry<VariableElement, VariableDeclaration> entry in getMapEntrySet(_declarationMap)) {
+      VariableElement element = entry.getKey();
       ReferenceFinder referenceFinder = new ReferenceFinder(element, _referenceGraph);
       _referenceGraph.addNode(element);
-      iter.value.initializer.accept(referenceFinder);
+      entry.getValue().initializer.accept(referenceFinder);
     }
     while (!_referenceGraph.isEmpty) {
       VariableElement element = _referenceGraph.removeSink();
@@ -2929,7 +2929,7 @@
       return false;
     }
     GenericState state = object as GenericState;
-    Set<String> otherFields = new Set<String>();
+    Set<String> otherFields = new Set<String>.from(state._fieldMap.keys.toSet());
     for (String fieldName in _fieldMap.keys.toSet()) {
       if (_fieldMap[fieldName] != state._fieldMap[fieldName]) {
         return false;
@@ -3938,9 +3938,9 @@
     } else if (count == 0) {
       return true;
     }
-    for (MapIterator<DartObjectImpl, DartObjectImpl> iter = SingleMapIterator.forMap(_entries); iter.moveNext();) {
-      DartObjectImpl key = iter.key;
-      DartObjectImpl value = iter.value;
+    for (MapEntry<DartObjectImpl, DartObjectImpl> entry in getMapEntrySet(_entries)) {
+      DartObjectImpl key = entry.getKey();
+      DartObjectImpl value = entry.getValue();
       DartObjectImpl otherValue = otherElements[key];
       if (value != otherValue) {
         return false;
@@ -3955,9 +3955,9 @@
   @override
   Map<Object, Object> get value {
     Map<Object, Object> result = new Map<Object, Object>();
-    for (MapIterator<DartObjectImpl, DartObjectImpl> iter = SingleMapIterator.forMap(_entries); iter.moveNext();) {
-      DartObjectImpl key = iter.key;
-      DartObjectImpl value = iter.value;
+    for (MapEntry<DartObjectImpl, DartObjectImpl> entry in getMapEntrySet(_entries)) {
+      DartObjectImpl key = entry.getKey();
+      DartObjectImpl value = entry.getValue();
       if (!key.hasExactValue || !value.hasExactValue) {
         return null;
       }
@@ -3968,8 +3968,8 @@
 
   @override
   bool get hasExactValue {
-    for (MapIterator<DartObjectImpl, DartObjectImpl> iter = SingleMapIterator.forMap(_entries); iter.moveNext();) {
-      if (!iter.key.hasExactValue || !iter.value.hasExactValue) {
+    for (MapEntry<DartObjectImpl, DartObjectImpl> entry in getMapEntrySet(_entries)) {
+      if (!entry.getKey().hasExactValue || !entry.getValue().hasExactValue) {
         return false;
       }
     }
diff --git a/pkg/analyzer/lib/src/generated/element.dart b/pkg/analyzer/lib/src/generated/element.dart
index a10931f..68eb93f 100644
--- a/pkg/analyzer/lib/src/generated/element.dart
+++ b/pkg/analyzer/lib/src/generated/element.dart
@@ -3867,7 +3867,7 @@
   /**
    * The name of the top-level variable used to mark a class as implementing a proxy object.
    */
-  static String _PROXY_VARIABLE_NAME = "proxy";
+  static String PROXY_VARIABLE_NAME = "proxy";
 
   /**
    * Initialize a newly created annotation.
@@ -3913,7 +3913,7 @@
     if (element != null) {
       LibraryElement library = element.library;
       if (library != null && library.isDartCore) {
-        if (element is PropertyAccessorElement && element.name == _PROXY_VARIABLE_NAME) {
+        if (element is PropertyAccessorElement && element.name == PROXY_VARIABLE_NAME) {
           return true;
         }
       }
@@ -5901,6 +5901,15 @@
   accept(ElementVisitor visitor) => visitor.visitMethodElement(this);
 
   @override
+  String get displayName {
+    String displayName = super.displayName;
+    if ("unary-" == displayName) {
+      return "-";
+    }
+    return displayName;
+  }
+
+  @override
   ClassElement get enclosingElement => super.enclosingElement as ClassElement;
 
   @override
@@ -8495,10 +8504,12 @@
     if (secondTypes.length != firstTypes.length) {
       return false;
     }
-    MapIterator<String, DartType> firstIterator = SingleMapIterator.forMap(firstTypes);
-    MapIterator<String, DartType> secondIterator = SingleMapIterator.forMap(secondTypes);
-    while (firstIterator.moveNext() && secondIterator.moveNext()) {
-      if (firstIterator.key != secondIterator.key || !(firstIterator.value as TypeImpl).internalEquals(secondIterator.value, visitedElementPairs)) {
+    JavaIterator<MapEntry<String, DartType>> firstIterator = new JavaIterator(getMapEntrySet(firstTypes));
+    JavaIterator<MapEntry<String, DartType>> secondIterator = new JavaIterator(getMapEntrySet(secondTypes));
+    while (firstIterator.hasNext) {
+      MapEntry<String, DartType> firstEntry = firstIterator.next();
+      MapEntry<String, DartType> secondEntry = secondIterator.next();
+      if (firstEntry.getKey() != secondEntry.getKey() || !(firstEntry.getValue() as TypeImpl).internalEquals(secondEntry.getValue(), visitedElementPairs)) {
         return false;
       }
     }
@@ -8574,15 +8585,15 @@
           needsComma = false;
         }
         builder.append("{");
-        for (MapIterator<String, DartType> iter = SingleMapIterator.forMap(namedParameterTypes); iter.moveNext();) {
+        for (MapEntry<String, DartType> entry in getMapEntrySet(namedParameterTypes)) {
           if (needsComma) {
             builder.append(", ");
           } else {
             needsComma = true;
           }
-          builder.append(iter.key);
+          builder.append(entry.getKey());
           builder.append(": ");
-          builder.append(iter.value.displayName);
+          builder.append(entry.getValue().displayName);
         }
         builder.append("}");
         needsComma = true;
@@ -8753,13 +8764,14 @@
       }
       // Loop through each element in S verifying that T has a matching parameter name and that the
       // corresponding type is more specific then the type in S.
-      MapIterator<String, DartType> iteratorS = SingleMapIterator.forMap(namedTypesS);
-      while (iteratorS.moveNext()) {
-        DartType typeT = namedTypesT[iteratorS.key];
+      JavaIterator<MapEntry<String, DartType>> iteratorS = new JavaIterator(getMapEntrySet(namedTypesS));
+      while (iteratorS.hasNext) {
+        MapEntry<String, DartType> entryS = iteratorS.next();
+        DartType typeT = namedTypesT[entryS.getKey()];
         if (typeT == null) {
           return false;
         }
-        if (!(typeT as TypeImpl).isMoreSpecificThan2(iteratorS.value, withDynamic, visitedTypePairs)) {
+        if (!(typeT as TypeImpl).isMoreSpecificThan2(entryS.getValue(), withDynamic, visitedTypePairs)) {
           return false;
         }
       }
@@ -8872,15 +8884,15 @@
         needsComma = false;
       }
       builder.append("{");
-      for (MapIterator<String, DartType> iter = SingleMapIterator.forMap(namedParameterTypes); iter.moveNext();) {
+      for (MapEntry<String, DartType> entry in getMapEntrySet(namedParameterTypes)) {
         if (needsComma) {
           builder.append(", ");
         } else {
           needsComma = true;
         }
-        builder.append(iter.key);
+        builder.append(entry.getKey());
         builder.append(": ");
-        (iter.value as TypeImpl).appendTo(builder);
+        (entry.getValue() as TypeImpl).appendTo(builder);
       }
       builder.append("}");
       needsComma = true;
@@ -8968,13 +8980,14 @@
       }
       // Loop through each element in S verifying that T has a matching parameter name and that the
       // corresponding type is assignable to the type in S.
-      MapIterator<String, DartType> iteratorS = SingleMapIterator.forMap(namedTypesS);
-      while (iteratorS.moveNext()) {
-        DartType typeT = namedTypesT[iteratorS.key];
+      JavaIterator<MapEntry<String, DartType>> iteratorS = new JavaIterator(getMapEntrySet(namedTypesS));
+      while (iteratorS.hasNext) {
+        MapEntry<String, DartType> entryS = iteratorS.next();
+        DartType typeT = namedTypesT[entryS.getKey()];
         if (typeT == null) {
           return false;
         }
-        if (!(typeT as TypeImpl).isAssignableTo2(iteratorS.value, visitedTypePairs)) {
+        if (!(typeT as TypeImpl).isAssignableTo2(entryS.getValue(), visitedTypePairs)) {
           return false;
         }
       }
@@ -9130,15 +9143,14 @@
    */
   static Set<InterfaceType> _computeSuperinterfaceSet(InterfaceType type, Set<InterfaceType> set) {
     Element element = type.element;
-    if (element != null && element is ClassElement) {
-      ClassElement classElement = element;
-      List<InterfaceType> superinterfaces = classElement.interfaces;
+    if (element != null) {
+      List<InterfaceType> superinterfaces = type.interfaces;
       for (InterfaceType superinterface in superinterfaces) {
         if (set.add(superinterface)) {
           _computeSuperinterfaceSet(superinterface, set);
         }
       }
-      InterfaceType supertype = classElement.supertype;
+      InterfaceType supertype = type.superclass;
       if (supertype != null) {
         if (set.add(supertype)) {
           _computeSuperinterfaceSet(supertype, set);
@@ -9150,27 +9162,15 @@
 
   /**
    * Return the intersection of the given sets of types, where intersection is based on the equality
-   * of the elements of the types rather than on the equality of the types themselves. In cases
-   * where two non-equal types have equal elements, which only happens when the class is
-   * parameterized, the type that is added to the intersection is the base type with type arguments
-   * that are the least upper bound of the type arguments of the two types.
+   * of the types themselves.
    *
    * @param first the first set of types to be intersected
    * @param second the second set of types to be intersected
    * @return the intersection of the given sets of types
    */
   static List<InterfaceType> _intersection(Set<InterfaceType> first, Set<InterfaceType> second) {
-    Map<ClassElement, InterfaceType> firstMap = new Map<ClassElement, InterfaceType>();
-    for (InterfaceType firstType in first) {
-      firstMap[firstType.element] = firstType;
-    }
-    Set<InterfaceType> result = new Set<InterfaceType>();
-    for (InterfaceType secondType in second) {
-      InterfaceType firstType = firstMap[secondType.element];
-      if (firstType != null) {
-        result.add(_leastUpperBound(firstType, secondType));
-      }
-    }
+    Set<InterfaceType> result = new Set<InterfaceType>.from(first);
+    result.retainAll(second);
     return new List.from(result);
   }
 
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index 18d00f7..3f91c74 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -87,6 +87,11 @@
   Logger _logger = Logger.NULL;
 
   /**
+   * The partition manager being used to manage the shared partitions.
+   */
+  final PartitionManager partitionManager = new PartitionManager();
+
+  /**
    * Create a new context in which analysis can be performed.
    *
    * @return the analysis context that was created
@@ -96,9 +101,9 @@
     // If instrumentation is ignoring data, return an uninstrumented analysis context.
     //
     if (Instrumentation.isNullLogger) {
-      return new DelegatingAnalysisContextImpl();
+      return new AnalysisContextImpl();
     }
-    return new InstrumentedAnalysisContextImpl.con1(new DelegatingAnalysisContextImpl());
+    return new InstrumentedAnalysisContextImpl.con1(new AnalysisContextImpl());
   }
 
   /**
@@ -959,24 +964,42 @@
 }
 
 /**
- * Instances of the class `ChangeSet` indicate what sources have been added, changed, or
- * removed.
+ * Instances of the class `ChangeSet` indicate which sources have been added, changed,
+ * removed, or deleted. In the case of a changed source, there are multiple ways of indicating the
+ * nature of the change.
+ *
+ * No source should be added to the change set more than once, either with the same or a different
+ * kind of change. It does not make sense, for example, for a source to be both added and removed,
+ * and it is redundant for a source to be marked as changed in its entirety and changed in some
+ * specific range.
  */
 class ChangeSet {
   /**
    * A list containing the sources that have been added.
    */
-  List<Source> _added = new List<Source>();
+  final List<Source> addedSources = new List<Source>();
 
   /**
    * A list containing the sources that have been changed.
    */
-  List<Source> _changed = new List<Source>();
+  final List<Source> changedSources = new List<Source>();
+
+  /**
+   * A table mapping the sources whose content has been changed to the current content of those
+   * sources.
+   */
+  Map<Source, String> _changedContent = new Map<Source, String>();
+
+  /**
+   * A table mapping the sources whose content has been changed within a single range to the current
+   * content of those sources and information about the affected range.
+   */
+  final Map<Source, ChangeSet_ContentChange> changedRanges = new Map<Source, ChangeSet_ContentChange>();
 
   /**
    * A list containing the sources that have been removed.
    */
-  List<Source> _removed = new List<Source>();
+  final List<Source> removedSources = new List<Source>();
 
   /**
    * A list containing the source containers specifying additional sources that have been removed.
@@ -984,52 +1007,78 @@
   final List<SourceContainer> removedContainers = new List<SourceContainer>();
 
   /**
-   * Record that the specified source has been added and that it's content is the default contents
-   * of the source.
+   * A list containing the sources that have been deleted.
+   */
+  final List<Source> deletedSources = new List<Source>();
+
+  /**
+   * Record that the specified source has been added and that its content is the default contents of
+   * the source.
    *
    * @param source the source that was added
    */
   void addedSource(Source source) {
-    _added.add(source);
+    addedSources.add(source);
   }
 
   /**
-   * Record that the specified source has been changed and that it's content is the default contents
-   * of the source.
+   * Record that the specified source has been changed and that its content is the given contents.
+   *
+   * @param source the source that was changed
+   * @param contents the new contents of the source, or `null` if the default contents of the
+   *          source are to be used
+   */
+  void changedContent(Source source, String contents) {
+    _changedContent[source] = contents;
+  }
+
+  /**
+   * Record that the specified source has been changed and that its content is the given contents.
+   *
+   * @param source the source that was changed
+   * @param contents the new contents of the source
+   * @param offset the offset into the current contents
+   * @param oldLength the number of characters in the original contents that were replaced
+   * @param newLength the number of characters in the replacement text
+   */
+  void changedRange(Source source, String contents, int offset, int oldLength, int newLength) {
+    changedRanges[source] = new ChangeSet_ContentChange(contents, offset, oldLength, newLength);
+  }
+
+  /**
+   * Record that the specified source has been changed. If the content of the source was previously
+   * overridden, use [changedContent] instead.
    *
    * @param source the source that was changed
    */
   void changedSource(Source source) {
-    _changed.add(source);
+    changedSources.add(source);
   }
 
   /**
-   * Return a collection of the sources that have been added.
+   * Record that the specified source has been deleted.
    *
-   * @return a collection of the sources that have been added
+   * @param source the source that was deleted
    */
-  List<Source> get addedSources => _added;
+  void deletedSource(Source source) {
+    deletedSources.add(source);
+  }
 
   /**
-   * Return a collection of sources that have been changed.
+   * Return a table mapping the sources whose content has been changed to the current content of
+   * those sources.
    *
-   * @return a collection of sources that have been changed
+   * @return a table mapping the sources whose content has been changed to the current content of
+   *         those sources
    */
-  List<Source> get changedSources => _changed;
-
-  /**
-   * Return a list containing the sources that were removed.
-   *
-   * @return a list containing the sources that were removed
-   */
-  List<Source> get removedSources => _removed;
+  Map<Source, String> get changedContents => _changedContent;
 
   /**
    * Return `true` if this change set does not contain any changes.
    *
    * @return `true` if this change set does not contain any changes
    */
-  bool get isEmpty => _added.isEmpty && _changed.isEmpty && _removed.isEmpty && removedContainers.isEmpty;
+  bool get isEmpty => addedSources.isEmpty && changedSources.isEmpty && _changedContent.isEmpty && changedRanges.isEmpty && removedSources.isEmpty && removedContainers.isEmpty && deletedSources.isEmpty;
 
   /**
    * Record that the specified source container has been removed.
@@ -1049,19 +1098,22 @@
    */
   void removedSource(Source source) {
     if (source != null) {
-      _removed.add(source);
+      removedSources.add(source);
     }
   }
 
   @override
   String toString() {
     JavaStringBuilder builder = new JavaStringBuilder();
-    bool needsSeparator = _appendSources(builder, _added, false, "added");
-    needsSeparator = _appendSources(builder, _changed, needsSeparator, "changed");
-    _appendSources(builder, _removed, needsSeparator, "removed");
+    bool needsSeparator = _appendSources(builder, addedSources, false, "addedSources");
+    needsSeparator = _appendSources(builder, changedSources, needsSeparator, "changedSources");
+    needsSeparator = _appendSources2(builder, _changedContent, needsSeparator, "changedContent");
+    needsSeparator = _appendSources2(builder, changedRanges, needsSeparator, "changedRanges");
+    needsSeparator = _appendSources(builder, deletedSources, needsSeparator, "deletedSources");
+    needsSeparator = _appendSources(builder, removedSources, needsSeparator, "removedSources");
     int count = removedContainers.length;
     if (count > 0) {
-      if (_removed.isEmpty) {
+      if (removedSources.isEmpty) {
         if (needsSeparator) {
           builder.append("; ");
         }
@@ -1103,6 +1155,68 @@
     }
     return true;
   }
+
+  /**
+   * Append the given sources to the given builder, prefixed with the given label and possibly a
+   * separator.
+   *
+   * @param builder the builder to which the sources are to be appended
+   * @param sources the sources to be appended
+   * @param needsSeparator `true` if a separator is needed before the label
+   * @param label the label used to prefix the sources
+   * @return `true` if future lists of sources will need a separator
+   */
+  bool _appendSources2(JavaStringBuilder builder, Map<Source, dynamic> sources, bool needsSeparator, String label) {
+    if (sources.isEmpty) {
+      return needsSeparator;
+    }
+    if (needsSeparator) {
+      builder.append("; ");
+    }
+    builder.append(label);
+    String prefix = " ";
+    for (Source source in sources.keys.toSet()) {
+      builder.append(prefix);
+      builder.append(source.fullName);
+      prefix = ", ";
+    }
+    return true;
+  }
+}
+
+/**
+ * Instances of the class `ContentChange` represent a change to the content of a source.
+ */
+class ChangeSet_ContentChange {
+  /**
+   * The new contents of the source.
+   */
+  final String contents;
+
+  /**
+   * The offset into the current contents.
+   */
+  final int offset;
+
+  /**
+   * The number of characters in the original contents that were replaced
+   */
+  final int oldLength;
+
+  /**
+   * The number of characters in the replacement text.
+   */
+  final int newLength;
+
+  /**
+   * Initialize a newly created change object to represent a change to the content of a source.
+   *
+   * @param contents the new contents of the source
+   * @param offset the offset into the current contents
+   * @param oldLength the number of characters in the original contents that were replaced
+   * @param newLength the number of characters in the replacement text
+   */
+  ChangeSet_ContentChange(this.contents, this.offset, this.oldLength, this.newLength);
 }
 
 /**
@@ -1139,6 +1253,148 @@
  */
 class AnalysisCache {
   /**
+   * An array containing the partitions of which this cache is comprised.
+   */
+  final List<CachePartition> _partitions;
+
+  /**
+   * Initialize a newly created cache to have the given partitions. The partitions will be searched
+   * in the order in which they appear in the array, so the most specific partition (usually an
+   * [SdkCachePartition]) should be first and the most general (usually a
+   * [UniversalCachePartition]) last.
+   *
+   * @param partitions the partitions for the newly created cache
+   */
+  AnalysisCache(this._partitions);
+
+  /**
+   * Record that the AST associated with the given source was just read from the cache.
+   *
+   * @param source the source whose AST was accessed
+   */
+  void accessedAst(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].accessedAst(source);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Return the entry associated with the given source.
+   *
+   * @param source the source whose entry is to be returned
+   * @return the entry associated with the given source
+   */
+  SourceEntry get(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        return _partitions[i].get(source);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Return an iterator returning all of the map entries mapping sources to cache entries.
+   *
+   * @return an iterator returning all of the map entries mapping sources to cache entries
+   */
+  MapIterator<Source, SourceEntry> iterator() {
+    int count = _partitions.length;
+    List<Map<Source, SourceEntry>> maps = new List<Map>(count);
+    for (int i = 0; i < count; i++) {
+      maps[i] = _partitions[i].map;
+    }
+    return new MultipleMapIterator<Source, SourceEntry>(maps);
+  }
+
+  /**
+   * Associate the given entry with the given source.
+   *
+   * @param source the source with which the entry is to be associated
+   * @param entry the entry to be associated with the source
+   */
+  void put(Source source, SourceEntry entry) {
+    (entry as SourceEntryImpl).fixExceptionState();
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].put(source, entry);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Remove all information related to the given source from this cache.
+   *
+   * @param source the source to be removed
+   */
+  void remove(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].remove(source);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Record that the AST associated with the given source was just removed from the cache.
+   *
+   * @param source the source whose AST was removed
+   */
+  void removedAst(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].removedAst(source);
+        return;
+      }
+    }
+  }
+
+  /**
+   * Return the number of sources that are mapped to cache entries.
+   *
+   * @return the number of sources that are mapped to cache entries
+   */
+  int size() {
+    int size = 0;
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      size += _partitions[i].size();
+    }
+    return size;
+  }
+
+  /**
+   * Record that the AST associated with the given source was just stored to the cache.
+   *
+   * @param source the source whose AST was stored
+   */
+  void storedAst(Source source) {
+    int count = _partitions.length;
+    for (int i = 0; i < count; i++) {
+      if (_partitions[i].contains(source)) {
+        _partitions[i].storedAst(source);
+        return;
+      }
+    }
+  }
+}
+
+/**
+ * Instances of the class `CachePartition` implement a single partition in an LRU cache of
+ * information related to analysis.
+ */
+abstract class CachePartition {
+  /**
    * A table mapping the sources known to the context to the information known about the source.
    */
   Map<Source, SourceEntry> _sourceMap = new Map<Source, SourceEntry>();
@@ -1169,7 +1425,7 @@
    * @param retentionPolicy the policy used to determine which pieces of data to remove from the
    *          cache
    */
-  AnalysisCache(int maxCacheSize, this._retentionPolicy) {
+  CachePartition(int maxCacheSize, this._retentionPolicy) {
     this._maxCacheSize = maxCacheSize;
     _recentlyUsed = new List<Source>();
   }
@@ -1193,6 +1449,14 @@
   }
 
   /**
+   * Return `true` if the given source is contained in this partition.
+   *
+   * @param source the source being tested
+   * @return `true` if the source is contained in this partition
+   */
+  bool contains(Source source);
+
+  /**
    * Return the entry associated with the given source.
    *
    * @param source the source whose entry is to be returned
@@ -1201,6 +1465,18 @@
   SourceEntry get(Source source) => _sourceMap[source];
 
   /**
+   * Return a table mapping the sources known to the context to the information known about the
+   * source.
+   *
+   * <b>Note:</b> This method is only visible for use by [AnalysisCache] and should not be
+   * used for any other purpose.
+   *
+   * @return a table mapping the sources known to the context to the information known about the
+   *         source
+   */
+  Map<Source, SourceEntry> get map => _sourceMap;
+
+  /**
    * Return an iterator returning all of the map entries mapping sources to cache entries.
    *
    * @return an iterator returning all of the map entries mapping sources to cache entries
@@ -1303,11 +1579,6 @@
    * flushed from the cache. The source that will be returned will be the source that has been
    * unreferenced for the longest period of time but that is not a priority for analysis.
    *
-   * It is possible for there to be no AST that can be flushed, in which case `null` will be
-   * returned. This happens, for example, if the context is reserving the AST's needed to resolve a
-   * cycle of libraries and the number of AST's being reserved is larger than the current cache
-   * size.
-   *
    * @return the source that was removed
    */
   Source _removeAstToFlush() {
@@ -1322,6 +1593,7 @@
       }
     }
     if (sourceToRemove < 0) {
+      AnalysisEngine.instance.logger.logError2("Internal error: Could not flush data from the cache", new JavaException());
       return null;
     }
     return _recentlyUsed.removeAt(sourceToRemove);
@@ -2823,6 +3095,38 @@
 }
 
 /**
+ * Instances of the class `DefaultRetentionPolicy` implement a retention policy that will keep
+ * AST's in the cache if there is analysis information that needs to be computed for a source, where
+ * the computation is dependent on having the AST.
+ */
+class DefaultRetentionPolicy implements CacheRetentionPolicy {
+  /**
+   * An instance of this class that can be shared.
+   */
+  static DefaultRetentionPolicy POLICY = new DefaultRetentionPolicy();
+
+  @override
+  RetentionPriority getAstPriority(Source source, SourceEntry sourceEntry) {
+    if (sourceEntry is DartEntry) {
+      DartEntry dartEntry = sourceEntry;
+      if (astIsNeeded(dartEntry)) {
+        return RetentionPriority.MEDIUM;
+      }
+    }
+    return RetentionPriority.LOW;
+  }
+
+  /**
+   * Return `true` if there is analysis information in the given entry that needs to be
+   * computed, where the computation is dependent on having the AST.
+   *
+   * @param dartEntry the entry being tested
+   * @return `true` if there is analysis information that needs to be computed from the AST
+   */
+  bool astIsNeeded(DartEntry dartEntry) => dartEntry.hasInvalidData(DartEntry.HINTS) || dartEntry.hasInvalidData(DartEntry.VERIFICATION_ERRORS) || dartEntry.hasInvalidData(DartEntry.RESOLUTION_ERRORS);
+}
+
+/**
  * The interface `HtmlEntry` defines the behavior of objects that maintain the information
  * cached by an analysis context about an individual HTML file.
  */
@@ -3426,6 +3730,37 @@
 }
 
 /**
+ * Instances of the class `PartitionManager` manage the partitions that can be shared between
+ * analysis contexts.
+ */
+class PartitionManager {
+  /**
+   * A table mapping SDK's to the partitions used for those SDK's.
+   */
+  Map<DartSdk, SdkCachePartition> _sdkPartitions = new Map<DartSdk, SdkCachePartition>();
+
+  /**
+   * The default cache size for a Dart SDK partition.
+   */
+  static int _DEFAULT_SDK_CACHE_SIZE = 256;
+
+  /**
+   * Return the partition being used for the given SDK, creating the partition if necessary.
+   *
+   * @param sdk the SDK for which a partition is being requested
+   * @return the partition being used for the given SDK
+   */
+  SdkCachePartition forSdk(DartSdk sdk) {
+    SdkCachePartition partition = _sdkPartitions[sdk];
+    if (partition == null) {
+      partition = new SdkCachePartition(_DEFAULT_SDK_CACHE_SIZE);
+      _sdkPartitions[sdk] = partition;
+    }
+    return partition;
+  }
+}
+
+/**
  * The enumerated type `RetentionPriority` represents the priority of data in the cache in
  * terms of the desirability of retaining some specified data about a specified source.
  */
@@ -3455,6 +3790,23 @@
 }
 
 /**
+ * Instances of the class `SdkCachePartition` implement a cache partition that contains all of
+ * the sources in the SDK.
+ */
+class SdkCachePartition extends CachePartition {
+  /**
+   * Initialize a newly created partition.
+   *
+   * @param maxCacheSize the maximum number of sources for which AST structures should be kept in
+   *          the cache
+   */
+  SdkCachePartition(int maxCacheSize) : super(maxCacheSize, DefaultRetentionPolicy.POLICY);
+
+  @override
+  bool contains(Source source) => source.isInSystemLibrary;
+}
+
+/**
  * The interface `SourceEntry` defines the behavior of objects that maintain the information
  * cached by an analysis context about an individual source, no matter what kind of source it is.
  *
@@ -3633,7 +3985,7 @@
    */
   void invalidateAllInformation() {
     _content = null;
-    _contentState = CacheState.INVALID;
+    _contentState = _checkContentState(CacheState.INVALID);
     _lineInfo = null;
     _lineInfoState = CacheState.INVALID;
   }
@@ -3676,7 +4028,7 @@
   void setState(DataDescriptor descriptor, CacheState state) {
     if (identical(descriptor, SourceEntry.CONTENT)) {
       _content = updatedValue(state, _content, null);
-      _contentState = state;
+      _contentState = _checkContentState(state);
     } else if (identical(descriptor, SourceEntry.LINE_INFO)) {
       _lineInfo = updatedValue(state, _lineInfo, null);
       _lineInfoState = state;
@@ -3694,7 +4046,7 @@
   void setValue(DataDescriptor descriptor, Object value) {
     if (identical(descriptor, SourceEntry.CONTENT)) {
       _content = value as String;
-      _contentState = CacheState.VALID;
+      _contentState = _checkContentState(CacheState.VALID);
     } else if (identical(descriptor, SourceEntry.LINE_INFO)) {
       _lineInfo = value as LineInfo;
       _lineInfoState = CacheState.VALID;
@@ -3796,6 +4148,43 @@
     builder.append("; lineInfo = ");
     builder.append(_lineInfoState);
   }
+
+  /**
+   * If the state is changing from ERROR to anything else, capture the information. This is an
+   * attempt to discover the underlying cause of a long-standing bug.
+   *
+   * @param newState the new state of the content
+   * @return the new state of the content
+   */
+  CacheState _checkContentState(CacheState newState) {
+    if (_contentState == CacheState.ERROR) {
+      InstrumentationBuilder builder = Instrumentation.builder2("SourceEntryImpl-checkContentState");
+      builder.data3("message", "contentState changing from ${_contentState} to ${newState}");
+      //builder.data("source", source.getFullName());
+      builder.record(new AnalysisException());
+      builder.log();
+    }
+    return newState;
+  }
+}
+
+/**
+ * Instances of the class `UniversalCachePartition` implement a cache partition that contains
+ * all sources not contained in other partitions.
+ */
+class UniversalCachePartition extends CachePartition {
+  /**
+   * Initialize a newly created partition.
+   *
+   * @param maxCacheSize the maximum number of sources for which AST structures should be kept in
+   *          the cache
+   * @param retentionPolicy the policy used to determine which pieces of data to remove from the
+   *          cache
+   */
+  UniversalCachePartition(int maxCacheSize, CacheRetentionPolicy retentionPolicy) : super(maxCacheSize, retentionPolicy);
+
+  @override
+  bool contains(Source source) => true;
 }
 
 /**
@@ -3954,6 +4343,11 @@
   Source _coreLibrarySource;
 
   /**
+   * The partition that contains analysis results that are not shared with other contexts.
+   */
+  CachePartition _privatePartition;
+
+  /**
    * A table mapping the sources known to the context to the information known about the source.
    */
   AnalysisCache _cache;
@@ -3989,7 +4383,7 @@
    * results are for the same version (modification time) of the source as our current cache
    * content.
    */
-  Object _cacheLock = new Object();
+  static Object _cacheLock = new Object();
 
   /**
    * The object used to record the results of performing an analysis task.
@@ -4017,7 +4411,8 @@
    */
   AnalysisContextImpl() : super() {
     _resultRecorder = new AnalysisContextImpl_AnalysisTaskResultRecorder(this);
-    _cache = new AnalysisCache(AnalysisOptionsImpl.DEFAULT_CACHE_SIZE, new AnalysisContextImpl_ContextRetentionPolicy(this));
+    _privatePartition = new UniversalCachePartition(AnalysisOptionsImpl.DEFAULT_CACHE_SIZE, new AnalysisContextImpl_ContextRetentionPolicy(this));
+    _cache = _createCacheFromSourceFactory(null);
   }
 
   @override
@@ -4052,6 +4447,16 @@
     for (Source source in changeSet.changedSources) {
       _sourceChanged(source);
     }
+    for (MapEntry<Source, String> entry in getMapEntrySet(changeSet.changedContents)) {
+      setContents(entry.getKey(), entry.getValue());
+    }
+    for (MapEntry<Source, ChangeSet_ContentChange> entry in getMapEntrySet(changeSet.changedRanges)) {
+      ChangeSet_ContentChange change = entry.getValue();
+      setChangedContents(entry.getKey(), change.contents, change.offset, change.oldLength, change.newLength);
+    }
+    for (Source source in changeSet.deletedSources) {
+      _sourceDeleted(source);
+    }
     for (Source source in removedSources) {
       _sourceRemoved(source);
     }
@@ -4751,9 +5156,9 @@
   @override
   void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
     Source htmlSource = _sourceFactory.forUri(DartSdk.DART_HTML);
-    for (MapIterator<Source, LibraryElement> iter = SingleMapIterator.forMap(elementMap); iter.moveNext();) {
-      Source librarySource = iter.key;
-      LibraryElement library = iter.value;
+    for (MapEntry<Source, LibraryElement> entry in getMapEntrySet(elementMap)) {
+      Source librarySource = entry.getKey();
+      LibraryElement library = entry.getValue();
       //
       // Cache the element in the library's info.
       //
@@ -4761,6 +5166,15 @@
       if (dartEntry != null) {
         DartEntryImpl dartCopy = dartEntry.writableCopy;
         _recordElementData(dartCopy, library, library.source, htmlSource);
+        dartCopy.setValue(DartEntry.SCAN_ERRORS, AnalysisError.NO_ERRORS);
+        dartCopy.setValue(DartEntry.PARSE_ERRORS, AnalysisError.NO_ERRORS);
+        dartCopy.setState(DartEntry.PARSED_UNIT, CacheState.FLUSHED);
+        dartCopy.setValueInLibrary(DartEntry.BUILD_ELEMENT_ERRORS, librarySource, AnalysisError.NO_ERRORS);
+        dartCopy.setValueInLibrary(DartEntry.RESOLUTION_ERRORS, librarySource, AnalysisError.NO_ERRORS);
+        dartCopy.setStateInLibrary(DartEntry.RESOLVED_UNIT, librarySource, CacheState.FLUSHED);
+        dartCopy.setValueInLibrary(DartEntry.VERIFICATION_ERRORS, librarySource, AnalysisError.NO_ERRORS);
+        dartCopy.setValue(DartEntry.ANGULAR_ERRORS, AnalysisError.NO_ERRORS);
+        dartCopy.setValueInLibrary(DartEntry.HINTS, librarySource, AnalysisError.NO_ERRORS);
         _cache.put(librarySource, dartCopy);
       }
     }
@@ -4789,7 +5203,8 @@
     int cacheSize = options.cacheSize;
     if (this._options.cacheSize != cacheSize) {
       this._options.cacheSize = cacheSize;
-      _cache.maxCacheSize = cacheSize;
+      //cache.setMaxCacheSize(cacheSize);
+      _privatePartition.maxCacheSize = cacheSize;
       //
       // Cap the size of the priority list to being less than the cache size. Failure to do so can
       // result in an infinite loop in performAnalysisTask() because re-caching one AST structure
@@ -4810,7 +5225,7 @@
     this._options.preserveComments = options.preserveComments;
     _generateSdkErrors = options.generateSdkErrors;
     if (needsRecompute) {
-      _invalidateAllResolutionInformation();
+      _invalidateAllLocalResolutionInformation();
     }
   }
 
@@ -4896,7 +5311,8 @@
     factory.context = this;
     _sourceFactory = factory;
     _coreLibrarySource = _sourceFactory.forUri(DartSdk.DART_CORE);
-    _invalidateAllResolutionInformation();
+    _cache = _createCacheFromSourceFactory(factory);
+    _invalidateAllLocalResolutionInformation();
   }
 
   /**
@@ -5419,7 +5835,11 @@
       // change, this loop will eventually terminate.
       //
       LibraryElement library = computeLibraryElement(librarySource);
-      dartEntry = new GenerateDartErrorsTask(this, unitSource, dartEntry.modificationTime, resolveCompilationUnit(unitSource, library), library).perform(_resultRecorder) as DartEntry;
+      CompilationUnit unit = resolveCompilationUnit(unitSource, library);
+      if (unit == null) {
+        throw new AnalysisException.con1("Could not resolve compilation unit ${unitSource.fullName} in ${librarySource.fullName}");
+      }
+      dartEntry = new GenerateDartErrorsTask(this, unitSource, dartEntry.modificationTime, unit, library).perform(_resultRecorder) as DartEntry;
       state = dartEntry.getStateInLibrary(descriptor, librarySource);
     }
     return dartEntry;
@@ -5544,6 +5964,25 @@
   }
 
   /**
+   * Create an analysis cache based on the given source factory.
+   *
+   * @param factory the source factory containing the information needed to create the cache
+   * @return the cache that was created
+   */
+  AnalysisCache _createCacheFromSourceFactory(SourceFactory factory) {
+    if (factory == null) {
+      return new AnalysisCache(<CachePartition> [_privatePartition]);
+    }
+    DartSdk sdk = factory.dartSdk;
+    if (sdk == null) {
+      return new AnalysisCache(<CachePartition> [_privatePartition]);
+    }
+    return new AnalysisCache(<CachePartition> [
+        AnalysisEngine.instance.partitionManager.forSdk(sdk),
+        _privatePartition]);
+  }
+
+  /**
    * Create a [GenerateDartErrorsTask] for the given source, marking the verification errors
    * as being in-process. The compilation unit and the library can be the same if the compilation
    * unit is the defining compilation unit of the library.
@@ -5559,6 +5998,13 @@
       return _createResolveDartLibraryTask(librarySource, libraryEntry);
     }
     CompilationUnit unit = unitEntry.getValueInLibrary(DartEntry.RESOLVED_UNIT, librarySource);
+    if (unit == null) {
+      AnalysisEngine.instance.logger.logInformation2("Entry has VALID state for RESOLVED_UNIT but null value for ${unitSource.fullName} in ${librarySource.fullName}", new AnalysisException());
+      DartEntryImpl dartCopy = unitEntry.writableCopy;
+      dartCopy.recordResolutionError();
+      _cache.put(unitSource, dartCopy);
+      return new AnalysisContextImpl_TaskData(null, false);
+    }
     LibraryElement libraryElement = libraryEntry.getValue(DartEntry.ELEMENT);
     DartEntryImpl dartCopy = unitEntry.writableCopy;
     dartCopy.setStateInLibrary(DartEntry.VERIFICATION_ERRORS, librarySource, CacheState.IN_PROCESS);
@@ -6592,9 +7038,9 @@
    *
    * <b>Note:</b> This method must only be invoked while we are synchronized on [cacheLock].
    */
-  void _invalidateAllResolutionInformation() {
+  void _invalidateAllLocalResolutionInformation() {
     Map<Source, List<Source>> oldPartMap = new Map<Source, List<Source>>();
-    MapIterator<Source, SourceEntry> iterator = _cache.iterator();
+    MapIterator<Source, SourceEntry> iterator = _privatePartition.iterator();
     while (iterator.moveNext()) {
       Source source = iterator.key;
       SourceEntry sourceEntry = iterator.value;
@@ -7027,9 +7473,9 @@
       _cache.put(librarySource, dartCopy);
       throw thrownException;
     }
-    for (MapIterator<Source, TimestampedData<List<AnalysisError>>> iter = SingleMapIterator.forMap(hintMap); iter.moveNext();) {
-      Source unitSource = iter.key;
-      TimestampedData<List<AnalysisError>> results = iter.value;
+    for (MapEntry<Source, TimestampedData<List<AnalysisError>>> entry in getMapEntrySet(hintMap)) {
+      Source unitSource = entry.getKey();
+      TimestampedData<List<AnalysisError>> results = entry.getValue();
       SourceEntry sourceEntry = _cache.get(unitSource);
       if (sourceEntry is! DartEntry) {
         // This shouldn't be possible because we should never have performed the task if the source
@@ -7943,9 +8389,9 @@
    * @param oldPartMap the table containing the parts associated with each library
    */
   void _removeFromPartsUsingMap(Map<Source, List<Source>> oldPartMap) {
-    for (MapIterator<Source, List<Source>> iter = SingleMapIterator.forMap(oldPartMap); iter.moveNext();) {
-      Source librarySource = iter.key;
-      List<Source> oldParts = iter.value;
+    for (MapEntry<Source, List<Source>> entry in getMapEntrySet(oldPartMap)) {
+      Source librarySource = entry.getKey();
+      List<Source> oldParts = entry.getValue();
       for (int i = 0; i < oldParts.length; i++) {
         Source partSource = oldParts[i];
         if (partSource != librarySource) {
@@ -8059,6 +8505,37 @@
    *
    * @param source the source that has been deleted
    */
+  void _sourceDeleted(Source source) {
+    SourceEntry sourceEntry = _cache.get(source);
+    if (sourceEntry is HtmlEntry) {
+      HtmlEntryImpl htmlCopy = sourceEntry.writableCopy;
+      _invalidateAngularResolution(htmlCopy);
+      htmlCopy.recordContentError();
+      _cache.put(source, htmlCopy);
+    } else if (sourceEntry is DartEntry) {
+      Set<Source> libraries = new Set<Source>();
+      for (Source librarySource in getLibrariesContaining(source)) {
+        libraries.add(librarySource);
+        for (Source dependentLibrary in getLibrariesDependingOn(librarySource)) {
+          libraries.add(dependentLibrary);
+        }
+      }
+      for (Source librarySource in libraries) {
+        _invalidateLibraryResolution(librarySource);
+      }
+      DartEntryImpl dartCopy = sourceEntry.writableCopy;
+      dartCopy.recordContentError();
+      _cache.put(source, dartCopy);
+    }
+    _workManager.remove(source);
+    _removeFromPriorityOrder(source);
+  }
+
+  /**
+   * <b>Note:</b> This method must only be invoked while we are synchronized on [cacheLock].
+   *
+   * @param source the source that has been removed
+   */
   void _sourceRemoved(Source source) {
     SourceEntry sourceEntry = _cache.get(source);
     if (sourceEntry is HtmlEntry) {
@@ -8684,7 +9161,8 @@
    * @param dartEntry the entry associated with the source
    */
   void _ensureResolvableCompilationUnit(Source source, DartEntry dartEntry) {
-    if (!dartEntry.hasResolvableCompilationUnit) {
+    // The entry will be null if the source represents a non-Dart file.
+    if (dartEntry != null && !dartEntry.hasResolvableCompilationUnit) {
       if (_taskData == null) {
         _taskData = AnalysisContextImpl_this._createParseDartTask(source, dartEntry);
       }
@@ -8964,319 +9442,6 @@
 }
 
 /**
- * Instances of the class `DelegatingAnalysisContextImpl` extend [AnalysisContextImpl
- ] to delegate sources to the appropriate analysis context. For instance, if the
- * source is in a system library then the analysis context from the [DartSdk] is used.
- */
-class DelegatingAnalysisContextImpl extends AnalysisContextImpl {
-  /**
-   * This references the [InternalAnalysisContext] held onto by the [DartSdk] which is
-   * used (instead of this [AnalysisContext]) for SDK sources. This field is set when
-   * #setSourceFactory(SourceFactory) is called, and references the analysis context in the
-   * [DartUriResolver] in the [SourceFactory], this analysis context assumes that there
-   * will be such a resolver.
-   */
-  InternalAnalysisContext _sdkAnalysisContext;
-
-  @override
-  void addSourceInfo(Source source, SourceEntry info) {
-    if (source.isInSystemLibrary) {
-      _sdkAnalysisContext.addSourceInfo(source, info);
-    } else {
-      super.addSourceInfo(source, info);
-    }
-  }
-
-  @override
-  List<AnalysisError> computeErrors(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeErrors(source);
-    } else {
-      return super.computeErrors(source);
-    }
-  }
-
-  @override
-  List<Source> computeExportedLibraries(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeExportedLibraries(source);
-    } else {
-      return super.computeExportedLibraries(source);
-    }
-  }
-
-  @override
-  HtmlElement computeHtmlElement(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeHtmlElement(source);
-    } else {
-      return super.computeHtmlElement(source);
-    }
-  }
-
-  @override
-  List<Source> computeImportedLibraries(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeImportedLibraries(source);
-    } else {
-      return super.computeImportedLibraries(source);
-    }
-  }
-
-  @override
-  SourceKind computeKindOf(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeKindOf(source);
-    } else {
-      return super.computeKindOf(source);
-    }
-  }
-
-  @override
-  LibraryElement computeLibraryElement(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeLibraryElement(source);
-    } else {
-      return super.computeLibraryElement(source);
-    }
-  }
-
-  @override
-  LineInfo computeLineInfo(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeLineInfo(source);
-    } else {
-      return super.computeLineInfo(source);
-    }
-  }
-
-  @override
-  ResolvableCompilationUnit computeResolvableCompilationUnit(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.computeResolvableCompilationUnit(source);
-    } else {
-      return super.computeResolvableCompilationUnit(source);
-    }
-  }
-
-  @override
-  AnalysisErrorInfo getErrors(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getErrors(source);
-    } else {
-      return super.getErrors(source);
-    }
-  }
-
-  @override
-  HtmlElement getHtmlElement(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getHtmlElement(source);
-    } else {
-      return super.getHtmlElement(source);
-    }
-  }
-
-  @override
-  List<Source> getHtmlFilesReferencing(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getHtmlFilesReferencing(source);
-    } else {
-      return super.getHtmlFilesReferencing(source);
-    }
-  }
-
-  @override
-  SourceKind getKindOf(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getKindOf(source);
-    } else {
-      return super.getKindOf(source);
-    }
-  }
-
-  @override
-  List<Source> getLibrariesContaining(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getLibrariesContaining(source);
-    } else {
-      return super.getLibrariesContaining(source);
-    }
-  }
-
-  @override
-  List<Source> getLibrariesDependingOn(Source librarySource) {
-    if (librarySource.isInSystemLibrary) {
-      return _sdkAnalysisContext.getLibrariesDependingOn(librarySource);
-    } else {
-      return super.getLibrariesDependingOn(librarySource);
-    }
-  }
-
-  @override
-  LibraryElement getLibraryElement(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getLibraryElement(source);
-    } else {
-      return super.getLibraryElement(source);
-    }
-  }
-
-  @override
-  List<Source> get librarySources => ArrayUtils.addAll(super.librarySources, _sdkAnalysisContext.librarySources);
-
-  @override
-  LineInfo getLineInfo(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getLineInfo(source);
-    } else {
-      return super.getLineInfo(source);
-    }
-  }
-
-  @override
-  Namespace getPublicNamespace(LibraryElement library) {
-    Source source = library.source;
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.getPublicNamespace(library);
-    } else {
-      return super.getPublicNamespace(library);
-    }
-  }
-
-  @override
-  CompilationUnit getResolvedCompilationUnit(Source unitSource, LibraryElement library) {
-    if (unitSource.isInSystemLibrary) {
-      return _sdkAnalysisContext.getResolvedCompilationUnit(unitSource, library);
-    } else {
-      return super.getResolvedCompilationUnit(unitSource, library);
-    }
-  }
-
-  @override
-  CompilationUnit getResolvedCompilationUnit2(Source unitSource, Source librarySource) {
-    if (unitSource.isInSystemLibrary) {
-      return _sdkAnalysisContext.getResolvedCompilationUnit2(unitSource, librarySource);
-    } else {
-      return super.getResolvedCompilationUnit2(unitSource, librarySource);
-    }
-  }
-
-  @override
-  bool isClientLibrary(Source librarySource) {
-    if (librarySource.isInSystemLibrary) {
-      return _sdkAnalysisContext.isClientLibrary(librarySource);
-    } else {
-      return super.isClientLibrary(librarySource);
-    }
-  }
-
-  @override
-  bool isServerLibrary(Source librarySource) {
-    if (librarySource.isInSystemLibrary) {
-      return _sdkAnalysisContext.isServerLibrary(librarySource);
-    } else {
-      return super.isServerLibrary(librarySource);
-    }
-  }
-
-  @override
-  CompilationUnit parseCompilationUnit(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.parseCompilationUnit(source);
-    } else {
-      return super.parseCompilationUnit(source);
-    }
-  }
-
-  @override
-  ht.HtmlUnit parseHtmlUnit(Source source) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.parseHtmlUnit(source);
-    } else {
-      return super.parseHtmlUnit(source);
-    }
-  }
-
-  @override
-  void recordLibraryElements(Map<Source, LibraryElement> elementMap) {
-    if (elementMap.isEmpty) {
-      return;
-    }
-    // TODO(jwren) we are making the assumption here that the elementMap will have sources from only
-    // one library, while this is true with our use of the Analysis Engine, it is not required by
-    // the API, revisit to fix cases where the elementMap can have sources both in the sdk and other
-    // libraries
-    Source source = new JavaIterator(elementMap.keys.toSet()).next();
-    if (source.isInSystemLibrary) {
-      _sdkAnalysisContext.recordLibraryElements(elementMap);
-    } else {
-      super.recordLibraryElements(elementMap);
-    }
-  }
-
-  @override
-  CompilationUnit resolveCompilationUnit(Source source, LibraryElement library) {
-    if (source.isInSystemLibrary) {
-      return _sdkAnalysisContext.resolveCompilationUnit(source, library);
-    } else {
-      return super.resolveCompilationUnit(source, library);
-    }
-  }
-
-  @override
-  CompilationUnit resolveCompilationUnit2(Source unitSource, Source librarySource) {
-    if (unitSource.isInSystemLibrary) {
-      return _sdkAnalysisContext.resolveCompilationUnit2(unitSource, librarySource);
-    } else {
-      return super.resolveCompilationUnit2(unitSource, librarySource);
-    }
-  }
-
-  @override
-  ht.HtmlUnit resolveHtmlUnit(Source unitSource) {
-    if (unitSource.isInSystemLibrary) {
-      return _sdkAnalysisContext.resolveHtmlUnit(unitSource);
-    } else {
-      return super.resolveHtmlUnit(unitSource);
-    }
-  }
-
-  @override
-  void setChangedContents(Source source, String contents, int offset, int oldLength, int newLength) {
-    if (source.isInSystemLibrary) {
-      _sdkAnalysisContext.setChangedContents(source, contents, offset, oldLength, newLength);
-    } else {
-      super.setChangedContents(source, contents, offset, oldLength, newLength);
-    }
-  }
-
-  @override
-  void setContents(Source source, String contents) {
-    if (source.isInSystemLibrary) {
-      _sdkAnalysisContext.setContents(source, contents);
-    } else {
-      super.setContents(source, contents);
-    }
-  }
-
-  @override
-  void set sourceFactory(SourceFactory factory) {
-    super.sourceFactory = factory;
-    DartSdk sdk = factory.dartSdk;
-    if (sdk != null) {
-      _sdkAnalysisContext = sdk.context as InternalAnalysisContext;
-      if (_sdkAnalysisContext is DelegatingAnalysisContextImpl) {
-        _sdkAnalysisContext = null;
-        throw new IllegalStateException("The context provided by an SDK cannot itself be a delegating analysis context");
-      }
-    } else {
-      throw new IllegalStateException("SourceFactorys provided to DelegatingAnalysisContextImpls must have a DartSdk associated with the provided SourceFactory.");
-    }
-  }
-}
-
-/**
  * Instances of the class `IncrementalAnalysisCache` hold information used to perform
  * incremental analysis.
  *
@@ -9382,7 +9547,7 @@
    */
   static IncrementalAnalysisCache verifyStructure(IncrementalAnalysisCache cache, Source source, CompilationUnit unit) {
     if (cache != null && unit != null && cache.source == source) {
-      if (!AstComparator.equalUnits(cache.resolvedUnit, unit)) {
+      if (!AstComparator.equalNodes(cache.resolvedUnit, unit)) {
         return null;
       }
     }
@@ -9487,7 +9652,7 @@
    * Create a new [InstrumentedAnalysisContextImpl] which wraps a new
    * [AnalysisContextImpl] as the basis context.
    */
-  InstrumentedAnalysisContextImpl() : this.con1(new DelegatingAnalysisContextImpl());
+  InstrumentedAnalysisContextImpl() : this.con1(new AnalysisContextImpl());
 
   /**
    * Create a new [InstrumentedAnalysisContextImpl] with a specified basis context, aka the
@@ -10360,14 +10525,14 @@
    * @return an array of errors (not `null`, contains no `null`s)
    */
   List<AnalysisError> get errors {
-    Iterable<Set<AnalysisError>> errorsSets = _errors.values;
-    int numEntries = errorsSets.length;
+    Iterable<MapEntry<Source, Set<AnalysisError>>> entrySet = getMapEntrySet(_errors);
+    int numEntries = entrySet.length;
     if (numEntries == 0) {
       return AnalysisError.NO_ERRORS;
     }
     List<AnalysisError> resultList = new List<AnalysisError>();
-    for (Set<AnalysisError> errorsSet in errorsSets) {
-      resultList.addAll(errorsSet);
+    for (MapEntry<Source, Set<AnalysisError>> entry in entrySet) {
+      resultList.addAll(entry.getValue());
     }
     return new List.from(resultList);
   }
@@ -11274,8 +11439,13 @@
       Token filterToken = tokens[i];
       Token barToken = filterToken;
       filterToken = filterToken.next;
-      // TODO(scheglov) report missing identifier
-      SimpleIdentifier name = _parseDartExpressionInToken(filterToken) as SimpleIdentifier;
+      // parse name
+      Expression nameExpression = _parseDartExpressionInToken(filterToken);
+      if (nameExpression is! SimpleIdentifier) {
+        _reportErrorForNode(AngularCode.INVALID_FILTER_NAME, nameExpression, []);
+        continue;
+      }
+      SimpleIdentifier name = nameExpression as SimpleIdentifier;
       filterToken = name.endToken.next;
       // parse arguments
       List<AngularFilterArgument> arguments = [];
@@ -11570,10 +11740,6 @@
       }
       _libraryElement.imports = new List.from(imports);
     }
-    // push conditional errors
-    for (ProxyConditionalAnalysisError conditionalCode in _resolver.proxyConditionalAnalysisErrors) {
-      _resolver.reportError(conditionalCode.analysisError);
-    }
   }
 
   void _resolveXmlExpression(AngularXmlExpression angularXmlExpression) {
@@ -14279,12 +14445,6 @@
     InheritanceManager inheritanceManager = new InheritanceManager(_libraryElement);
     ResolverVisitor resolverVisitor = new ResolverVisitor.con2(_libraryElement, source, typeProvider, inheritanceManager, errorListener);
     unit.accept(resolverVisitor);
-    // TODO (jwren) Move this logic/ loop into the ResolverVisitor and then make the reportError protected again.
-    for (ProxyConditionalAnalysisError conditionalCode in resolverVisitor.proxyConditionalAnalysisErrors) {
-      if (conditionalCode.shouldIncludeErrorCode()) {
-        resolverVisitor.reportError(conditionalCode.analysisError);
-      }
-    }
     //
     // Perform additional error checking.
     //
diff --git a/pkg/analyzer/lib/src/generated/error.dart b/pkg/analyzer/lib/src/generated/error.dart
index e944834..e71e593 100644
--- a/pkg/analyzer/lib/src/generated/error.dart
+++ b/pkg/analyzer/lib/src/generated/error.dart
@@ -192,34 +192,37 @@
 class AngularCode extends Enum<AngularCode> implements ErrorCode {
   static const AngularCode CANNOT_PARSE_SELECTOR = const AngularCode('CANNOT_PARSE_SELECTOR', 0, "The selector '%s' cannot be parsed");
 
-  static const AngularCode INVALID_PROPERTY_KIND = const AngularCode('INVALID_PROPERTY_KIND', 1, "Unknown property binding kind '%s', use one of the '@', '=>', '=>!' or '<=>'");
+  static const AngularCode INVALID_FILTER_NAME = const AngularCode('INVALID_FILTER_NAME', 1, "Filter name must be a simple identifier");
 
-  static const AngularCode INVALID_PROPERTY_FIELD = const AngularCode('INVALID_PROPERTY_FIELD', 2, "Unknown property field '%s'");
+  static const AngularCode INVALID_PROPERTY_KIND = const AngularCode('INVALID_PROPERTY_KIND', 2, "Unknown property binding kind '%s', use one of the '@', '=>', '=>!' or '<=>'");
 
-  static const AngularCode INVALID_PROPERTY_MAP = const AngularCode('INVALID_PROPERTY_MAP', 3, "Argument 'map' must be a constant map literal");
+  static const AngularCode INVALID_PROPERTY_FIELD = const AngularCode('INVALID_PROPERTY_FIELD', 3, "Unknown property field '%s'");
 
-  static const AngularCode INVALID_PROPERTY_NAME = const AngularCode('INVALID_PROPERTY_NAME', 4, "Property name must be a string literal");
+  static const AngularCode INVALID_PROPERTY_MAP = const AngularCode('INVALID_PROPERTY_MAP', 4, "Argument 'map' must be a constant map literal");
 
-  static const AngularCode INVALID_PROPERTY_SPEC = const AngularCode('INVALID_PROPERTY_SPEC', 5, "Property binding specification must be a string literal");
+  static const AngularCode INVALID_PROPERTY_NAME = const AngularCode('INVALID_PROPERTY_NAME', 5, "Property name must be a string literal");
 
-  static const AngularCode INVALID_REPEAT_SYNTAX = const AngularCode('INVALID_REPEAT_SYNTAX', 6, "Expected statement in form '_item_ in _collection_ [tracked by _id_]'");
+  static const AngularCode INVALID_PROPERTY_SPEC = const AngularCode('INVALID_PROPERTY_SPEC', 6, "Property binding specification must be a string literal");
 
-  static const AngularCode INVALID_REPEAT_ITEM_SYNTAX = const AngularCode('INVALID_REPEAT_ITEM_SYNTAX', 7, "Item must by identifier or in '(_key_, _value_)' pair.");
+  static const AngularCode INVALID_REPEAT_SYNTAX = const AngularCode('INVALID_REPEAT_SYNTAX', 7, "Expected statement in form '_item_ in _collection_ [tracked by _id_]'");
 
-  static const AngularCode INVALID_URI = const AngularCode('INVALID_URI', 8, "Invalid URI syntax: '%s'");
+  static const AngularCode INVALID_REPEAT_ITEM_SYNTAX = const AngularCode('INVALID_REPEAT_ITEM_SYNTAX', 8, "Item must by identifier or in '(_key_, _value_)' pair.");
 
-  static const AngularCode MISSING_FILTER_COLON = const AngularCode('MISSING_FILTER_COLON', 9, "Missing ':' before filter argument");
+  static const AngularCode INVALID_URI = const AngularCode('INVALID_URI', 9, "Invalid URI syntax: '%s'");
 
-  static const AngularCode MISSING_NAME = const AngularCode('MISSING_NAME', 10, "Argument 'name' must be provided");
+  static const AngularCode MISSING_FILTER_COLON = const AngularCode('MISSING_FILTER_COLON', 10, "Missing ':' before filter argument");
 
-  static const AngularCode MISSING_PUBLISH_AS = const AngularCode('MISSING_PUBLISH_AS', 11, "Argument 'publishAs' must be provided");
+  static const AngularCode MISSING_NAME = const AngularCode('MISSING_NAME', 11, "Argument 'name' must be provided");
 
-  static const AngularCode MISSING_SELECTOR = const AngularCode('MISSING_SELECTOR', 12, "Argument 'selector' must be provided");
+  static const AngularCode MISSING_PUBLISH_AS = const AngularCode('MISSING_PUBLISH_AS', 12, "Argument 'publishAs' must be provided");
 
-  static const AngularCode URI_DOES_NOT_EXIST = const AngularCode('URI_DOES_NOT_EXIST', 13, "Target of URI does not exist: '%s'");
+  static const AngularCode MISSING_SELECTOR = const AngularCode('MISSING_SELECTOR', 13, "Argument 'selector' must be provided");
+
+  static const AngularCode URI_DOES_NOT_EXIST = const AngularCode('URI_DOES_NOT_EXIST', 14, "Target of URI does not exist: '%s'");
 
   static const List<AngularCode> values = const [
       CANNOT_PARSE_SELECTOR,
+      INVALID_FILTER_NAME,
       INVALID_PROPERTY_KIND,
       INVALID_PROPERTY_FIELD,
       INVALID_PROPERTY_MAP,
@@ -1534,7 +1537,7 @@
    * @param expressionSource the expression source code that is the unexpected type
    * @param expectedType the name of the expected type
    */
-  static const CompileTimeErrorCode INCONSISTENT_CASE_EXPRESSION_TYPES = const CompileTimeErrorCode.con1('INCONSISTENT_CASE_EXPRESSION_TYPES', 65, "Case expressions must have the same types, '%s' is not a %s'");
+  static const CompileTimeErrorCode INCONSISTENT_CASE_EXPRESSION_TYPES = const CompileTimeErrorCode.con1('INCONSISTENT_CASE_EXPRESSION_TYPES', 65, "Case expressions must have the same types, '%s' is not a '%s'");
 
   /**
    * 7.6.1 Generative Constructors: Let <i>k</i> be a generative constructor. It is a compile-time
@@ -2104,16 +2107,6 @@
   static const CompileTimeErrorCode UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT = const CompileTimeErrorCode.con1('UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT', 134, "The class '%s' does not have a default generative constructor");
 
   /**
-   * 12.14.3 Unqualified Invocation: If there exists a lexically visible declaration named
-   * <i>id</i>, let <i>f<sub>id</sub></i> be the innermost such declaration. Then: [skip].
-   * Otherwise, <i>i</i> is equivalent to <b>this</b>.<i>id</i>(<i>a<sub>1</sub></i>; ...
-   * <i>x<sub>n+k</sub></i> : <i>a<sub>n+k</sub></i>).
-   *
-   * @param methodName the name of the method that is undefined
-   */
-  static const CompileTimeErrorCode UNDEFINED_FUNCTION = const CompileTimeErrorCode.con1('UNDEFINED_FUNCTION', 135, "The function '%s' is not defined");
-
-  /**
    * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>, <i>1<=i<=l</i>,
    * must have a corresponding named parameter in the set {<i>p<sub>n+1</sub></i> ...
    * <i>p<sub>n+k</sub></i>} or a static warning occurs.
@@ -2123,7 +2116,7 @@
    *
    * @param name the name of the requested named parameter
    */
-  static const CompileTimeErrorCode UNDEFINED_NAMED_PARAMETER = const CompileTimeErrorCode.con1('UNDEFINED_NAMED_PARAMETER', 136, "The named parameter '%s' is not defined");
+  static const CompileTimeErrorCode UNDEFINED_NAMED_PARAMETER = const CompileTimeErrorCode.con1('UNDEFINED_NAMED_PARAMETER', 135, "The named parameter '%s' is not defined");
 
   /**
    * 14.2 Exports: It is a compile-time error if the compilation unit found at the specified URI is
@@ -2138,7 +2131,7 @@
    * @param uri the URI pointing to a non-existent file
    * @see #INVALID_URI
    */
-  static const CompileTimeErrorCode URI_DOES_NOT_EXIST = const CompileTimeErrorCode.con1('URI_DOES_NOT_EXIST', 137, "Target of URI does not exist: '%s'");
+  static const CompileTimeErrorCode URI_DOES_NOT_EXIST = const CompileTimeErrorCode.con1('URI_DOES_NOT_EXIST', 136, "Target of URI does not exist: '%s'");
 
   /**
    * 14.1 Imports: It is a compile-time error if <i>x</i> is not a compile-time constant, or if
@@ -2150,7 +2143,7 @@
    * 14.5 URIs: It is a compile-time error if the string literal <i>x</i> that describes a URI is
    * not a compile-time constant, or if <i>x</i> involves string interpolation.
    */
-  static const CompileTimeErrorCode URI_WITH_INTERPOLATION = const CompileTimeErrorCode.con1('URI_WITH_INTERPOLATION', 138, "URIs cannot use string interpolation");
+  static const CompileTimeErrorCode URI_WITH_INTERPOLATION = const CompileTimeErrorCode.con1('URI_WITH_INTERPOLATION', 137, "URIs cannot use string interpolation");
 
   /**
    * 7.1.1 Operators: It is a compile-time error if the arity of the user-declared operator []= is
@@ -2163,7 +2156,7 @@
    * @param expectedNumberOfParameters the number of parameters expected
    * @param actualNumberOfParameters the number of parameters found in the operator declaration
    */
-  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR', 139, "Operator '%s' should declare exactly %d parameter(s), but %d found");
+  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR', 138, "Operator '%s' should declare exactly %d parameter(s), but %d found");
 
   /**
    * 7.1.1 Operators: It is a compile time error if the arity of the user-declared operator - is not
@@ -2171,13 +2164,13 @@
    *
    * @param actualNumberOfParameters the number of parameters found in the operator declaration
    */
-  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS', 140, "Operator '-' should declare 0 or 1 parameter, but %d found");
+  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_OPERATOR_MINUS', 139, "Operator '-' should declare 0 or 1 parameter, but %d found");
 
   /**
    * 7.3 Setters: It is a compile-time error if a setter's formal parameter list does not include
    * exactly one required formal parameter <i>p</i>.
    */
-  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER', 141, "Setters should declare exactly one required parameter");
+  static const CompileTimeErrorCode WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER = const CompileTimeErrorCode.con1('WRONG_NUMBER_OF_PARAMETERS_FOR_SETTER', 140, "Setters should declare exactly one required parameter");
 
   static const List<CompileTimeErrorCode> values = const [
       AMBIGUOUS_EXPORT,
@@ -2315,7 +2308,6 @@
       UNDEFINED_CLASS,
       UNDEFINED_CONSTRUCTOR_IN_INITIALIZER,
       UNDEFINED_CONSTRUCTOR_IN_INITIALIZER_DEFAULT,
-      UNDEFINED_FUNCTION,
       UNDEFINED_NAMED_PARAMETER,
       URI_DOES_NOT_EXIST,
       URI_WITH_INTERPOLATION,
@@ -2670,14 +2662,13 @@
   static const StaticWarningCode IMPORT_DUPLICATED_LIBRARY_NAME = const StaticWarningCode.con1('IMPORT_DUPLICATED_LIBRARY_NAME', 25, "The imported libraries '%s' and '%s' should not have the same name '%s'");
 
   /**
-   * 8.1.1 Inheritance and Overriding: However, if there are multiple members <i>m<sub>1</sub>,
-   * &hellip; m<sub>k</sub></i> with the same name <i>n</i> that would be inherited (because
-   * identically named members existed in several superinterfaces) then at most one member is
-   * inherited.
+   * 8.1.1 Inheritance and Overriding: However, if the above rules would cause multiple members
+   * <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i> with the same name <i>n</i> that would be
+   * inherited (because identically named members existed in several superinterfaces) then at most
+   * one member is inherited.
    *
-   * If some but not all of the <i>m<sub>i</sub>, 1 &lt;= i &lt;= k</i>, are getters, or if some but
-   * not all of the <i>m<sub>i</sub></i> are setters, none of the <i>m<sub>i</sub></i> are
-   * inherited, and a static warning is issued.
+   * If some but not all of the <i>m<sub>i</sub>, 1 &lt;= i &lt;= k</i> are getters none of the
+   * <i>m<sub>i</sub></i> are inherited, and a static warning is issued.
    */
   static const StaticWarningCode INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD = const StaticWarningCode.con1('INCONSISTENT_METHOD_INHERITANCE_GETTER_AND_METHOD', 26, "'%s' is inherited as a getter and also a method");
 
@@ -2807,7 +2798,7 @@
   static const StaticWarningCode INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE = const StaticWarningCode.con1('INVALID_SETTER_OVERRIDE_NORMAL_PARAM_TYPE', 38, "The parameter type '%s' is not assignable to '%s' as required by the setter it is overriding from '%s'");
 
   /**
-   * 12.6 Lists: A run-time list literal &lt;<i>E</i>&gt; [<i>e<sub>1</sub></i> ...
+   * 12.6 Lists: A run-time list literal &lt;<i>E</i>&gt; [<i>e<sub>1</sub></i> &hellip;
    * <i>e<sub>n</sub></i>] is evaluated as follows:
    * * The operator []= is invoked on <i>a</i> with first argument <i>i</i> and second argument
    * <i>o<sub>i+1</sub></i><i>, 1 &lt;= i &lt;= n</i>
@@ -2822,7 +2813,8 @@
 
   /**
    * 12.7 Map: A run-time map literal &lt;<i>K</i>, <i>V</i>&gt; [<i>k<sub>1</sub></i> :
-   * <i>e<sub>1</sub></i> ... <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as follows:
+   * <i>e<sub>1</sub></i> &hellip; <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as
+   * follows:
    * * The operator []= is invoked on <i>m</i> with first argument <i>k<sub>i</sub></i> and second
    * argument <i>e<sub>i</sub></i><i>, 1 &lt;= i &lt;= n</i>
    *
@@ -2836,7 +2828,8 @@
 
   /**
    * 12.7 Map: A run-time map literal &lt;<i>K</i>, <i>V</i>&gt; [<i>k<sub>1</sub></i> :
-   * <i>e<sub>1</sub></i> ... <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as follows:
+   * <i>e<sub>1</sub></i> &hellip; <i>k<sub>n</sub></i> : <i>e<sub>n</sub></i>] is evaluated as
+   * follows:
    * * The operator []= is invoked on <i>m</i> with first argument <i>k<sub>i</sub></i> and second
    * argument <i>e<sub>i</sub></i><i>, 1 &lt;= i &lt;= n</i>
    *
@@ -2899,7 +2892,7 @@
    * x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>: a<sub>n+k</sub>)</i> it is a
    * static warning if <i>T.id</i> is not the name of a constructor declared by the type <i>T</i>.
    * If <i>e</i> of the form <i>new T(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
-   * a<sub>n+1</sub>, &hellip; x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
+   * a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
    * type <i>T</i> does not declare a constructor with the same name as the declaration of <i>T</i>.
    */
   static const StaticWarningCode NEW_WITH_UNDEFINED_CONSTRUCTOR = const StaticWarningCode.con1('NEW_WITH_UNDEFINED_CONSTRUCTOR', 48, "The class '%s' does not have a constructor '%s'");
@@ -2910,7 +2903,7 @@
    * x<sub>n+1</sub>: a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>: a<sub>n+k</sub>)</i> it is a
    * static warning if <i>T.id</i> is not the name of a constructor declared by the type <i>T</i>.
    * If <i>e</i> of the form <i>new T(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>:
-   * a<sub>n+1</sub>, &hellip; x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
+   * a<sub>n+1</sub>, &hellip;, x<sub>n+k</sub>: a<sub>n+kM/sub>)</i> it is a static warning if the
    * type <i>T</i> does not declare a constructor with the same name as the declaration of <i>T</i>.
    */
   static const StaticWarningCode NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT = const StaticWarningCode.con1('NEW_WITH_UNDEFINED_CONSTRUCTOR_DEFAULT', 49, "The class '%s' does not have a default constructor");
@@ -3165,8 +3158,8 @@
   static const StaticWarningCode UNDEFINED_IDENTIFIER = const StaticWarningCode.con1('UNDEFINED_IDENTIFIER', 73, "Undefined name '%s'");
 
   /**
-   * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>, <i>1<=i<=l</i>,
-   * must have a corresponding named parameter in the set {<i>p<sub>n+1</sub></i> ...
+   * 12.14.2 Binding Actuals to Formals: Furthermore, each <i>q<sub>i</sub></i>, <i>1<=i<=l</i>,
+   * must have a corresponding named parameter in the set {<i>p<sub>n+1</sub></i> &hellip;
    * <i>p<sub>n+k</sub></i>} or a static warning occurs.
    *
    * @param name the name of the requested named parameter
@@ -3429,24 +3422,26 @@
   static const StaticTypeWarningCode INACCESSIBLE_SETTER = const StaticTypeWarningCode.con1('INACCESSIBLE_SETTER', 2, "");
 
   /**
-   * 8.1.1 Inheritance and Overriding: However, if there are multiple members <i>m<sub>1</sub>,
-   * &hellip; m<sub>k</sub></i> with the same name <i>n</i> that would be inherited (because
-   * identically named members existed in several superinterfaces) then at most one member is
-   * inherited.
+   * 8.1.1 Inheritance and Overriding: However, if the above rules would cause multiple members
+   * <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i> with the same name <i>n</i> that would be
+   * inherited (because identically named members existed in several superinterfaces) then at most
+   * one member is inherited.
    *
    * If the static types <i>T<sub>1</sub>, &hellip;, T<sub>k</sub></i> of the members
    * <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i> are not identical, then there must be a member
-   * <i>m<sub>x</sub></i> such that <i>T<sub>x</sub> &lt; T<sub>i</sub>, 1 &lt;= x &lt;= k</i> for
-   * all <i>i, 1 &lt;= i &lt; k</i>, or a static type warning occurs. The member that is inherited
+   * <i>m<sub>x</sub></i> such that <i>T<sub>x</sub> &lt;: T<sub>i</sub>, 1 &lt;= x &lt;= k</i> for
+   * all <i>i, 1 &lt;= i &lt;= k</i>, or a static type warning occurs. The member that is inherited
    * is <i>m<sub>x</sub></i>, if it exists; otherwise:
-   * <ol>
-   * * If all of <i>m<sub>1</sub>, &hellip; m<sub>k</sub></i> have the same number <i>r</i> of
-   * required parameters and the same set of named parameters <i>s</i>, then let <i>h = max(
-   * numberOfOptionalPositionals( m<sub>i</sub> ) ), 1 &lt;= i &lt;= k</i>. <i>I</i> has a method
-   * named <i>n</i>, with <i>r</i> required parameters of type dynamic, <i>h</i> optional positional
-   * parameters of type dynamic, named parameters <i>s</i> of type dynamic and return type dynamic.
+   * * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional parameters of a
+   * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote the number of
+   * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denote the set of all
+   * named parameters of the <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i>. Then let
+   * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
+   * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <= i <= k.</i>
+   * If <i>r <= h</i> then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameters
+   * of type <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, named parameters
+   * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
    * * Otherwise none of the members <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i> is inherited.
-   * </ol>
    */
   static const StaticTypeWarningCode INCONSISTENT_METHOD_INHERITANCE = const StaticTypeWarningCode.con1('INCONSISTENT_METHOD_INHERITANCE', 3, "'%s' is inherited by at least two interfaces inconsistently, from %s");
 
@@ -3500,7 +3495,7 @@
 
   /**
    * 12.14.4 Function Expression Invocation: A function expression invocation <i>i</i> has the form
-   * <i>e<sub>f</sub>(a<sub>1</sub>, &hellip; a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>,
+   * <i>e<sub>f</sub>(a<sub>1</sub>, &hellip;, a<sub>n</sub>, x<sub>n+1</sub>: a<sub>n+1</sub>,
    * &hellip;, x<sub>n+k</sub>: a<sub>n+k</sub>)</i>, where <i>e<sub>f</sub></i> is an expression.
    *
    * It is a static type warning if the static type <i>F</i> of <i>e<sub>f</sub></i> may not be
@@ -3559,12 +3554,13 @@
    * <i>G</i>.
    *
    * 15.8 Parameterized Types: If <i>S</i> is the static type of a member <i>m</i> of <i>G</i>, then
-   * the static type of the member <i>m</i> of <i>G&lt;A<sub>1</sub>, &hellip; A<sub>n</sub>&gt;</i>
-   * is <i>[A<sub>1</sub>, &hellip;, A<sub>n</sub>/T<sub>1</sub>, &hellip;, T<sub>n</sub>]S</i>
-   * where <i>T<sub>1</sub>, &hellip; T<sub>n</sub></i> are the formal type parameters of <i>G</i>.
-   * Let <i>B<sub>i</sub></i> be the bounds of <i>T<sub>i</sub>, 1 &lt;= i &lt;= n</i>. It is a
-   * static type warning if <i>A<sub>i</sub></i> is not a subtype of <i>[A<sub>1</sub>, &hellip;,
-   * A<sub>n</sub>/T<sub>1</sub>, &hellip;, T<sub>n</sub>]B<sub>i</sub>, 1 &lt;= i &lt;= n</i>.
+   * the static type of the member <i>m</i> of <i>G&lt;A<sub>1</sub>, &hellip;,
+   * A<sub>n</sub>&gt;</i> is <i>[A<sub>1</sub>, &hellip;, A<sub>n</sub>/T<sub>1</sub>, &hellip;,
+   * T<sub>n</sub>]S</i> where <i>T<sub>1</sub>, &hellip;, T<sub>n</sub></i> are the formal type
+   * parameters of <i>G</i>. Let <i>B<sub>i</sub></i> be the bounds of <i>T<sub>i</sub>, 1 &lt;= i
+   * &lt;= n</i>. It is a static type warning if <i>A<sub>i</sub></i> is not a subtype of
+   * <i>[A<sub>1</sub>, &hellip;, A<sub>n</sub>/T<sub>1</sub>, &hellip;,
+   * T<sub>n</sub>]B<sub>i</sub>, 1 &lt;= i &lt;= n</i>.
    *
    * 7.6.2 Factories: It is a static type warning if any of the type arguments to <i>k'</i> are not
    * subtypes of the bounds of the corresponding formal type parameters of type.
@@ -3585,13 +3581,24 @@
   static const StaticTypeWarningCode TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND = const StaticTypeWarningCode.con1('TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND', 14, "'%s' cannot be a supertype of its upper bound");
 
   /**
+   * 12.15.3 Unqualified Invocation: If there exists a lexically visible declaration named
+   * <i>id</i>, let <i>f<sub>id</sub></i> be the innermost such declaration. Then: [skip].
+   * Otherwise, <i>f<sub>id</sub></i> is considered equivalent to the ordinary method invocation
+   * <b>this</b>.<i>id</i>(<i>a<sub>1</sub></i>, ..., <i>a<sub>n</sub></i>, <i>x<sub>n+1</sub></i> :
+   * <i>a<sub>n+1</sub></i>, ..., <i>x<sub>n+k</sub></i> : <i>a<sub>n+k</sub></i>).
+   *
+   * @param methodName the name of the method that is undefined
+   */
+  static const StaticTypeWarningCode UNDEFINED_FUNCTION = const StaticTypeWarningCode.con1('UNDEFINED_FUNCTION', 15, "The function '%s' is not defined");
+
+  /**
    * 12.17 Getter Invocation: Let <i>T</i> be the static type of <i>e</i>. It is a static type
    * warning if <i>T</i> does not have a getter named <i>m</i>.
    *
    * @param getterName the name of the getter
    * @param enclosingType the name of the enclosing type where the getter is being looked for
    */
-  static const StaticTypeWarningCode UNDEFINED_GETTER = const StaticTypeWarningCode.con1('UNDEFINED_GETTER', 15, "There is no such getter '%s' in '%s'");
+  static const StaticTypeWarningCode UNDEFINED_GETTER = const StaticTypeWarningCode.con1('UNDEFINED_GETTER', 16, "There is no such getter '%s' in '%s'");
 
   /**
    * 12.15.1 Ordinary Invocation: Let <i>T</i> be the static type of <i>o</i>. It is a static type
@@ -3600,7 +3607,7 @@
    * @param methodName the name of the method that is undefined
    * @param typeName the resolved type name that the method lookup is happening on
    */
-  static const StaticTypeWarningCode UNDEFINED_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_METHOD', 16, "The method '%s' is not defined for the class '%s'");
+  static const StaticTypeWarningCode UNDEFINED_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_METHOD', 17, "The method '%s' is not defined for the class '%s'");
 
   /**
    * 12.18 Assignment: Evaluation of an assignment of the form
@@ -3618,7 +3625,7 @@
    * @param operator the name of the operator
    * @param enclosingType the name of the enclosing type where the operator is being looked for
    */
-  static const StaticTypeWarningCode UNDEFINED_OPERATOR = const StaticTypeWarningCode.con1('UNDEFINED_OPERATOR', 17, "There is no such operator '%s' in '%s'");
+  static const StaticTypeWarningCode UNDEFINED_OPERATOR = const StaticTypeWarningCode.con1('UNDEFINED_OPERATOR', 18, "There is no such operator '%s' in '%s'");
 
   /**
    * 12.18 Assignment: Let <i>T</i> be the static type of <i>e<sub>1</sub></i>. It is a static type
@@ -3628,7 +3635,7 @@
    * @param enclosingType the name of the enclosing type where the setter is being looked for
    * @see #INACCESSIBLE_SETTER
    */
-  static const StaticTypeWarningCode UNDEFINED_SETTER = const StaticTypeWarningCode.con1('UNDEFINED_SETTER', 18, "There is no such setter '%s' in '%s'");
+  static const StaticTypeWarningCode UNDEFINED_SETTER = const StaticTypeWarningCode.con1('UNDEFINED_SETTER', 19, "There is no such setter '%s' in '%s'");
 
   /**
    * 12.15.4 Super Invocation: A super method invocation <i>i</i> has the form
@@ -3639,7 +3646,7 @@
    * @param methodName the name of the method that is undefined
    * @param typeName the resolved type name that the method lookup is happening on
    */
-  static const StaticTypeWarningCode UNDEFINED_SUPER_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_SUPER_METHOD', 19, "There is no such method '%s' in '%s'");
+  static const StaticTypeWarningCode UNDEFINED_SUPER_METHOD = const StaticTypeWarningCode.con1('UNDEFINED_SUPER_METHOD', 20, "There is no such method '%s' in '%s'");
 
   /**
    * 12.15.1 Ordinary Invocation: It is a static type warning if <i>T</i> does not have an
@@ -3649,7 +3656,7 @@
    * able to find the name defined in a supertype. It exists to provide a more informative error
    * message.
    */
-  static const StaticTypeWarningCode UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER = const StaticTypeWarningCode.con1('UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER', 20, "Static members from supertypes must be qualified by the name of the defining type");
+  static const StaticTypeWarningCode UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER = const StaticTypeWarningCode.con1('UNQUALIFIED_REFERENCE_TO_NON_LOCAL_STATIC_MEMBER', 21, "Static members from supertypes must be qualified by the name of the defining type");
 
   /**
    * 15.8 Parameterized Types: It is a static type warning if <i>G</i> is not a generic type with
@@ -3661,7 +3668,7 @@
    * @see CompileTimeErrorCode#CONST_WITH_INVALID_TYPE_PARAMETERS
    * @see CompileTimeErrorCode#NEW_WITH_INVALID_TYPE_PARAMETERS
    */
-  static const StaticTypeWarningCode WRONG_NUMBER_OF_TYPE_ARGUMENTS = const StaticTypeWarningCode.con1('WRONG_NUMBER_OF_TYPE_ARGUMENTS', 21, "The type '%s' is declared with %d type parameters, but %d type arguments were given");
+  static const StaticTypeWarningCode WRONG_NUMBER_OF_TYPE_ARGUMENTS = const StaticTypeWarningCode.con1('WRONG_NUMBER_OF_TYPE_ARGUMENTS', 22, "The type '%s' is declared with %d type parameters, but %d type arguments were given");
 
   static const List<StaticTypeWarningCode> values = const [
       EXPECTED_ONE_LIST_TYPE_ARGUMENTS,
@@ -3679,6 +3686,7 @@
       RETURN_OF_INVALID_TYPE,
       TYPE_ARGUMENT_NOT_MATCHING_BOUNDS,
       TYPE_PARAMETER_SUPERTYPE_OF_ITS_BOUND,
+      UNDEFINED_FUNCTION,
       UNDEFINED_GETTER,
       UNDEFINED_METHOD,
       UNDEFINED_OPERATOR,
diff --git a/pkg/analyzer/lib/src/generated/html.dart b/pkg/analyzer/lib/src/generated/html.dart
index 2b26d5b..8438a54 100644
--- a/pkg/analyzer/lib/src/generated/html.dart
+++ b/pkg/analyzer/lib/src/generated/html.dart
@@ -1860,7 +1860,20 @@
   /**
    * A set containing the names of tags that do not have a closing tag.
    */
-  static Set<String> SELF_CLOSING = new Set<String>();
+  static Set<String> SELF_CLOSING = new Set<String>.from(JavaArrays.asList(<String> [
+      "area",
+      "base",
+      "basefont",
+      "br",
+      "col",
+      "frame",
+      "hr",
+      "img",
+      "input",
+      "link",
+      "meta",
+      "param",
+      "!"]));
 
   /**
    * Given the contents of an embedded expression that occurs at the given offset, parse it as a
diff --git a/pkg/analyzer/lib/src/generated/index.dart b/pkg/analyzer/lib/src/generated/index.dart
index 4eda02c..544f582 100644
--- a/pkg/analyzer/lib/src/generated/index.dart
+++ b/pkg/analyzer/lib/src/generated/index.dart
@@ -17,7 +17,6 @@
 import 'resolver.dart' show Namespace, NamespaceBuilder;
 import 'engine.dart';
 import 'html.dart' as ht;
-import 'utilities_collection.dart';
 
 /**
  * Instances of the [RemoveSourceOperation] implement an operation that removes from the index
@@ -1505,9 +1504,9 @@
       importElementsMap[importElement] = elements;
     }
     // use import namespace to choose correct one
-    for (MapIterator<ImportElement, Set<Element>> iter = SingleMapIterator.forMap(importElementsMap); iter.moveNext();) {
-      if (iter.value.contains(usedElement)) {
-        return iter.key;
+    for (MapEntry<ImportElement, Set<Element>> entry in getMapEntrySet(importElementsMap)) {
+      if (entry.getValue().contains(usedElement)) {
+        return entry.getKey();
       }
     }
     // not found
diff --git a/pkg/analyzer/lib/src/generated/java_core.dart b/pkg/analyzer/lib/src/generated/java_core.dart
index a075ca8..3ee1055 100644
--- a/pkg/analyzer/lib/src/generated/java_core.dart
+++ b/pkg/analyzer/lib/src/generated/java_core.dart
@@ -220,6 +220,9 @@
     }
     return sb.toString();
   }
+  static String join(Iterable iter, [String separator = " "]) {
+    return iter.join(separator);
+  }
 }
 
 class Math {
@@ -257,7 +260,7 @@
 }
 
 class UnsupportedOperationException extends JavaException {
-  String toString() => "UnsupportedOperationException";
+  UnsupportedOperationException([message = ""]) : super(message);
 }
 
 class NoSuchElementException extends JavaException {
diff --git a/pkg/analyzer/lib/src/generated/parser.dart b/pkg/analyzer/lib/src/generated/parser.dart
index 2721627..dab5fb6 100644
--- a/pkg/analyzer/lib/src/generated/parser.dart
+++ b/pkg/analyzer/lib/src/generated/parser.dart
@@ -1458,6 +1458,12 @@
    */
   bool _inSwitch = false;
 
+  /**
+   * A flag indicating whether the parser is currently in a constructor field initializer, with no
+   * intervening parens, braces, or brackets.
+   */
+  bool _inInitializer = false;
+
   static String _HIDE = "hide";
 
   static String _OF = "of";
@@ -1629,28 +1635,34 @@
     // Even though unnamed arguments must all appear before any named arguments, we allow them to
     // appear in any order so that we can recover faster.
     //
-    Expression argument = parseArgument();
-    arguments.add(argument);
-    bool foundNamedArgument = argument is NamedExpression;
-    bool generatedError = false;
-    while (_optional(TokenType.COMMA)) {
-      argument = parseArgument();
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = false;
+    try {
+      Expression argument = parseArgument();
       arguments.add(argument);
-      if (foundNamedArgument) {
-        if (!generatedError && argument is! NamedExpression) {
-          // Report the error, once, but allow the arguments to be in any order in the AST.
-          _reportErrorForCurrentToken(ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT, []);
-          generatedError = true;
+      bool foundNamedArgument = argument is NamedExpression;
+      bool generatedError = false;
+      while (_optional(TokenType.COMMA)) {
+        argument = parseArgument();
+        arguments.add(argument);
+        if (foundNamedArgument) {
+          if (!generatedError && argument is! NamedExpression) {
+            // Report the error, once, but allow the arguments to be in any order in the AST.
+            _reportErrorForCurrentToken(ParserErrorCode.POSITIONAL_AFTER_NAMED_ARGUMENT, []);
+            generatedError = true;
+          }
+        } else if (argument is NamedExpression) {
+          foundNamedArgument = true;
         }
-      } else if (argument is NamedExpression) {
-        foundNamedArgument = true;
       }
+      // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see whether there is a
+      // matching right parenthesis. If there is, then we're more likely missing a comma and should
+      // go back to parsing arguments.
+      Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+      return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
+    } finally {
+      _inInitializer = wasInInitializer;
     }
-    // TODO(brianwilkerson) Recovery: Look at the left parenthesis to see whether there is a
-    // matching right parenthesis. If there is, then we're more likely missing a comma and should
-    // go back to parsing arguments.
-    Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
-    return new ArgumentList(leftParenthesis, arguments, rightParenthesis);
   }
 
   /**
@@ -3072,6 +3084,10 @@
    * @return `true` if the given token appears to be the beginning of a function expression
    */
   bool _isFunctionExpression(Token startToken) {
+    // Function expressions aren't allowed in initializer lists.
+    if (_inInitializer) {
+      return false;
+    }
     Token afterParameters = _skipFormalParameterList(startToken);
     if (afterParameters == null) {
       return false;
@@ -3504,9 +3520,15 @@
   Expression _parseAssignableSelector(Expression prefix, bool optional) {
     if (_matches(TokenType.OPEN_SQUARE_BRACKET)) {
       Token leftBracket = andAdvance;
-      Expression index = parseExpression2();
-      Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
-      return new IndexExpression.forTarget(prefix, leftBracket, index, rightBracket);
+      bool wasInInitializer = _inInitializer;
+      _inInitializer = false;
+      try {
+        Expression index = parseExpression2();
+        Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+        return new IndexExpression.forTarget(prefix, leftBracket, index, rightBracket);
+      } finally {
+        _inInitializer = wasInInitializer;
+      }
     } else if (_matches(TokenType.PERIOD)) {
       Token period = andAdvance;
       return new PropertyAccess(prefix, period, parseSimpleIdentifier());
@@ -3617,10 +3639,16 @@
       functionName = parseSimpleIdentifier();
     } else if (_currentToken.type == TokenType.OPEN_SQUARE_BRACKET) {
       Token leftBracket = andAdvance;
-      Expression index = parseExpression2();
-      Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
-      expression = new IndexExpression.forCascade(period, leftBracket, index, rightBracket);
-      period = null;
+      bool wasInInitializer = _inInitializer;
+      _inInitializer = false;
+      try {
+        Expression index = parseExpression2();
+        Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+        expression = new IndexExpression.forCascade(period, leftBracket, index, rightBracket);
+        period = null;
+      } finally {
+        _inInitializer = wasInInitializer;
+      }
     } else {
       _reportErrorForToken(ParserErrorCode.MISSING_IDENTIFIER, _currentToken, [_currentToken.lexeme]);
       functionName = _createSyntheticIdentifier();
@@ -3830,13 +3858,12 @@
       typeParameters = parseTypeParameterList();
     }
     Token equals = _expect(TokenType.EQ);
-    if (_matchesKeyword(Keyword.ABSTRACT)) {
-      abstractKeyword = andAdvance;
-    }
     TypeName superclass = parseTypeName();
     WithClause withClause = null;
     if (_matchesKeyword(Keyword.WITH)) {
       withClause = parseWithClause();
+    } else {
+      _reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, [Keyword.WITH.syntax]);
     }
     ImplementsClause implementsClause = null;
     if (_matchesKeyword(Keyword.IMPLEMENTS)) {
@@ -4235,20 +4262,26 @@
     }
     SimpleIdentifier fieldName = parseSimpleIdentifier();
     Token equals = _expect(TokenType.EQ);
-    Expression expression = parseConditionalExpression();
-    TokenType tokenType = _currentToken.type;
-    if (tokenType == TokenType.PERIOD_PERIOD) {
-      List<Expression> cascadeSections = new List<Expression>();
-      while (tokenType == TokenType.PERIOD_PERIOD) {
-        Expression section = _parseCascadeSection();
-        if (section != null) {
-          cascadeSections.add(section);
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = true;
+    try {
+      Expression expression = parseConditionalExpression();
+      TokenType tokenType = _currentToken.type;
+      if (tokenType == TokenType.PERIOD_PERIOD) {
+        List<Expression> cascadeSections = new List<Expression>();
+        while (tokenType == TokenType.PERIOD_PERIOD) {
+          Expression section = _parseCascadeSection();
+          if (section != null) {
+            cascadeSections.add(section);
+          }
+          tokenType = _currentToken.type;
         }
-        tokenType = _currentToken.type;
+        expression = new CascadeExpression(expression, cascadeSections);
       }
-      expression = new CascadeExpression(expression, cascadeSections);
+      return new ConstructorFieldInitializer(keyword, period, fieldName, equals, expression);
+    } finally {
+      _inInitializer = wasInInitializer;
     }
-    return new ConstructorFieldInitializer(keyword, period, fieldName, equals, expression);
   }
 
   /**
@@ -5055,16 +5088,22 @@
     if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
       return new ListLiteral(modifier, typeArguments, leftBracket, null, andAdvance);
     }
-    List<Expression> elements = new List<Expression>();
-    elements.add(parseExpression2());
-    while (_optional(TokenType.COMMA)) {
-      if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
-        return new ListLiteral(modifier, typeArguments, leftBracket, elements, andAdvance);
-      }
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = false;
+    try {
+      List<Expression> elements = new List<Expression>();
       elements.add(parseExpression2());
+      while (_optional(TokenType.COMMA)) {
+        if (_matches(TokenType.CLOSE_SQUARE_BRACKET)) {
+          return new ListLiteral(modifier, typeArguments, leftBracket, elements, andAdvance);
+        }
+        elements.add(parseExpression2());
+      }
+      Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
+      return new ListLiteral(modifier, typeArguments, leftBracket, elements, rightBracket);
+    } finally {
+      _inInitializer = wasInInitializer;
     }
-    Token rightBracket = _expect(TokenType.CLOSE_SQUARE_BRACKET);
-    return new ListLiteral(modifier, typeArguments, leftBracket, elements, rightBracket);
   }
 
   /**
@@ -5133,15 +5172,21 @@
     if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
       return new MapLiteral(modifier, typeArguments, leftBracket, entries, andAdvance);
     }
-    entries.add(parseMapLiteralEntry());
-    while (_optional(TokenType.COMMA)) {
-      if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
-        return new MapLiteral(modifier, typeArguments, leftBracket, entries, andAdvance);
-      }
+    bool wasInInitializer = _inInitializer;
+    _inInitializer = false;
+    try {
       entries.add(parseMapLiteralEntry());
+      while (_optional(TokenType.COMMA)) {
+        if (_matches(TokenType.CLOSE_CURLY_BRACKET)) {
+          return new MapLiteral(modifier, typeArguments, leftBracket, entries, andAdvance);
+        }
+        entries.add(parseMapLiteralEntry());
+      }
+      Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+      return new MapLiteral(modifier, typeArguments, leftBracket, entries, rightBracket);
+    } finally {
+      _inInitializer = wasInInitializer;
     }
-    Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
-    return new MapLiteral(modifier, typeArguments, leftBracket, entries, rightBracket);
   }
 
   /**
@@ -5679,9 +5724,15 @@
         return parseFunctionExpression();
       }
       Token leftParenthesis = andAdvance;
-      Expression expression = parseExpression2();
-      Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
-      return new ParenthesizedExpression(leftParenthesis, expression, rightParenthesis);
+      bool wasInInitializer = _inInitializer;
+      _inInitializer = false;
+      try {
+        Expression expression = parseExpression2();
+        Token rightParenthesis = _expect(TokenType.CLOSE_PAREN);
+        return new ParenthesizedExpression(leftParenthesis, expression, rightParenthesis);
+      } finally {
+        _inInitializer = wasInInitializer;
+      }
     } else if (_matches(TokenType.LT)) {
       return _parseListOrMapLiteral(null);
     } else if (_matches(TokenType.QUESTION)) {
@@ -5884,9 +5935,15 @@
     while (hasMore) {
       if (_matches(TokenType.STRING_INTERPOLATION_EXPRESSION)) {
         Token openToken = andAdvance;
-        Expression expression = parseExpression2();
-        Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
-        elements.add(new InterpolationExpression(openToken, expression, rightBracket));
+        bool wasInInitializer = _inInitializer;
+        _inInitializer = false;
+        try {
+          Expression expression = parseExpression2();
+          Token rightBracket = _expect(TokenType.CLOSE_CURLY_BRACKET);
+          elements.add(new InterpolationExpression(openToken, expression, rightBracket));
+        } finally {
+          _inInitializer = wasInInitializer;
+        }
       } else {
         Token openToken = andAdvance;
         Expression expression = null;
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 3572534..b933093 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -16,7 +16,6 @@
 import 'scanner.dart' as sc;
 import 'utilities_dart.dart';
 import 'utilities_general.dart';
-import 'utilities_collection.dart';
 import 'ast.dart';
 import 'parser.dart' show Parser, ParserErrorCode;
 import 'sdk.dart' show DartSdk, SdkLibrary;
@@ -5671,9 +5670,13 @@
         MethodElement propagatedMethod = _lookUpMethod(leftHandSide, propagatedType, methodName);
         node.propagatedElement = propagatedMethod;
         if (_shouldReportMissingMember(staticType, staticMethod)) {
-          _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_METHOD, operator, [methodName, staticType.displayName]);
+          if (_doesClassElementHaveProxy(staticType.element)) {
+            _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_METHOD, operator, [methodName, staticType.displayName]);
+          }
         } else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
-          _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_METHOD, operator, [methodName, propagatedType.displayName]);
+          if (_doesClassElementHaveProxy(propagatedType.element)) {
+            _resolver.reportErrorForToken(HintCode.UNDEFINED_METHOD, operator, [methodName, propagatedType.displayName]);
+          }
         }
       }
     }
@@ -5694,9 +5697,13 @@
         MethodElement propagatedMethod = _lookUpMethod(leftOperand, propagatedType, methodName);
         node.propagatedElement = propagatedMethod;
         if (_shouldReportMissingMember(staticType, staticMethod)) {
-          _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+          if (_doesClassElementHaveProxy(staticType.element)) {
+            _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+          }
         } else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
-          _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+          if (_doesClassElementHaveProxy(propagatedType.element)) {
+            _resolver.reportErrorForToken(HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+          }
         }
       }
     }
@@ -6126,15 +6133,17 @@
     }
     if (identical(errorCode, StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION)) {
       _resolver.reportErrorForNode(StaticTypeWarningCode.INVOCATION_OF_NON_FUNCTION, methodName, [methodName.name]);
-    } else if (identical(errorCode, CompileTimeErrorCode.UNDEFINED_FUNCTION)) {
-      _resolver.reportErrorForNode(CompileTimeErrorCode.UNDEFINED_FUNCTION, methodName, [methodName.name]);
+    } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_FUNCTION)) {
+      _resolver.reportErrorForNode(StaticTypeWarningCode.UNDEFINED_FUNCTION, methodName, [methodName.name]);
     } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_METHOD)) {
       String targetTypeName;
       if (target == null) {
         ClassElement enclosingClass = _resolver.enclosingClass;
         targetTypeName = enclosingClass.displayName;
-        ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD) as ErrorCode;
-        _resolver.reportProxyConditionalErrorForNode(_resolver.enclosingClass, proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+        ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD);
+        if (_doesClassElementHaveProxy(_resolver.enclosingClass)) {
+          _resolver.reportErrorForNode(proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+        }
       } else {
         // ignore Function "call"
         // (if we are about to create a hint using type propagation, then we can use type
@@ -6155,8 +6164,10 @@
           return null;
         }
         targetTypeName = targetType == null ? null : targetType.displayName;
-        ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD) as ErrorCode;
-        _resolver.reportProxyConditionalErrorForNode(targetType.element, proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+        ErrorCode proxyErrorCode = (generatedWithTypePropagation ? HintCode.UNDEFINED_METHOD : StaticTypeWarningCode.UNDEFINED_METHOD);
+        if (_doesClassElementHaveProxy(targetType.element)) {
+          _resolver.reportErrorForNode(proxyErrorCode, methodName, [methodName.name, targetTypeName]);
+        }
       }
     } else if (identical(errorCode, StaticTypeWarningCode.UNDEFINED_SUPER_METHOD)) {
       // Generate the type name.
@@ -6191,9 +6202,13 @@
     MethodElement propagatedMethod = _lookUpMethod(operand, propagatedType, methodName);
     node.propagatedElement = propagatedMethod;
     if (_shouldReportMissingMember(staticType, staticMethod)) {
-      _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [methodName, staticType.displayName]);
+      if (_doesClassElementHaveProxy(staticType.element)) {
+        _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_OPERATOR, node.operator, [methodName, staticType.displayName]);
+      }
     } else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
-      _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR, node.operator, [methodName, propagatedType.displayName]);
+      if (_doesClassElementHaveProxy(propagatedType.element)) {
+        _resolver.reportErrorForToken(HintCode.UNDEFINED_OPERATOR, node.operator, [methodName, propagatedType.displayName]);
+      }
     }
     return null;
   }
@@ -6270,9 +6285,13 @@
       MethodElement propagatedMethod = _lookUpMethod(operand, propagatedType, methodName);
       node.propagatedElement = propagatedMethod;
       if (_shouldReportMissingMember(staticType, staticMethod)) {
-        _resolver.reportProxyConditionalErrorForToken(staticType.element, StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+        if (_doesClassElementHaveProxy(staticType.element)) {
+          _resolver.reportErrorForToken(StaticTypeWarningCode.UNDEFINED_OPERATOR, operator, [methodName, staticType.displayName]);
+        }
       } else if (_enableHints && _shouldReportMissingMember(propagatedType, propagatedMethod) && !_memberFoundInSubclass(propagatedType.element, methodName, true, false)) {
-        _resolver.reportProxyConditionalErrorForToken(propagatedType.element, HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+        if (_doesClassElementHaveProxy(propagatedType.element)) {
+          _resolver.reportErrorForToken(HintCode.UNDEFINED_OPERATOR, operator, [methodName, propagatedType.displayName]);
+        }
       }
     }
     return null;
@@ -6366,7 +6385,9 @@
         Annotation annotation = node.parent as Annotation;
         _resolver.reportErrorForNode(CompileTimeErrorCode.INVALID_ANNOTATION, annotation, []);
       } else {
-        _resolver.reportProxyConditionalErrorForNode(_resolver.enclosingClass, StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]);
+        if (_doesClassElementHaveProxy(_resolver.enclosingClass)) {
+          _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_IDENTIFIER, node, [node.name]);
+        }
       }
     }
     node.staticElement = element;
@@ -6520,7 +6541,7 @@
         if (target == null) {
           ClassElement enclosingClass = _resolver.enclosingClass;
           if (enclosingClass == null) {
-            return CompileTimeErrorCode.UNDEFINED_FUNCTION;
+            return StaticTypeWarningCode.UNDEFINED_FUNCTION;
           } else if (element == null) {
             // Proxy-conditional warning, based on state of resolver.getEnclosingClass()
             return StaticTypeWarningCode.UNDEFINED_METHOD;
@@ -6537,7 +6558,7 @@
             targetType = target.bestType;
           }
           if (targetType == null) {
-            return CompileTimeErrorCode.UNDEFINED_FUNCTION;
+            return StaticTypeWarningCode.UNDEFINED_FUNCTION;
           } else if (!targetType.isDynamic && !targetType.isBottom) {
             // Proxy-conditional warning, based on state of targetType.getElement()
             return StaticTypeWarningCode.UNDEFINED_METHOD;
@@ -6564,17 +6585,21 @@
     if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
       sc.Token leftBracket = node.leftBracket;
       sc.Token rightBracket = node.rightBracket;
-      ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR) as ErrorCode;
+      ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_OPERATOR : HintCode.UNDEFINED_OPERATOR);
       if (leftBracket == null || rightBracket == null) {
-        _resolver.reportProxyConditionalErrorForNode(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, node, [
-            methodName,
-            shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+        if (_doesClassElementHaveProxy(shouldReportMissingMember_static ? staticType.element : propagatedType.element)) {
+          _resolver.reportErrorForNode(errorCode, node, [
+              methodName,
+              shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+        }
       } else {
         int offset = leftBracket.offset;
         int length = rightBracket.offset - offset + 1;
-        _resolver.reportProxyConditionalErrorForOffset(shouldReportMissingMember_static ? staticType.element : propagatedType.element, errorCode, offset, length, [
-            methodName,
-            shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+        if (_doesClassElementHaveProxy(shouldReportMissingMember_static ? staticType.element : propagatedType.element)) {
+          _resolver.reportErrorForOffset(errorCode, offset, length, [
+              methodName,
+              shouldReportMissingMember_static ? staticType.displayName : propagatedType.displayName]);
+        }
       }
       return true;
     }
@@ -6647,6 +6672,22 @@
   }
 
   /**
+   * Return `true` iff the passed [Element] is a [ClassElement] and either has, or
+   * in that is or inherits proxy.
+   *
+   * @param element the enclosing element
+   * @return `true` iff the passed [Element] is a [ClassElement] and either has,
+   *         or in that is or inherits proxy
+   * @see ClassElement#isOrInheritsProxy()
+   */
+  bool _doesClassElementHaveProxy(Element element) {
+    if (element is ClassElement) {
+      return !element.isOrInheritsProxy;
+    }
+    return true;
+  }
+
+  /**
    * Look for any declarations of the given identifier that are imported using a prefix. Return the
    * element that was found, or `null` if the name is not imported using a prefix.
    *
@@ -6750,6 +6791,12 @@
       return true;
     } else if (type is InterfaceType) {
       ClassElement classElement = type.element;
+      // 16078 from Gilad: If the type is a Functor with the @proxy annotation, treat it as an
+      // executable type.
+      // example code: NonErrorResolverTest.test_invocationOfNonFunction_proxyOnFunctionClass()
+      if (classElement.isProxy && type.isSubtypeOf(_resolver.typeProvider.functionType)) {
+        return true;
+      }
       MethodElement methodElement = classElement.lookUpMethod(CALL_METHOD_NAME, _definingLibrary);
       return methodElement != null;
     }
@@ -7400,7 +7447,7 @@
         String name = nameNode.name;
         ParameterElement element = namedParameters[name];
         if (element == null) {
-          ErrorCode errorCode = (reportError ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER : StaticWarningCode.UNDEFINED_NAMED_PARAMETER) as ErrorCode;
+          ErrorCode errorCode = (reportError ? CompileTimeErrorCode.UNDEFINED_NAMED_PARAMETER : StaticWarningCode.UNDEFINED_NAMED_PARAMETER);
           _resolver.reportErrorForNode(errorCode, nameNode, [name]);
         } else {
           resolvedParameters[i] = element;
@@ -7417,10 +7464,10 @@
       }
     }
     if (positionalArgumentCount < requiredParameters.length) {
-      ErrorCode errorCode = (reportError ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS) as ErrorCode;
+      ErrorCode errorCode = (reportError ? CompileTimeErrorCode.NOT_ENOUGH_REQUIRED_ARGUMENTS : StaticWarningCode.NOT_ENOUGH_REQUIRED_ARGUMENTS);
       _resolver.reportErrorForNode(errorCode, argumentList, [requiredParameters.length, positionalArgumentCount]);
     } else if (positionalArgumentCount > unnamedParameterCount) {
-      ErrorCode errorCode = (reportError ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS) as ErrorCode;
+      ErrorCode errorCode = (reportError ? CompileTimeErrorCode.EXTRA_POSITIONAL_ARGUMENTS : StaticWarningCode.EXTRA_POSITIONAL_ARGUMENTS);
       _resolver.reportErrorForNode(errorCode, argumentList, [unnamedParameterCount, positionalArgumentCount]);
     }
     return resolvedParameters;
@@ -7620,10 +7667,10 @@
     if (shouldReportMissingMember_static || shouldReportMissingMember_propagated) {
       if (staticType.isVoid) {
         if (propertyName.inSetterContext()) {
-          ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
+          ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER);
           _resolver.reportErrorForNode(errorCode, propertyName, [propertyName.name, staticType.displayName]);
         } else if (propertyName.inGetterContext()) {
-          ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
+          ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER);
           _resolver.reportErrorForNode(errorCode, propertyName, [propertyName.name, staticType.displayName]);
         } else {
           _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
@@ -7634,30 +7681,40 @@
         bool isStaticProperty = _isStatic(staticOrPropagatedEnclosingElt);
         if (propertyName.inSetterContext()) {
           if (isStaticProperty) {
-            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
-            _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
-                propertyName.name,
-                staticOrPropagatedEnclosingElt.displayName]);
+            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER);
+            if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+              _resolver.reportErrorForNode(errorCode, propertyName, [
+                  propertyName.name,
+                  staticOrPropagatedEnclosingElt.displayName]);
+            }
           } else {
-            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER) as ErrorCode;
-            _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
-                propertyName.name,
-                staticOrPropagatedEnclosingElt.displayName]);
+            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_SETTER : HintCode.UNDEFINED_SETTER);
+            if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+              _resolver.reportErrorForNode(errorCode, propertyName, [
+                  propertyName.name,
+                  staticOrPropagatedEnclosingElt.displayName]);
+            }
           }
         } else if (propertyName.inGetterContext()) {
           if (isStaticProperty) {
-            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
-            _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
-                propertyName.name,
-                staticOrPropagatedEnclosingElt.displayName]);
+            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER);
+            if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+              _resolver.reportErrorForNode(errorCode, propertyName, [
+                  propertyName.name,
+                  staticOrPropagatedEnclosingElt.displayName]);
+            }
           } else {
-            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER) as ErrorCode;
-            _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, errorCode, propertyName, [
-                propertyName.name,
-                staticOrPropagatedEnclosingElt.displayName]);
+            ErrorCode errorCode = (shouldReportMissingMember_static ? StaticTypeWarningCode.UNDEFINED_GETTER : HintCode.UNDEFINED_GETTER);
+            if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+              _resolver.reportErrorForNode(errorCode, propertyName, [
+                  propertyName.name,
+                  staticOrPropagatedEnclosingElt.displayName]);
+            }
           }
         } else {
-          _resolver.reportProxyConditionalErrorForNode(staticOrPropagatedEnclosingElt, StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
+          if (_doesClassElementHaveProxy(staticOrPropagatedEnclosingElt)) {
+            _resolver.reportErrorForNode(StaticWarningCode.UNDEFINED_IDENTIFIER, propertyName, [propertyName.name]);
+          }
         }
       }
     }
@@ -7959,11 +8016,6 @@
   void _resolveReferences(AstNode node, Scope scope) {
     ResolverVisitor visitor = new ResolverVisitor.con3(_definingLibrary, _source, _typeProvider, scope, _errorListener);
     node.accept(visitor);
-    for (ProxyConditionalAnalysisError conditionalCode in visitor.proxyConditionalAnalysisErrors) {
-      if (conditionalCode.shouldIncludeErrorCode()) {
-        visitor.reportError(conditionalCode.analysisError);
-      }
-    }
   }
 
   void _resolveTypes(AstNode node, Scope scope) {
@@ -7984,12 +8036,20 @@
 class InheritanceManager {
   /**
    * Given some array of [ExecutableElement]s, this method creates a synthetic element as
-   * described in the Superinterfaces section of Inheritance and Overriding.
+   * described in 8.1.1:
    *
-   * TODO (jwren) Copy contents from the Spec into this javadoc.
+   * Let <i>numberOfPositionals</i>(<i>f</i>) denote the number of positional parameters of a
+   * function <i>f</i>, and let <i>numberOfRequiredParams</i>(<i>f</i>) denote the number of
+   * required parameters of a function <i>f</i>. Furthermore, let <i>s</i> denote the set of all
+   * named parameters of the <i>m<sub>1</sub>, &hellip;, m<sub>k</sub></i>. Then let
+   * * <i>h = max(numberOfPositionals(m<sub>i</sub>)),</i>
+   * * <i>r = min(numberOfRequiredParams(m<sub>i</sub>)), for all <i>i</i>, 1 <= i <= k.</i>
+   * If <i>r <= h</i> then <i>I</i> has a method named <i>n</i>, with <i>r</i> required parameters
+   * of type <b>dynamic</b>, <i>h</i> positional parameters of type <b>dynamic</b>, named parameters
+   * <i>s</i> of type <b>dynamic</b> and return type <b>dynamic</b>.
    *
    * TODO (jwren) Associate a propagated type to the synthetic method element using least upper
-   * bound calls
+   * bounds instead of dynamic
    */
   static ExecutableElement _computeMergedExecutableElement(List<ExecutableElement> elementArrayToMerge) {
     int h = _getNumOfPositionalParameters(elementArrayToMerge[0]);
@@ -8633,9 +8693,9 @@
     //
     // Loop through the entries in the unionMap, adding them to the resultMap appropriately.
     //
-    for (MapIterator<String, List<ExecutableElement>> iter = SingleMapIterator.forMap(unionMap); iter.moveNext();) {
-      String key = iter.key;
-      List<ExecutableElement> list = iter.value;
+    for (MapEntry<String, List<ExecutableElement>> entry in getMapEntrySet(unionMap)) {
+      String key = entry.getKey();
+      List<ExecutableElement> list = entry.getValue();
       int numOfEltsWithMatchingNames = list.length;
       if (numOfEltsWithMatchingNames == 1) {
         //
@@ -10126,11 +10186,6 @@
         ast.accept(new VariableResolverVisitor.con1(library, source, _typeProvider));
         ResolverVisitor visitor = new ResolverVisitor.con1(library, source, _typeProvider);
         ast.accept(visitor);
-        for (ProxyConditionalAnalysisError conditionalCode in visitor.proxyConditionalAnalysisErrors) {
-          if (conditionalCode.shouldIncludeErrorCode()) {
-            visitor.reportError(conditionalCode.analysisError);
-          }
-        }
       }
     } finally {
       timeCounter.stop();
@@ -10567,11 +10622,6 @@
         ast.accept(new VariableResolverVisitor.con3(library, source, _typeProvider));
         ResolverVisitor visitor = new ResolverVisitor.con4(library, source, _typeProvider);
         ast.accept(visitor);
-        for (ProxyConditionalAnalysisError conditionalCode in visitor.proxyConditionalAnalysisErrors) {
-          if (conditionalCode.shouldIncludeErrorCode()) {
-            visitor.reportError(conditionalCode.analysisError);
-          }
-        }
       }
     } finally {
       timeCounter.stop();
@@ -10764,49 +10814,6 @@
 }
 
 /**
- * This class is a wrapper for an [AnalysisError] which can also be queried after resolution
- * to find out if the error should actually be reported. In this case, these errors are conditional
- * on the non-existence of an `@proxy` annotation.
- *
- * If we have other conditional error codes in the future, we should have this class implement some
- * ConditionalErrorCode so that after resolution, a list of ConditionalErrorCode can be visited
- * instead of multiple lists of *ConditionalErrorCodes.
- */
-class ProxyConditionalAnalysisError {
-  /**
-   * The enclosing [ClassElement], this is what will determine if the error code should, or
-   * should not, be generated on the source.
-   */
-  final Element _enclosingElement;
-
-  /**
-   * The conditional analysis error.
-   */
-  final AnalysisError analysisError;
-
-  /**
-   * Instantiate a new [ProxyConditionalAnalysisError] with some enclosing element and the
-   * conditional analysis error.
-   *
-   * @param enclosingElement the enclosing element
-   * @param analysisError the conditional analysis error
-   */
-  ProxyConditionalAnalysisError(this._enclosingElement, this.analysisError);
-
-  /**
-   * Return `true` iff the enclosing class has the proxy annotation.
-   *
-   * @return `true` iff the enclosing class has the proxy annotation
-   */
-  bool shouldIncludeErrorCode() {
-    if (_enclosingElement is ClassElement) {
-      return !(_enclosingElement as ClassElement).isOrInheritsProxy;
-    }
-    return true;
-  }
-}
-
-/**
  * Instances of the class `Library` represent the data about a single library during the
  * resolution of some (possibly different) library. They are not intended to be used except during
  * the resolution process.
@@ -11119,11 +11126,6 @@
   TypePromotionManager _promoteManager = new TypePromotionManager();
 
   /**
-   * Proxy conditional error codes.
-   */
-  List<ProxyConditionalAnalysisError> _proxyConditionalAnalysisErrors = new List<ProxyConditionalAnalysisError>();
-
-  /**
    * Initialize a newly created visitor to resolve the nodes in a compilation unit.
    *
    * @param library the library containing the compilation unit being resolved
@@ -11195,8 +11197,6 @@
    */
   TypePromotionManager get promoteManager => _promoteManager;
 
-  List<ProxyConditionalAnalysisError> get proxyConditionalAnalysisErrors => _proxyConditionalAnalysisErrors;
-
   @override
   Object visitAsExpression(AsExpression node) {
     super.visitAsExpression(node);
@@ -11885,43 +11885,6 @@
     }
   }
 
-  /**
-   * Report a conditional analysis error with the given error code and arguments.
-   *
-   * @param enclosingElement the enclosing element
-   * @param errorCode the error code of the error to be reported
-   * @param node the node specifying the location of the error
-   * @param arguments the arguments to the error, used to compose the error message
-   */
-  void reportProxyConditionalErrorForNode(Element enclosingElement, ErrorCode errorCode, AstNode node, List<Object> arguments) {
-    _proxyConditionalAnalysisErrors.add(new ProxyConditionalAnalysisError(enclosingElement, new AnalysisError.con2(source, node.offset, node.length, errorCode, arguments)));
-  }
-
-  /**
-   * Report a conditional analysis error with the given error code and arguments.
-   *
-   * @param enclosingElement the enclosing element
-   * @param errorCode the error code of the error to be reported
-   * @param offset the offset of the location of the error
-   * @param length the length of the location of the error
-   * @param arguments the arguments to the error, used to compose the error message
-   */
-  void reportProxyConditionalErrorForOffset(Element enclosingElement, ErrorCode errorCode, int offset, int length, List<Object> arguments) {
-    _proxyConditionalAnalysisErrors.add(new ProxyConditionalAnalysisError(enclosingElement, new AnalysisError.con2(source, offset, length, errorCode, arguments)));
-  }
-
-  /**
-   * Report a conditional analysis error with the given error code and arguments.
-   *
-   * @param enclosingElement the enclosing element
-   * @param errorCode the error code of the error to be reported
-   * @param token the token specifying the location of the error
-   * @param arguments the arguments to the error, used to compose the error message
-   */
-  void reportProxyConditionalErrorForToken(Element enclosingElement, ErrorCode errorCode, sc.Token token, List<Object> arguments) {
-    _proxyConditionalAnalysisErrors.add(new ProxyConditionalAnalysisError(enclosingElement, new AnalysisError.con2(source, token.offset, token.length, errorCode, arguments)));
-  }
-
   @override
   void visitForEachStatementInScope(ForEachStatement node) {
     //
@@ -12519,15 +12482,6 @@
     return _nameScope;
   }
 
-  /**
-   * Report an error with the given analysis error.
-   *
-   * @param errorCode analysis error
-   */
-  void reportError(AnalysisError analysisError) {
-    _errorListener.onError(analysisError);
-  }
-
   @override
   Object visitBlock(Block node) {
     Scope outerScope = _nameScope;
@@ -13592,36 +13546,17 @@
       }
     }
     _recordStaticType(node, _typeProvider.listType.substitute4(<DartType> [staticType]));
-    NodeList<Expression> elements = node.elements;
-    int count = elements.length;
-    if (count > 0) {
-      DartType propagatedType = elements[0].bestType;
-      for (int i = 1; i < count; i++) {
-        DartType elementType = elements[i].bestType;
-        if (propagatedType != elementType) {
-          propagatedType = _dynamicType;
-        } else {
-          propagatedType = propagatedType.getLeastUpperBound(elementType);
-          if (propagatedType == null) {
-            propagatedType = _dynamicType;
-          }
-        }
-      }
-      if (propagatedType.isMoreSpecificThan(staticType)) {
-        _recordPropagatedType(node, _typeProvider.listType.substitute4(<DartType> [propagatedType]));
-      }
-    }
     return null;
   }
 
   /**
    * The Dart Language Specification, 12.7: <blockquote>The static type of a map literal of the form
-   * <i><b>const</b> &lt;String, V&gt; {k<sub>1</sub>:e<sub>1</sub>, &hellip;,
-   * k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i>&lt;String, V&gt; {k<sub>1</sub>:e<sub>1</sub>,
-   * &hellip;, k<sub>n</sub>:e<sub>n</sub>}</i> is `Map&lt;String, V&gt;`. The static type a
-   * map literal of the form <i><b>const</b> {k<sub>1</sub>:e<sub>1</sub>, &hellip;,
+   * <i><b>const</b> &lt;K, V&gt; {k<sub>1</sub>:e<sub>1</sub>, &hellip;,
+   * k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i>&lt;K, V&gt; {k<sub>1</sub>:e<sub>1</sub>,
+   * &hellip;, k<sub>n</sub>:e<sub>n</sub>}</i> is `Map&lt;K, V&gt;`. The static type a map
+   * literal of the form <i><b>const</b> {k<sub>1</sub>:e<sub>1</sub>, &hellip;,
    * k<sub>n</sub>:e<sub>n</sub>}</i> or the form <i>{k<sub>1</sub>:e<sub>1</sub>, &hellip;,
-   * k<sub>n</sub>:e<sub>n</sub>}</i> is `Map&lt;String, dynamic&gt;`.
+   * k<sub>n</sub>:e<sub>n</sub>}</i> is `Map&lt;dynamic, dynamic&gt;`.
    *
    * It is a compile-time error if the first type argument to a map literal is not
    * <i>String</i>.</blockquote>
@@ -13647,45 +13582,6 @@
       }
     }
     _recordStaticType(node, _typeProvider.mapType.substitute4(<DartType> [staticKeyType, staticValueType]));
-    NodeList<MapLiteralEntry> entries = node.entries;
-    int count = entries.length;
-    if (count > 0) {
-      MapLiteralEntry entry = entries[0];
-      DartType propagatedKeyType = entry.key.bestType;
-      DartType propagatedValueType = entry.value.bestType;
-      for (int i = 1; i < count; i++) {
-        entry = entries[i];
-        DartType elementKeyType = entry.key.bestType;
-        if (propagatedKeyType != elementKeyType) {
-          propagatedKeyType = _dynamicType;
-        } else {
-          propagatedKeyType = propagatedKeyType.getLeastUpperBound(elementKeyType);
-          if (propagatedKeyType == null) {
-            propagatedKeyType = _dynamicType;
-          }
-        }
-        DartType elementValueType = entry.value.bestType;
-        if (propagatedValueType != elementValueType) {
-          propagatedValueType = _dynamicType;
-        } else {
-          propagatedValueType = propagatedValueType.getLeastUpperBound(elementValueType);
-          if (propagatedValueType == null) {
-            propagatedValueType = _dynamicType;
-          }
-        }
-      }
-      bool betterKey = propagatedKeyType != null && propagatedKeyType.isMoreSpecificThan(staticKeyType);
-      bool betterValue = propagatedValueType != null && propagatedValueType.isMoreSpecificThan(staticValueType);
-      if (betterKey || betterValue) {
-        if (!betterKey) {
-          propagatedKeyType = staticKeyType;
-        }
-        if (!betterValue) {
-          propagatedValueType = staticValueType;
-        }
-        _recordPropagatedType(node, _typeProvider.mapType.substitute4(<DartType> [propagatedKeyType, propagatedValueType]));
-      }
-    }
     return null;
   }
 
@@ -14994,8 +14890,8 @@
    * @param overrides the overrides to be applied
    */
   void applyOverrides(Map<Element, DartType> overrides) {
-    for (MapIterator<Element, DartType> iter = SingleMapIterator.forMap(overrides); iter.moveNext();) {
-      _overridenTypes[iter.key] = iter.value;
+    for (MapEntry<Element, DartType> entry in getMapEntrySet(overrides)) {
+      _overridenTypes[entry.getKey()] = entry.getValue();
     }
   }
 
@@ -15588,6 +15484,33 @@
   }
 
   @override
+  Object visitAnnotation(Annotation node) {
+    //
+    // Visit annotations, if the annotation is @proxy, on a class, and "proxy" resolves to the proxy
+    // annotation in dart.core, then create create the ElementAnnotationImpl and set it as the
+    // metadata on the enclosing class.
+    //
+    // Element resolution is done in the ElementResolver, and this work will be done in the general
+    // case for all annotations in the ElementResolver. The reason we resolve this particular
+    // element early is so that ClassElement.isProxy() returns the correct information during all
+    // phases of the ElementResolver.
+    //
+    super.visitAnnotation(node);
+    Identifier identifier = node.name;
+    if (identifier.name.endsWith(ElementAnnotationImpl.PROXY_VARIABLE_NAME) && node.parent is ClassDeclaration) {
+      Element element = nameScope.lookup(identifier, definingLibrary);
+      if (element != null && element.library.isDartCore && element is PropertyAccessorElement) {
+        // This is the @proxy from dart.core
+        ClassDeclaration classDeclaration = node.parent as ClassDeclaration;
+        ElementAnnotationImpl elementAnnotation = new ElementAnnotationImpl(element);
+        node.elementAnnotation = elementAnnotation;
+        (classDeclaration.element as ClassElementImpl).metadata = <ElementAnnotationImpl> [elementAnnotation];
+      }
+    }
+    return null;
+  }
+
+  @override
   Object visitCatchClause(CatchClause node) {
     super.visitCatchClause(node);
     SimpleIdentifier exception = node.exceptionParameter;
@@ -15922,7 +15845,7 @@
       } else if (_isTypeNameInIsExpression(node)) {
         reportErrorForNode(StaticWarningCode.TYPE_TEST_NON_TYPE, typeName, [typeName.name]);
       } else if ((redirectingConstructorKind = _getRedirectingConstructorKind(node)) != null) {
-        ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS) as ErrorCode;
+        ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS);
         reportErrorForNode(errorCode, typeName, [typeName.name]);
       } else if (_isTypeNameInTypeArgumentList(node)) {
         reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, typeName, [typeName.name]);
@@ -15969,7 +15892,7 @@
       } else if (_isTypeNameInIsExpression(node)) {
         reportErrorForNode(StaticWarningCode.TYPE_TEST_NON_TYPE, typeName, [typeName.name]);
       } else if ((redirectingConstructorKind = _getRedirectingConstructorKind(node)) != null) {
-        ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS) as ErrorCode;
+        ErrorCode errorCode = (redirectingConstructorKind == RedirectingConstructorKind.CONST ? CompileTimeErrorCode.REDIRECT_TO_NON_CLASS : StaticWarningCode.REDIRECT_TO_NON_CLASS);
         reportErrorForNode(errorCode, typeName, [typeName.name]);
       } else if (_isTypeNameInTypeArgumentList(node)) {
         reportErrorForNode(StaticTypeWarningCode.NON_TYPE_AS_TYPE_ARGUMENT, typeName, [typeName.name]);
@@ -17345,8 +17268,8 @@
    * @param namespace the namespace containing the names to be added to this namespace
    */
   void _addAllFromMap(Map<String, Element> definedNames, Map<String, Element> newNames) {
-    for (MapIterator<String, Element> iter = SingleMapIterator.forMap(newNames); iter.moveNext();) {
-      definedNames[iter.key] = iter.value;
+    for (MapEntry<String, Element> entry in getMapEntrySet(newNames)) {
+      definedNames[entry.getKey()] = entry.getValue();
     }
   }
 
@@ -17428,8 +17351,8 @@
     if (prefixElement != null) {
       String prefix = prefixElement.name;
       Map<String, Element> newNames = new Map<String, Element>();
-      for (MapIterator<String, Element> iter = SingleMapIterator.forMap(definedNames); iter.moveNext();) {
-        newNames["${prefix}.${iter.key}"] = iter.value;
+      for (MapEntry<String, Element> entry in getMapEntrySet(definedNames)) {
+        newNames["${prefix}.${entry.getKey()}"] = entry.getValue();
       }
       return newNames;
     } else {
@@ -19219,9 +19142,9 @@
       }
     }
     // Visit all of the states in the map to ensure that none were never initialized.
-    for (MapIterator<FieldElement, INIT_STATE> iter = SingleMapIterator.forMap(fieldElementsMap); iter.moveNext();) {
-      if (iter.value == INIT_STATE.NOT_INIT) {
-        FieldElement fieldElement = iter.key;
+    for (MapEntry<FieldElement, INIT_STATE> entry in getMapEntrySet(fieldElementsMap)) {
+      if (entry.getValue() == INIT_STATE.NOT_INIT) {
+        FieldElement fieldElement = entry.getKey();
         if (fieldElement.isConst) {
           _errorReporter.reportErrorForNode(CompileTimeErrorCode.CONST_NOT_INITIALIZED, node.returnType, [fieldElement.name]);
           foundError = true;
@@ -19395,21 +19318,22 @@
       parameterIndex++;
     }
     // SWC.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE & SWC.INVALID_OVERRIDE_DIFFERENT_DEFAULT_VALUES
-    MapIterator<String, DartType> overriddenNamedPTIterator = SingleMapIterator.forMap(overriddenNamedPT);
-    while (overriddenNamedPTIterator.moveNext()) {
-      DartType overridingType = overridingNamedPT[overriddenNamedPTIterator.key];
+    JavaIterator<MapEntry<String, DartType>> overriddenNamedPTIterator = new JavaIterator(getMapEntrySet(overriddenNamedPT));
+    while (overriddenNamedPTIterator.hasNext) {
+      MapEntry<String, DartType> overriddenNamedPTEntry = overriddenNamedPTIterator.next();
+      DartType overridingType = overridingNamedPT[overriddenNamedPTEntry.getKey()];
       if (overridingType == null) {
         // Error, this is never reached- INVALID_OVERRIDE_NAMED would have been created above if
         // this could be reached.
         continue;
       }
-      if (!overriddenNamedPTIterator.value.isAssignableTo(overridingType)) {
+      if (!overriddenNamedPTEntry.getValue().isAssignableTo(overridingType)) {
         // lookup the parameter for the error to select
         ParameterElement parameterToSelect = null;
         AstNode parameterLocationToSelect = null;
         for (int i = 0; i < parameters.length; i++) {
           ParameterElement parameter = parameters[i];
-          if (parameter.parameterKind == ParameterKind.NAMED && overriddenNamedPTIterator.key == parameter.name) {
+          if (parameter.parameterKind == ParameterKind.NAMED && overriddenNamedPTEntry.getKey() == parameter.name) {
             parameterToSelect = parameter;
             parameterLocationToSelect = parameterLocations[i];
             break;
@@ -19418,7 +19342,7 @@
         if (parameterToSelect != null) {
           _errorReporter.reportErrorForNode(StaticWarningCode.INVALID_METHOD_OVERRIDE_NAMED_PARAM_TYPE, parameterLocationToSelect, [
               overridingType.displayName,
-              overriddenNamedPTIterator.value.displayName,
+              overriddenNamedPTEntry.getValue().displayName,
               overriddenExecutable.enclosingElement.displayName]);
           return true;
         }
@@ -19667,7 +19591,7 @@
         if (redirectedConstructor.name != null) {
           constructorStrName += ".${redirectedConstructor.name.name}";
         }
-        ErrorCode errorCode = (node.constKeyword != null ? CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR : StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR) as ErrorCode;
+        ErrorCode errorCode = (node.constKeyword != null ? CompileTimeErrorCode.REDIRECT_TO_MISSING_CONSTRUCTOR : StaticWarningCode.REDIRECT_TO_MISSING_CONSTRUCTOR);
         _errorReporter.reportErrorForNode(errorCode, redirectedConstructor, [constructorStrName, redirectedType.displayName]);
         return true;
       }
@@ -19756,9 +19680,9 @@
     // check exported names
     Namespace namespace = new NamespaceBuilder().createExportNamespaceForDirective(exportElement);
     Map<String, Element> definedNames = namespace.definedNames;
-    for (MapIterator<String, Element> iter = SingleMapIterator.forMap(definedNames); iter.moveNext();) {
-      String name = iter.key;
-      Element element = iter.value;
+    for (MapEntry<String, Element> definedEntry in getMapEntrySet(definedNames)) {
+      String name = definedEntry.getKey();
+      Element element = definedEntry.getValue();
       Element prevElement = _exportedElements[name];
       if (element != null && prevElement != null && prevElement != element) {
         _errorReporter.reportErrorForNode(CompileTimeErrorCode.AMBIGUOUS_EXPORT, node, [
@@ -22816,6 +22740,10 @@
     if (!classElement.type.isSubtypeOf(_typeProvider.functionType)) {
       return false;
     }
+    // If there is a noSuchMethod method, then don't report the warning, see dartbug.com/16078
+    if (classElement.getMethod(ElementResolver.NO_SUCH_METHOD_METHOD_NAME) != null) {
+      return false;
+    }
     ExecutableElement callMethod = _inheritanceManager.lookupMember(classElement, "call");
     if (callMethod == null || callMethod is! MethodElement || (callMethod as MethodElement).isAbstract) {
       _errorReporter.reportErrorForNode(StaticWarningCode.FUNCTION_WITHOUT_CALL, node.name, []);
diff --git a/pkg/analyzer/lib/src/generated/sdk_io.dart b/pkg/analyzer/lib/src/generated/sdk_io.dart
index d09350b..fc67528 100644
--- a/pkg/analyzer/lib/src/generated/sdk_io.dart
+++ b/pkg/analyzer/lib/src/generated/sdk_io.dart
@@ -88,11 +88,6 @@
   static String _BIN_DIRECTORY_NAME = "bin";
 
   /**
-   * The name of the directory on Mac that contains dartium.
-   */
-  static String _DARTIUM_DIRECTORY_NAME_MAC = "Chromium.app";
-
-  /**
    * The name of the directory on non-Mac that contains dartium.
    */
   static String _DARTIUM_DIRECTORY_NAME = "chromium";
@@ -232,21 +227,25 @@
   DirectoryBasedDartSdk(JavaFile sdkDirectory, [bool useDart2jsPaths = false]) {
     this._sdkDirectory = sdkDirectory.getAbsoluteFile();
     _libraryMap = initialLibraryMap(useDart2jsPaths);
-    _analysisContext = new AnalysisContextImpl();
-    _analysisContext.sourceFactory = new SourceFactory([new DartUriResolver(this)]);
-    List<String> uris = this.uris;
-    ChangeSet changeSet = new ChangeSet();
-    for (String uri in uris) {
-      changeSet.addedSource(_analysisContext.sourceFactory.forUri(uri));
-    }
-    _analysisContext.applyChanges(changeSet);
   }
 
   @override
   Source fromEncoding(UriKind kind, Uri uri) => new FileBasedSource.con2(new JavaFile.fromUri(uri), kind);
 
   @override
-  AnalysisContext get context => _analysisContext;
+  AnalysisContext get context {
+    if (_analysisContext == null) {
+      _analysisContext = new AnalysisContextImpl();
+      _analysisContext.sourceFactory = new SourceFactory([new DartUriResolver(this)]);
+      List<String> uris = this.uris;
+      ChangeSet changeSet = new ChangeSet();
+      for (String uri in uris) {
+        changeSet.addedSource(_analysisContext.sourceFactory.forUri(uri));
+      }
+      _analysisContext.applyChanges(changeSet);
+    }
+    return _analysisContext;
+  }
 
   /**
    * Return the file containing the dart2js executable, or `null` if it does not exist.
diff --git a/pkg/analyzer/lib/src/generated/source_io.dart b/pkg/analyzer/lib/src/generated/source_io.dart
index 0be26ec..554985d 100644
--- a/pkg/analyzer/lib/src/generated/source_io.dart
+++ b/pkg/analyzer/lib/src/generated/source_io.dart
@@ -168,6 +168,105 @@
 }
 
 /**
+ * An implementation of an non-existing [Source].
+ */
+class NonExistingSource implements Source {
+  final String _name;
+
+  final UriKind uriKind;
+
+  NonExistingSource(this._name, this.uriKind);
+
+  @override
+  bool exists() => false;
+
+  @override
+  TimestampedData<String> get contents {
+    throw new UnsupportedOperationException("${_name}does not exist.");
+  }
+
+  @override
+  String get encoding {
+    throw new UnsupportedOperationException("${_name}does not exist.");
+  }
+
+  @override
+  String get fullName => _name;
+
+  @override
+  int get modificationStamp => 0;
+
+  @override
+  String get shortName => _name;
+
+  @override
+  bool get isInSystemLibrary => false;
+
+  @override
+  Source resolveRelative(Uri relativeUri) {
+    throw new UnsupportedOperationException("${_name}does not exist.");
+  }
+}
+
+/**
+ * Instances of the class `RelativeFileUriResolver` resolve `file` URI's.
+ */
+class RelativeFileUriResolver extends UriResolver {
+  /**
+   * The name of the `file` scheme.
+   */
+  static String FILE_SCHEME = "file";
+
+  /**
+   * Return `true` if the given URI is a `file` URI.
+   *
+   * @param uri the URI being tested
+   * @return `true` if the given URI is a `file` URI
+   */
+  static bool isFileUri(Uri uri) => uri.scheme == FILE_SCHEME;
+
+  /**
+   * The directories for the relatvie URI's
+   */
+  final List<JavaFile> _relativeDirectories;
+
+  /**
+   * The root directory for all the source trees
+   */
+  final JavaFile _rootDirectory;
+
+  /**
+   * Initialize a newly created resolver to resolve `file` URI's relative to the given root
+   * directory.
+   */
+  RelativeFileUriResolver(this._rootDirectory, this._relativeDirectories) : super();
+
+  @override
+  Source fromEncoding(UriKind kind, Uri uri) {
+    if (kind == UriKind.FILE_URI) {
+      return new FileBasedSource.con2(new JavaFile.fromUri(uri), kind);
+    }
+    return null;
+  }
+
+  @override
+  Source resolveAbsolute(Uri uri) {
+    String rootPath = _rootDirectory.toURI().path;
+    String uriPath = uri.path;
+    if (uriPath != null && uriPath.startsWith(rootPath)) {
+      String filePath = uri.path.substring(rootPath.length);
+      for (JavaFile dir in _relativeDirectories) {
+        JavaFile file = new JavaFile.relative(dir, filePath);
+        if (file.exists()) {
+          return new FileBasedSource.con2(file, UriKind.FILE_URI);
+        }
+      }
+    }
+    return null;
+  }
+}
+
+/**
  * Instances of the class `PackageUriResolver` resolve `package` URI's in the context of
  * an application.
  *
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index b11960a..b4e0daf 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
 name: analyzer
-version: 0.13.5
+version: 0.13.6
 author: Dart Team <misc@dartlang.org>
 description: Static analyzer for Dart.
 homepage: http://www.dartlang.org
diff --git a/pkg/analyzer/test/generated/ast_test.dart b/pkg/analyzer/test/generated/ast_test.dart
index 5a478f4..a01561c 100644
--- a/pkg/analyzer/test/generated/ast_test.dart
+++ b/pkg/analyzer/test/generated/ast_test.dart
@@ -369,7 +369,7 @@
 
   static CatchClause catchClause4(TypeName exceptionType, String exceptionParameter, List<Statement> statements) => catchClause5(exceptionType, exceptionParameter, null, statements);
 
-  static CatchClause catchClause5(TypeName exceptionType, String exceptionParameter, String stackTraceParameter, List<Statement> statements) => new CatchClause(exceptionType == null ? null : TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "on"), exceptionType, exceptionParameter == null ? null : TokenFactory.tokenFromKeyword(Keyword.CATCH), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.OPEN_PAREN), identifier3(exceptionParameter), stackTraceParameter == null ? null : TokenFactory.tokenFromType(TokenType.COMMA), stackTraceParameter == null ? null : identifier3(stackTraceParameter), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), block(statements));
+  static CatchClause catchClause5(TypeName exceptionType, String exceptionParameter, String stackTraceParameter, List<Statement> statements) => new CatchClause(exceptionType == null ? null : TokenFactory.tokenFromTypeAndString(TokenType.IDENTIFIER, "on"), exceptionType, exceptionParameter == null ? null : TokenFactory.tokenFromKeyword(Keyword.CATCH), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.OPEN_PAREN), exceptionParameter == null ? null : identifier3(exceptionParameter), stackTraceParameter == null ? null : TokenFactory.tokenFromType(TokenType.COMMA), stackTraceParameter == null ? null : identifier3(stackTraceParameter), exceptionParameter == null ? null : TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), block(statements));
 
   static ClassDeclaration classDeclaration(Keyword abstractKeyword, String name, TypeParameterList typeParameters, ExtendsClause extendsClause, WithClause withClause, ImplementsClause implementsClause, List<ClassMember> members) => new ClassDeclaration(null, null, abstractKeyword == null ? null : TokenFactory.tokenFromKeyword(abstractKeyword), TokenFactory.tokenFromKeyword(Keyword.CLASS), identifier3(name), typeParameters, extendsClause, withClause, implementsClause, TokenFactory.tokenFromType(TokenType.OPEN_CURLY_BRACKET), list(members), TokenFactory.tokenFromType(TokenType.CLOSE_CURLY_BRACKET));
 
@@ -2408,10 +2408,14 @@
     _assertSource("A this.a", AstFactory.fieldFormalParameter(null, AstFactory.typeName4("A", []), "a"));
   }
 
-  void test_visitForEachStatement() {
+  void test_visitForEachStatement_declared() {
     _assertSource("for (a in b) {}", AstFactory.forEachStatement(AstFactory.declaredIdentifier3("a"), AstFactory.identifier3("b"), AstFactory.block([])));
   }
 
+  void test_visitForEachStatement_variable() {
+    _assertSource("for (a in b) {}", new ForEachStatement.con2(TokenFactory.tokenFromKeyword(Keyword.FOR), TokenFactory.tokenFromType(TokenType.OPEN_PAREN), AstFactory.identifier3("a"), TokenFactory.tokenFromKeyword(Keyword.IN), AstFactory.identifier3("b"), TokenFactory.tokenFromType(TokenType.CLOSE_PAREN), AstFactory.block([])));
+  }
+
   void test_visitFormalParameterList_empty() {
     _assertSource("()", AstFactory.formalParameterList([]));
   }
@@ -3566,9 +3570,13 @@
         final __test = new ToSourceVisitorTest();
         runJUnitTest(__test, __test.test_visitFieldFormalParameter_type);
       });
-      _ut.test('test_visitForEachStatement', () {
+      _ut.test('test_visitForEachStatement_declared', () {
         final __test = new ToSourceVisitorTest();
-        runJUnitTest(__test, __test.test_visitForEachStatement);
+        runJUnitTest(__test, __test.test_visitForEachStatement_declared);
+      });
+      _ut.test('test_visitForEachStatement_variable', () {
+        final __test = new ToSourceVisitorTest();
+        runJUnitTest(__test, __test.test_visitForEachStatement_variable);
       });
       _ut.test('test_visitForStatement_c', () {
         final __test = new ToSourceVisitorTest();
diff --git a/pkg/analyzer/test/generated/element_test.dart b/pkg/analyzer/test/generated/element_test.dart
index 8f4db1f..a8babb9 100644
--- a/pkg/analyzer/test/generated/element_test.dart
+++ b/pkg/analyzer/test/generated/element_test.dart
@@ -703,6 +703,89 @@
     JUnitTestCase.assertEquals(3, InterfaceTypeImpl.computeLongestInheritancePathToObject(classC.type));
   }
 
+  void test_computeSuperinterfaceSet_genericInterfacePath() {
+    //
+    //  A
+    //  | implements
+    //  B<T>
+    //  | implements
+    //  C<T>
+    //
+    //  D
+    //
+    ClassElementImpl classA = ElementFactory.classElement2("A", []);
+    ClassElementImpl classB = ElementFactory.classElement2("B", ["T"]);
+    ClassElementImpl classC = ElementFactory.classElement2("C", ["T"]);
+    ClassElement classD = ElementFactory.classElement2("D", []);
+    InterfaceType typeA = classA.type;
+    classB.interfaces = <InterfaceType> [typeA];
+    InterfaceTypeImpl typeBT = new InterfaceTypeImpl.con1(classB);
+    DartType typeT = classC.type.typeArguments[0];
+    typeBT.typeArguments = <DartType> [typeT];
+    classC.interfaces = <InterfaceType> [typeBT];
+    // A
+    Set<InterfaceType> superinterfacesOfA = InterfaceTypeImpl.computeSuperinterfaceSet(typeA);
+    EngineTestCase.assertSizeOfSet(1, superinterfacesOfA);
+    InterfaceType typeObject = ElementFactory.object.type;
+    JUnitTestCase.assertTrue(superinterfacesOfA.contains(typeObject));
+    // B<D>
+    InterfaceTypeImpl typeBD = new InterfaceTypeImpl.con1(classB);
+    typeBD.typeArguments = <DartType> [classD.type];
+    Set<InterfaceType> superinterfacesOfBD = InterfaceTypeImpl.computeSuperinterfaceSet(typeBD);
+    EngineTestCase.assertSizeOfSet(2, superinterfacesOfBD);
+    JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeObject));
+    JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeA));
+    // C<D>
+    InterfaceTypeImpl typeCD = new InterfaceTypeImpl.con1(classC);
+    typeCD.typeArguments = <DartType> [classD.type];
+    Set<InterfaceType> superinterfacesOfCD = InterfaceTypeImpl.computeSuperinterfaceSet(typeCD);
+    EngineTestCase.assertSizeOfSet(3, superinterfacesOfCD);
+    JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeObject));
+    JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeA));
+    JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeBD));
+  }
+
+  void test_computeSuperinterfaceSet_genericSuperclassPath() {
+    //
+    //  A
+    //  |
+    //  B<T>
+    //  |
+    //  C<T>
+    //
+    //  D
+    //
+    ClassElement classA = ElementFactory.classElement2("A", []);
+    InterfaceType typeA = classA.type;
+    ClassElement classB = ElementFactory.classElement("B", typeA, ["T"]);
+    ClassElementImpl classC = ElementFactory.classElement2("C", ["T"]);
+    InterfaceTypeImpl typeBT = new InterfaceTypeImpl.con1(classB);
+    DartType typeT = classC.type.typeArguments[0];
+    typeBT.typeArguments = <DartType> [typeT];
+    classC.supertype = typeBT;
+    ClassElement classD = ElementFactory.classElement2("D", []);
+    // A
+    Set<InterfaceType> superinterfacesOfA = InterfaceTypeImpl.computeSuperinterfaceSet(typeA);
+    EngineTestCase.assertSizeOfSet(1, superinterfacesOfA);
+    InterfaceType typeObject = ElementFactory.object.type;
+    JUnitTestCase.assertTrue(superinterfacesOfA.contains(typeObject));
+    // B<D>
+    InterfaceTypeImpl typeBD = new InterfaceTypeImpl.con1(classB);
+    typeBD.typeArguments = <DartType> [classD.type];
+    Set<InterfaceType> superinterfacesOfBD = InterfaceTypeImpl.computeSuperinterfaceSet(typeBD);
+    EngineTestCase.assertSizeOfSet(2, superinterfacesOfBD);
+    JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeObject));
+    JUnitTestCase.assertTrue(superinterfacesOfBD.contains(typeA));
+    // C<D>
+    InterfaceTypeImpl typeCD = new InterfaceTypeImpl.con1(classC);
+    typeCD.typeArguments = <DartType> [classD.type];
+    Set<InterfaceType> superinterfacesOfCD = InterfaceTypeImpl.computeSuperinterfaceSet(typeCD);
+    EngineTestCase.assertSizeOfSet(3, superinterfacesOfCD);
+    JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeObject));
+    JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeA));
+    JUnitTestCase.assertTrue(superinterfacesOfCD.contains(typeBD));
+  }
+
   void test_computeSuperinterfaceSet_multipleInterfacePaths() {
     ClassElementImpl classA = ElementFactory.classElement2("A", []);
     ClassElementImpl classB = ElementFactory.classElement2("B", []);
@@ -1140,7 +1223,7 @@
     InterfaceType doubleType = _typeProvider.doubleType;
     InterfaceType listOfIntType = listType.substitute4(<DartType> [intType]);
     InterfaceType listOfDoubleType = listType.substitute4(<DartType> [doubleType]);
-    JUnitTestCase.assertEquals(listType.substitute4(<DartType> [_typeProvider.dynamicType]), listOfIntType.getLeastUpperBound(listOfDoubleType));
+    JUnitTestCase.assertEquals(_typeProvider.objectType, listOfIntType.getLeastUpperBound(listOfDoubleType));
   }
 
   void test_getLeastUpperBound_typeParameters_same() {
@@ -1999,6 +2082,14 @@
         final __test = new InterfaceTypeImplTest();
         runJUnitTest(__test, __test.test_computeLongestInheritancePathToObject_singleSuperclassPath);
       });
+      _ut.test('test_computeSuperinterfaceSet_genericInterfacePath', () {
+        final __test = new InterfaceTypeImplTest();
+        runJUnitTest(__test, __test.test_computeSuperinterfaceSet_genericInterfacePath);
+      });
+      _ut.test('test_computeSuperinterfaceSet_genericSuperclassPath', () {
+        final __test = new InterfaceTypeImplTest();
+        runJUnitTest(__test, __test.test_computeSuperinterfaceSet_genericSuperclassPath);
+      });
       _ut.test('test_computeSuperinterfaceSet_multipleInterfacePaths', () {
         final __test = new InterfaceTypeImplTest();
         runJUnitTest(__test, __test.test_computeSuperinterfaceSet_multipleInterfacePaths);
diff --git a/pkg/analyzer/test/generated/parser_test.dart b/pkg/analyzer/test/generated/parser_test.dart
index 3672606..fef7f9d 100644
--- a/pkg/analyzer/test/generated/parser_test.dart
+++ b/pkg/analyzer/test/generated/parser_test.dart
@@ -134,6 +134,34 @@
     JUnitTestCase.assertTrue(literal.isSynthetic);
   }
 
+  void test_function_literal_allowed_at_toplevel() {
+    ParserTestCase.parseCompilationUnit("var x = () {};", []);
+  }
+
+  void test_function_literal_allowed_in_ArgumentList_in_ConstructorFieldInitializer() {
+    ParserTestCase.parseCompilationUnit("class C { C() : a = f(() {}); }", []);
+  }
+
+  void test_function_literal_allowed_in_IndexExpression_in_ConstructorFieldInitializer() {
+    ParserTestCase.parseCompilationUnit("class C { C() : a = x[() {}]; }", []);
+  }
+
+  void test_function_literal_allowed_in_ListLiteral_in_ConstructorFieldInitializer() {
+    ParserTestCase.parseCompilationUnit("class C { C() : a = [() {}]; }", []);
+  }
+
+  void test_function_literal_allowed_in_MapLiteral_in_ConstructorFieldInitializer() {
+    ParserTestCase.parseCompilationUnit("class C { C() : a = {'key': () {}}; }", []);
+  }
+
+  void test_function_literal_allowed_in_ParenthesizedExpression_in_ConstructorFieldInitializer() {
+    ParserTestCase.parseCompilationUnit("class C { C() : a = (() {}); }", []);
+  }
+
+  void test_function_literal_allowed_in_StringInterpolation_in_ConstructorFieldInitializer() {
+    ParserTestCase.parseCompilationUnit("class C { C() : a = \"\${(){}}\"; }", []);
+  }
+
   void test_isFunctionDeclaration_nameButNoReturn_block() {
     JUnitTestCase.assertTrue(_isFunctionDeclaration("f() {}"));
   }
@@ -1138,41 +1166,29 @@
     JUnitTestCase.assertNotNull(constructor.body);
   }
 
-  void test_parseClassTypeAlias() {
-    Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
-    ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B;");
-    JUnitTestCase.assertNotNull(classTypeAlias.keyword);
-    JUnitTestCase.assertEquals("A", classTypeAlias.name.name);
-    JUnitTestCase.assertNotNull(classTypeAlias.equals);
-    JUnitTestCase.assertNull(classTypeAlias.abstractKeyword);
-    JUnitTestCase.assertNotNullMsg("B", classTypeAlias.superclass.name.name);
-    JUnitTestCase.assertNull(classTypeAlias.withClause);
-    JUnitTestCase.assertNull(classTypeAlias.implementsClause);
-    JUnitTestCase.assertNotNull(classTypeAlias.semicolon);
-  }
-
   void test_parseClassTypeAlias_abstract() {
-    Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
-    ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = abstract B;");
+    Token classToken = TokenFactory.tokenFromKeyword(Keyword.CLASS);
+    Token abstractToken = TokenFactory.tokenFromKeyword(Keyword.ABSTRACT);
+    ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), abstractToken, classToken], "A = B with C;");
     JUnitTestCase.assertNotNull(classTypeAlias.keyword);
     JUnitTestCase.assertEquals("A", classTypeAlias.name.name);
     JUnitTestCase.assertNotNull(classTypeAlias.equals);
     JUnitTestCase.assertNotNull(classTypeAlias.abstractKeyword);
     JUnitTestCase.assertNotNullMsg("B", classTypeAlias.superclass.name.name);
-    JUnitTestCase.assertNull(classTypeAlias.withClause);
+    JUnitTestCase.assertNotNull(classTypeAlias.withClause);
     JUnitTestCase.assertNull(classTypeAlias.implementsClause);
     JUnitTestCase.assertNotNull(classTypeAlias.semicolon);
   }
 
   void test_parseClassTypeAlias_implements() {
     Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
-    ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B implements C;");
+    ClassTypeAlias classTypeAlias = ParserTestCase.parse("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B with C implements D;");
     JUnitTestCase.assertNotNull(classTypeAlias.keyword);
     JUnitTestCase.assertEquals("A", classTypeAlias.name.name);
     JUnitTestCase.assertNotNull(classTypeAlias.equals);
     JUnitTestCase.assertNull(classTypeAlias.abstractKeyword);
     JUnitTestCase.assertNotNullMsg("B", classTypeAlias.superclass.name.name);
-    JUnitTestCase.assertNull(classTypeAlias.withClause);
+    JUnitTestCase.assertNotNull(classTypeAlias.withClause);
     JUnitTestCase.assertNotNull(classTypeAlias.implementsClause);
     JUnitTestCase.assertNotNull(classTypeAlias.semicolon);
   }
@@ -1669,7 +1685,7 @@
   }
 
   void test_parseCompilationUnitMember_typeAlias_abstract() {
-    ClassTypeAlias typeAlias = ParserTestCase.parse("parseCompilationUnitMember", <Object> [emptyCommentAndMetadata()], "class C = abstract S with M;");
+    ClassTypeAlias typeAlias = ParserTestCase.parse("parseCompilationUnitMember", <Object> [emptyCommentAndMetadata()], "abstract class C = S with M;");
     JUnitTestCase.assertNotNull(typeAlias.keyword);
     JUnitTestCase.assertEquals("C", typeAlias.name.name);
     JUnitTestCase.assertNull(typeAlias.typeParameters);
@@ -1801,6 +1817,19 @@
   void test_parseConstructor() {
   }
 
+  void test_parseConstructor_with_pseudo_function_literal() {
+    // "(b) {}" should not be misinterpreted as a function literal even though it looks like one.
+    ClassMember classMember = ParserTestCase.parse("parseClassMember", <Object> ["C"], "C() : a = (b) {}");
+    EngineTestCase.assertInstanceOf((obj) => obj is ConstructorDeclaration, ConstructorDeclaration, classMember);
+    ConstructorDeclaration constructor = classMember as ConstructorDeclaration;
+    NodeList<ConstructorInitializer> initializers = constructor.initializers;
+    EngineTestCase.assertSizeOfList(1, initializers);
+    ConstructorInitializer initializer = initializers[0];
+    EngineTestCase.assertInstanceOf((obj) => obj is ConstructorFieldInitializer, ConstructorFieldInitializer, initializer);
+    EngineTestCase.assertInstanceOf((obj) => obj is ParenthesizedExpression, ParenthesizedExpression, (initializer as ConstructorFieldInitializer).expression);
+    EngineTestCase.assertInstanceOf((obj) => obj is BlockFunctionBody, BlockFunctionBody, constructor.body);
+  }
+
   void test_parseConstructorFieldInitializer_qualified() {
     ConstructorFieldInitializer invocation = ParserTestCase.parse4("parseConstructorFieldInitializer", "this.a = b", []);
     JUnitTestCase.assertNotNull(invocation.equals);
@@ -4700,6 +4729,34 @@
         final __test = new SimpleParserTest();
         runJUnitTest(__test, __test.test_createSyntheticStringLiteral);
       });
+      _ut.test('test_function_literal_allowed_at_toplevel', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_function_literal_allowed_at_toplevel);
+      });
+      _ut.test('test_function_literal_allowed_in_ArgumentList_in_ConstructorFieldInitializer', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_function_literal_allowed_in_ArgumentList_in_ConstructorFieldInitializer);
+      });
+      _ut.test('test_function_literal_allowed_in_IndexExpression_in_ConstructorFieldInitializer', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_function_literal_allowed_in_IndexExpression_in_ConstructorFieldInitializer);
+      });
+      _ut.test('test_function_literal_allowed_in_ListLiteral_in_ConstructorFieldInitializer', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_function_literal_allowed_in_ListLiteral_in_ConstructorFieldInitializer);
+      });
+      _ut.test('test_function_literal_allowed_in_MapLiteral_in_ConstructorFieldInitializer', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_function_literal_allowed_in_MapLiteral_in_ConstructorFieldInitializer);
+      });
+      _ut.test('test_function_literal_allowed_in_ParenthesizedExpression_in_ConstructorFieldInitializer', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_function_literal_allowed_in_ParenthesizedExpression_in_ConstructorFieldInitializer);
+      });
+      _ut.test('test_function_literal_allowed_in_StringInterpolation_in_ConstructorFieldInitializer', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_function_literal_allowed_in_StringInterpolation_in_ConstructorFieldInitializer);
+      });
       _ut.test('test_isFunctionDeclaration_nameButNoReturn_block', () {
         final __test = new SimpleParserTest();
         runJUnitTest(__test, __test.test_isFunctionDeclaration_nameButNoReturn_block);
@@ -5192,10 +5249,6 @@
         final __test = new SimpleParserTest();
         runJUnitTest(__test, __test.test_parseClassMember_redirectingFactory_nonConst);
       });
-      _ut.test('test_parseClassTypeAlias', () {
-        final __test = new SimpleParserTest();
-        runJUnitTest(__test, __test.test_parseClassTypeAlias);
-      });
       _ut.test('test_parseClassTypeAlias_abstract', () {
         final __test = new SimpleParserTest();
         runJUnitTest(__test, __test.test_parseClassTypeAlias_abstract);
@@ -5528,6 +5581,10 @@
         final __test = new SimpleParserTest();
         runJUnitTest(__test, __test.test_parseConstructorName_unnamed_prefixed);
       });
+      _ut.test('test_parseConstructor_with_pseudo_function_literal', () {
+        final __test = new SimpleParserTest();
+        runJUnitTest(__test, __test.test_parseConstructor_with_pseudo_function_literal);
+      });
       _ut.test('test_parseContinueStatement_label', () {
         final __test = new SimpleParserTest();
         runJUnitTest(__test, __test.test_parseContinueStatement_label);
@@ -8716,6 +8773,20 @@
     JUnitTestCase.assertTrue(syntheticExpression.isSynthetic);
   }
 
+  void test_functionExpression_in_ConstructorFieldInitializer() {
+    CompilationUnit unit = ParserTestCase.parseCompilationUnit("class A { A() : a = (){}; var v; }", [
+        ParserErrorCode.MISSING_IDENTIFIER,
+        ParserErrorCode.UNEXPECTED_TOKEN]);
+    // Make sure we recovered and parsed "var v" correctly
+    ClassDeclaration declaration = unit.declarations[0] as ClassDeclaration;
+    NodeList<ClassMember> members = declaration.members;
+    ClassMember fieldDecl = members[1];
+    EngineTestCase.assertInstanceOf((obj) => obj is FieldDeclaration, FieldDeclaration, fieldDecl);
+    NodeList<VariableDeclaration> vars = (fieldDecl as FieldDeclaration).fields.variables;
+    EngineTestCase.assertSizeOfList(1, vars);
+    JUnitTestCase.assertEquals("v", vars[0].name.name);
+  }
+
   void test_incomplete_topLevelVariable() {
     CompilationUnit unit = ParserTestCase.parseCompilationUnit("String", [ParserErrorCode.EXPECTED_EXECUTABLE]);
     NodeList<CompilationUnitMember> declarations = unit.declarations;
@@ -9161,6 +9232,10 @@
         final __test = new RecoveryParserTest();
         runJUnitTest(__test, __test.test_expressionList_multiple_start);
       });
+      _ut.test('test_functionExpression_in_ConstructorFieldInitializer', () {
+        final __test = new RecoveryParserTest();
+        runJUnitTest(__test, __test.test_functionExpression_in_ConstructorFieldInitializer);
+      });
       _ut.test('test_incomplete_topLevelVariable', () {
         final __test = new RecoveryParserTest();
         runJUnitTest(__test, __test.test_incomplete_topLevelVariable);
@@ -9298,6 +9373,15 @@
 }
 
 class IncrementalParserTest extends EngineTestCase {
+  void fail_replace_identifier_with_functionLiteral_in_initializer() {
+    // Function literals aren't allowed inside initializers; incremental parsing needs to gather
+    // the appropriate context.
+    //
+    // "class A { var a; A(b) : a = b ? b : 0 { } }"
+    // "class A { var a; A(b) : a = b ? () {} : 0 { } }"
+    _assertParse("class A { var a; A(b) : a = b ? ", "b", "() {}", " : 0 { } }");
+  }
+
   void test_delete_everything() {
     // "f() => a + b;"
     // ""
@@ -9547,7 +9631,7 @@
     // Validate that the results of the incremental parse are the same as the full parse of the
     // modified source.
     //
-    JUnitTestCase.assertTrue(AstComparator.equalUnits(modifiedUnit, incrementalUnit));
+    JUnitTestCase.assertTrue(AstComparator.equalNodes(modifiedUnit, incrementalUnit));
   }
 
   static dartSuite() {
@@ -9850,6 +9934,14 @@
     ParserTestCase.parseStatement("() {for (; x;) {break;}};", []);
   }
 
+  void test_classTypeAlias_abstractAfterEq() {
+    // This syntax has been removed from the language in favor of "abstract class A = B with C;"
+    // (issue 18098).
+    ParserTestCase.parse3("parseCompilationUnitMember", <Object> [emptyCommentAndMetadata()], "class A = abstract B with C;", [
+        ParserErrorCode.EXPECTED_TOKEN,
+        ParserErrorCode.EXPECTED_TOKEN]);
+  }
+
   void test_constAndFinal() {
     ParserTestCase.parse3("parseClassMember", <Object> ["C"], "const final int x;", [ParserErrorCode.CONST_AND_FINAL]);
   }
@@ -9927,11 +10019,11 @@
   }
 
   void test_deprecatedClassTypeAlias() {
-    ParserTestCase.parseCompilationUnit("typedef C = abstract S with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
+    ParserTestCase.parseCompilationUnit("typedef C = S with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
   }
 
   void test_deprecatedClassTypeAlias_withGeneric() {
-    ParserTestCase.parseCompilationUnit("typedef C<T> = abstract S<T> with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
+    ParserTestCase.parseCompilationUnit("typedef C<T> = S<T> with M;", [ParserErrorCode.DEPRECATED_CLASS_TYPE_ALIAS]);
   }
 
   void test_directiveAfterDeclaration_classBeforeDirective() {
@@ -10040,7 +10132,7 @@
 
   void test_expectedToken_semicolonAfterClass() {
     Token token = TokenFactory.tokenFromKeyword(Keyword.CLASS);
-    ParserTestCase.parse3("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B", [ParserErrorCode.EXPECTED_TOKEN]);
+    ParserTestCase.parse3("parseClassTypeAlias", <Object> [emptyCommentAndMetadata(), null, token], "A = B with C", [ParserErrorCode.EXPECTED_TOKEN]);
   }
 
   void test_expectedToken_semicolonMissingAfterExport() {
@@ -10494,6 +10586,10 @@
     ParserTestCase.parse4("parseFormalParameterList", "(a, [b], {c})", [ParserErrorCode.MIXED_PARAMETER_GROUPS]);
   }
 
+  void test_mixin_application_lacks_with_clause() {
+    ParserTestCase.parseCompilationUnit("class Foo = Bar;", [ParserErrorCode.EXPECTED_TOKEN]);
+  }
+
   void test_multipleExtendsClauses() {
     ParserTestCase.parseCompilationUnit("class A extends B extends C {}", [ParserErrorCode.MULTIPLE_EXTENDS_CLAUSES]);
   }
@@ -10871,6 +10967,10 @@
         final __test = new ErrorParserTest();
         runJUnitTest(__test, __test.test_breakOutsideOfLoop_functionExpression_withALoop);
       });
+      _ut.test('test_classTypeAlias_abstractAfterEq', () {
+        final __test = new ErrorParserTest();
+        runJUnitTest(__test, __test.test_classTypeAlias_abstractAfterEq);
+      });
       _ut.test('test_constAndFinal', () {
         final __test = new ErrorParserTest();
         runJUnitTest(__test, __test.test_constAndFinal);
@@ -11483,6 +11583,10 @@
         final __test = new ErrorParserTest();
         runJUnitTest(__test, __test.test_mixedParameterGroups_positionalNamed);
       });
+      _ut.test('test_mixin_application_lacks_with_clause', () {
+        final __test = new ErrorParserTest();
+        runJUnitTest(__test, __test.test_mixin_application_lacks_with_clause);
+      });
       _ut.test('test_multipleExtendsClauses', () {
         final __test = new ErrorParserTest();
         runJUnitTest(__test, __test.test_multipleExtendsClauses);
diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart
index b719bc4..73bde01 100644
--- a/pkg/analyzer/test/generated/resolver_test.dart
+++ b/pkg/analyzer/test/generated/resolver_test.dart
@@ -20,7 +20,6 @@
 import 'package:analyzer/src/generated/resolver.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/utilities_dart.dart';
-import 'package:analyzer/src/generated/utilities_collection.dart';
 import 'package:analyzer/src/generated/sdk.dart' show DartSdk;
 import 'package:analyzer/src/generated/sdk_io.dart' show DirectoryBasedDartSdk;
 import 'package:unittest/unittest.dart' as _ut;
@@ -658,7 +657,13 @@
     BlockFunctionBody body = function.functionExpression.body as BlockFunctionBody;
     ReturnStatement statement = body.block.statements[1] as ReturnStatement;
     IndexExpression indexExpression = statement.expression as IndexExpression;
-    JUnitTestCase.assertSame(typeProvider.intType, indexExpression.propagatedType);
+    JUnitTestCase.assertNull(indexExpression.propagatedType);
+    Expression v = indexExpression.target;
+    InterfaceType propagatedType = v.propagatedType as InterfaceType;
+    JUnitTestCase.assertSame(typeProvider.listType.element, propagatedType.element);
+    List<DartType> typeArguments = propagatedType.typeArguments;
+    EngineTestCase.assertLength(1, typeArguments);
+    JUnitTestCase.assertSame(typeProvider.dynamicType, typeArguments[0]);
   }
 
   void test_mapLiteral_different() {
@@ -701,8 +706,8 @@
     JUnitTestCase.assertSame(typeProvider.mapType.element, propagatedType.element);
     List<DartType> typeArguments = propagatedType.typeArguments;
     EngineTestCase.assertLength(2, typeArguments);
-    JUnitTestCase.assertSame(typeProvider.stringType, typeArguments[0]);
-    JUnitTestCase.assertSame(typeProvider.intType, typeArguments[1]);
+    JUnitTestCase.assertSame(typeProvider.dynamicType, typeArguments[0]);
+    JUnitTestCase.assertSame(typeProvider.dynamicType, typeArguments[1]);
   }
 
   void test_propagatedReturnType_function_hasReturnType_returnsNull() {
@@ -1728,7 +1733,8 @@
         "class A<E extends num> {",
         "  A(E x, E y);",
         "}",
-        "class B<E extends num> = A<E>;",
+        "class M {}",
+        "class B<E extends num> = A<E> with M;",
         "void main() {",
         "   B<int> x = new B<int>(0,0);",
         "}"]));
@@ -1932,6 +1938,26 @@
     verify([source]);
   }
 
+  void test_functionWithoutCall_doesNotImplementFunction() {
+    Source source = addSource(EngineTestCase.createSource(["class A {}"]));
+    resolve(source);
+    assertNoErrors(source);
+    verify([source]);
+  }
+
+  void test_functionWithoutCall_withNoSuchMethod() {
+    // 16078
+    Source source = addSource(EngineTestCase.createSource([
+        "class A implements Function {",
+        "  noSuchMethod(inv) {",
+        "    return 42;",
+        "  }",
+        "}"]));
+    resolve(source);
+    assertNoErrors(source);
+    verify([source]);
+  }
+
   void test_implicitThisReferenceInInitializer_constructorName() {
     Source source = addSource(EngineTestCase.createSource([
         "class A {",
@@ -2808,6 +2834,24 @@
     verify([source]);
   }
 
+  void test_invocationOfNonFunction_proxyOnFunctionClass() {
+    // 16078
+    Source source = addSource(EngineTestCase.createSource([
+        "@proxy",
+        "class Functor implements Function {",
+        "  noSuchMethod(inv) {",
+        "    return 42;",
+        "  }",
+        "}",
+        "main() {",
+        "  Functor f = new Functor();",
+        "  f();",
+        "}"]));
+    resolve(source);
+    assertErrors(source, []);
+    verify([source]);
+  }
+
   void test_listElementTypeNotAssignable() {
     Source source = addSource(EngineTestCase.createSource(["var v1 = <int> [42];", "var v2 = const <int> [42];"]));
     resolve(source);
@@ -3685,6 +3729,19 @@
     assertNoErrors(source);
   }
 
+  void test_proxy_annotation_proxyHasPrefixedIdentifier() {
+    Source source = addSource(EngineTestCase.createSource([
+        "library L;",
+        "import 'dart:core' as core;",
+        "@core.proxy class PrefixProxy {}",
+        "main() {",
+        "  new PrefixProxy().foo;",
+        "  new PrefixProxy().foo();",
+        "}"]));
+    resolve(source);
+    assertNoErrors(source);
+  }
+
   void test_proxy_annotation_simple() {
     Source source = addSource(EngineTestCase.createSource([
         "library L;",
@@ -5078,6 +5135,14 @@
         final __test = new NonErrorResolverTest();
         runJUnitTest(__test, __test.test_functionWithoutCall);
       });
+      _ut.test('test_functionWithoutCall_doesNotImplementFunction', () {
+        final __test = new NonErrorResolverTest();
+        runJUnitTest(__test, __test.test_functionWithoutCall_doesNotImplementFunction);
+      });
+      _ut.test('test_functionWithoutCall_withNoSuchMethod', () {
+        final __test = new NonErrorResolverTest();
+        runJUnitTest(__test, __test.test_functionWithoutCall_withNoSuchMethod);
+      });
       _ut.test('test_implicitThisReferenceInInitializer_constructorName', () {
         final __test = new NonErrorResolverTest();
         runJUnitTest(__test, __test.test_implicitThisReferenceInInitializer_constructorName);
@@ -5370,6 +5435,10 @@
         final __test = new NonErrorResolverTest();
         runJUnitTest(__test, __test.test_invocationOfNonFunction_localVariable_dynamic2);
       });
+      _ut.test('test_invocationOfNonFunction_proxyOnFunctionClass', () {
+        final __test = new NonErrorResolverTest();
+        runJUnitTest(__test, __test.test_invocationOfNonFunction_proxyOnFunctionClass);
+      });
       _ut.test('test_listElementTypeNotAssignable', () {
         final __test = new NonErrorResolverTest();
         runJUnitTest(__test, __test.test_listElementTypeNotAssignable);
@@ -5678,6 +5747,10 @@
         final __test = new NonErrorResolverTest();
         runJUnitTest(__test, __test.test_proxy_annotation_prefixed3);
       });
+      _ut.test('test_proxy_annotation_proxyHasPrefixedIdentifier', () {
+        final __test = new NonErrorResolverTest();
+        runJUnitTest(__test, __test.test_proxy_annotation_proxyHasPrefixedIdentifier);
+      });
       _ut.test('test_proxy_annotation_simple', () {
         final __test = new NonErrorResolverTest();
         runJUnitTest(__test, __test.test_proxy_annotation_simple);
@@ -6328,6 +6401,19 @@
     verify([source]);
   }
 
+  void test_ambiguousImport_function() {
+    Source source = addSource(EngineTestCase.createSource([
+        "import 'lib1.dart';",
+        "import 'lib2.dart';",
+        "g() { return f(); }"]));
+    addNamedSource("/lib1.dart", EngineTestCase.createSource(["library lib1;", "f() {}"]));
+    addNamedSource("/lib2.dart", EngineTestCase.createSource(["library lib2;", "f() {}"]));
+    resolve(source);
+    assertErrors(source, [
+        StaticWarningCode.AMBIGUOUS_IMPORT,
+        StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+  }
+
   void test_expectedOneListTypeArgument() {
     Source source = addSource(EngineTestCase.createSource(["main() {", "  <int, int> [];", "}"]));
     resolve(source);
@@ -7177,12 +7263,59 @@
     assertErrors(source, [StaticTypeWarningCode.UNDEFINED_GETTER]);
   }
 
+  void test_undefinedFunction() {
+    Source source = addSource(EngineTestCase.createSource(["void f() {", "  g();", "}"]));
+    resolve(source);
+    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+  }
+
+  void test_undefinedFunction_hasImportPrefix() {
+    Source source = addSource(EngineTestCase.createSource(["import 'lib.dart' as f;", "main() { return f(); }"]));
+    addNamedSource("/lib.dart", "library lib;");
+    resolve(source);
+    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+  }
+
+  void test_undefinedFunction_inCatch() {
+    Source source = addSource(EngineTestCase.createSource([
+        "void f() {",
+        "  try {",
+        "  } on Object {",
+        "    g();",
+        "  }",
+        "}"]));
+    resolve(source);
+    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+  }
+
+  void test_undefinedFunction_inImportedLib() {
+    Source source = addSource(EngineTestCase.createSource(["import 'lib.dart' as f;", "main() { return f.g(); }"]));
+    addNamedSource("/lib.dart", EngineTestCase.createSource(["library lib;", "h() {}"]));
+    resolve(source);
+    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_FUNCTION]);
+  }
+
   void test_undefinedGetter() {
     Source source = addSource(EngineTestCase.createSource(["class T {}", "f(T e) { return e.m; }"]));
     resolve(source);
     assertErrors(source, [StaticTypeWarningCode.UNDEFINED_GETTER]);
   }
 
+  void test_undefinedGetter_proxy_annotation_fakeProxy() {
+    Source source = addSource(EngineTestCase.createSource([
+        "library L;",
+        "class Fake {",
+        "  const Fake();",
+        "}",
+        "const proxy = const Fake();",
+        "@proxy class PrefixProxy {}",
+        "main() {",
+        "  new PrefixProxy().foo;",
+        "}"]));
+    resolve(source);
+    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_GETTER]);
+  }
+
   void test_undefinedGetter_static() {
     Source source = addSource(EngineTestCase.createSource(["class A {}", "var a = A.B;"]));
     resolve(source);
@@ -7286,6 +7419,21 @@
     assertErrors(source, [StaticTypeWarningCode.UNDEFINED_METHOD]);
   }
 
+  void test_undefinedMethod_proxy_annotation_fakeProxy() {
+    Source source = addSource(EngineTestCase.createSource([
+        "library L;",
+        "class Fake {",
+        "  const Fake();",
+        "}",
+        "const proxy = const Fake();",
+        "@proxy class PrefixProxy {}",
+        "main() {",
+        "  new PrefixProxy().foo();",
+        "}"]));
+    resolve(source);
+    assertErrors(source, [StaticTypeWarningCode.UNDEFINED_METHOD]);
+  }
+
   void test_undefinedOperator_indexBoth() {
     Source source = addSource(EngineTestCase.createSource(["class A {}", "f(A a) {", "  a[0]++;", "}"]));
     resolve(source);
@@ -7400,7 +7548,10 @@
   }
 
   void test_wrongNumberOfTypeArguments_classAlias() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class B<F extends num> = A<F>;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class B<F extends num> = A<F> with M;"]));
     resolve(source);
     assertErrors(source, [StaticTypeWarningCode.WRONG_NUMBER_OF_TYPE_ARGUMENTS]);
     verify([source]);
@@ -7446,6 +7597,10 @@
 
   static dartSuite() {
     _ut.group('StaticTypeWarningCodeTest', () {
+      _ut.test('test_ambiguousImport_function', () {
+        final __test = new StaticTypeWarningCodeTest();
+        runJUnitTest(__test, __test.test_ambiguousImport_function);
+      });
       _ut.test('test_expectedOneListTypeArgument', () {
         final __test = new StaticTypeWarningCodeTest();
         runJUnitTest(__test, __test.test_expectedOneListTypeArgument);
@@ -7766,10 +7921,30 @@
         final __test = new StaticTypeWarningCodeTest();
         runJUnitTest(__test, __test.test_typePromotion_if_with_notMoreSpecific_dynamic);
       });
+      _ut.test('test_undefinedFunction', () {
+        final __test = new StaticTypeWarningCodeTest();
+        runJUnitTest(__test, __test.test_undefinedFunction);
+      });
+      _ut.test('test_undefinedFunction_hasImportPrefix', () {
+        final __test = new StaticTypeWarningCodeTest();
+        runJUnitTest(__test, __test.test_undefinedFunction_hasImportPrefix);
+      });
+      _ut.test('test_undefinedFunction_inCatch', () {
+        final __test = new StaticTypeWarningCodeTest();
+        runJUnitTest(__test, __test.test_undefinedFunction_inCatch);
+      });
+      _ut.test('test_undefinedFunction_inImportedLib', () {
+        final __test = new StaticTypeWarningCodeTest();
+        runJUnitTest(__test, __test.test_undefinedFunction_inImportedLib);
+      });
       _ut.test('test_undefinedGetter', () {
         final __test = new StaticTypeWarningCodeTest();
         runJUnitTest(__test, __test.test_undefinedGetter);
       });
+      _ut.test('test_undefinedGetter_proxy_annotation_fakeProxy', () {
+        final __test = new StaticTypeWarningCodeTest();
+        runJUnitTest(__test, __test.test_undefinedGetter_proxy_annotation_fakeProxy);
+      });
       _ut.test('test_undefinedGetter_static', () {
         final __test = new StaticTypeWarningCodeTest();
         runJUnitTest(__test, __test.test_undefinedGetter_static);
@@ -7806,6 +7981,10 @@
         final __test = new StaticTypeWarningCodeTest();
         runJUnitTest(__test, __test.test_undefinedMethod_private);
       });
+      _ut.test('test_undefinedMethod_proxy_annotation_fakeProxy', () {
+        final __test = new StaticTypeWarningCodeTest();
+        runJUnitTest(__test, __test.test_undefinedMethod_proxy_annotation_fakeProxy);
+      });
       _ut.test('test_undefinedOperator_indexBoth', () {
         final __test = new StaticTypeWarningCodeTest();
         runJUnitTest(__test, __test.test_undefinedOperator_indexBoth);
@@ -9804,6 +9983,16 @@
   }
 
   /**
+   * In the rare cases we want to group several tests into single "test_" method, so need a way to
+   * reset test instance to reuse it.
+   *
+   * @param options the analysis options for the context
+   */
+  void resetWithOptions(AnalysisOptions options) {
+    _analysisContext = AnalysisContextFactory.contextWithCoreAndOptions(options);
+  }
+
+  /**
    * Given a library and all of its parts, resolve the contents of the library and the contents of
    * the parts. This assumes that the sources for the library and its parts have already been added
    * to the content provider using the method [addNamedSource].
@@ -11204,19 +11393,6 @@
     verify([source]);
   }
 
-  void test_ambiguousImport_function() {
-    Source source = addSource(EngineTestCase.createSource([
-        "import 'lib1.dart';",
-        "import 'lib2.dart';",
-        "g() { return f(); }"]));
-    addNamedSource("/lib1.dart", EngineTestCase.createSource(["library lib1;", "f() {}"]));
-    addNamedSource("/lib2.dart", EngineTestCase.createSource(["library lib2;", "f() {}"]));
-    resolve(source);
-    assertErrors(source, [
-        StaticWarningCode.AMBIGUOUS_IMPORT,
-        CompileTimeErrorCode.UNDEFINED_FUNCTION]);
-  }
-
   void test_builtInIdentifierAsMixinName_classTypeAlias() {
     Source source = addSource(EngineTestCase.createSource(["class A {}", "class B {}", "class as = A with B;"]));
     resolve(source);
@@ -12067,42 +12243,42 @@
   }
 
   void test_extendsDisallowedClass_classTypeAlias_bool() {
-    Source source = addSource(EngineTestCase.createSource(["class C = bool;"]));
+    Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = bool with M;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_extendsDisallowedClass_classTypeAlias_double() {
-    Source source = addSource(EngineTestCase.createSource(["class C = double;"]));
+    Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = double with M;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_extendsDisallowedClass_classTypeAlias_int() {
-    Source source = addSource(EngineTestCase.createSource(["class C = int;"]));
+    Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = int with M;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_extendsDisallowedClass_classTypeAlias_Null() {
-    Source source = addSource(EngineTestCase.createSource(["class C = Null;"]));
+    Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = Null with M;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_extendsDisallowedClass_classTypeAlias_num() {
-    Source source = addSource(EngineTestCase.createSource(["class C = num;"]));
+    Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = num with M;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_extendsDisallowedClass_classTypeAlias_String() {
-    Source source = addSource(EngineTestCase.createSource(["class C = String;"]));
+    Source source = addSource(EngineTestCase.createSource(["class M {}", "class C = String with M;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.EXTENDS_DISALLOWED_CLASS]);
     verify([source]);
@@ -12369,49 +12545,70 @@
   }
 
   void test_implementsDisallowedClass_classTypeAlias_bool() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements bool;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class C = A with M implements bool;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_implementsDisallowedClass_classTypeAlias_double() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements double;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class C = A with M implements double;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_implementsDisallowedClass_classTypeAlias_int() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements int;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class C = A with M implements int;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_implementsDisallowedClass_classTypeAlias_Null() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements Null;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class C = A with M implements Null;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_implementsDisallowedClass_classTypeAlias_num() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements num;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class C = A with M implements num;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_implementsDisallowedClass_classTypeAlias_String() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements String;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class C = A with M implements String;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS]);
     verify([source]);
   }
 
   void test_implementsDisallowedClass_classTypeAlias_String_num() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "class C = A implements String, num;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "class C = A with M implements String, num;"]));
     resolve(source);
     assertErrors(source, [
         CompileTimeErrorCode.IMPLEMENTS_DISALLOWED_CLASS,
@@ -12434,7 +12631,11 @@
   }
 
   void test_implementsNonClass_typeAlias() {
-    Source source = addSource(EngineTestCase.createSource(["class A {}", "int B;", "class C = A implements B;"]));
+    Source source = addSource(EngineTestCase.createSource([
+        "class A {}",
+        "class M {}",
+        "int B;",
+        "class C = A with M implements B;"]));
     resolve(source);
     assertErrors(source, [CompileTimeErrorCode.IMPLEMENTS_NON_CLASS]);
     verify([source]);
@@ -14209,31 +14410,6 @@
     verify([source]);
   }
 
-  void test_undefinedFunction() {
-    Source source = addSource(EngineTestCase.createSource(["void f() {", "  g();", "}"]));
-    resolve(source);
-    assertErrors(source, [CompileTimeErrorCode.UNDEFINED_FUNCTION]);
-  }
-
-  void test_undefinedFunction_hasImportPrefix() {
-    Source source = addSource(EngineTestCase.createSource(["import 'lib.dart' as f;", "main() { return f(); }"]));
-    addNamedSource("/lib.dart", "library lib;");
-    resolve(source);
-    assertErrors(source, [CompileTimeErrorCode.UNDEFINED_FUNCTION]);
-  }
-
-  void test_undefinedFunction_inCatch() {
-    Source source = addSource(EngineTestCase.createSource([
-        "void f() {",
-        "  try {",
-        "  } on Object {",
-        "    g();",
-        "  }",
-        "}"]));
-    resolve(source);
-    assertErrors(source, [CompileTimeErrorCode.UNDEFINED_FUNCTION]);
-  }
-
   void test_undefinedNamedParameter() {
     Source source = addSource(EngineTestCase.createSource([
         "class A {",
@@ -14436,10 +14612,6 @@
         final __test = new CompileTimeErrorCodeTest();
         runJUnitTest(__test, __test.test_ambiguousExport);
       });
-      _ut.test('test_ambiguousImport_function', () {
-        final __test = new CompileTimeErrorCodeTest();
-        runJUnitTest(__test, __test.test_ambiguousImport_function);
-      });
       _ut.test('test_builtInIdentifierAsMixinName_classTypeAlias', () {
         final __test = new CompileTimeErrorCodeTest();
         runJUnitTest(__test, __test.test_builtInIdentifierAsMixinName_classTypeAlias);
@@ -15728,18 +15900,6 @@
         final __test = new CompileTimeErrorCodeTest();
         runJUnitTest(__test, __test.test_undefinedConstructorInInitializer_implicit);
       });
-      _ut.test('test_undefinedFunction', () {
-        final __test = new CompileTimeErrorCodeTest();
-        runJUnitTest(__test, __test.test_undefinedFunction);
-      });
-      _ut.test('test_undefinedFunction_hasImportPrefix', () {
-        final __test = new CompileTimeErrorCodeTest();
-        runJUnitTest(__test, __test.test_undefinedFunction_hasImportPrefix);
-      });
-      _ut.test('test_undefinedFunction_inCatch', () {
-        final __test = new CompileTimeErrorCodeTest();
-        runJUnitTest(__test, __test.test_undefinedFunction_inCatch);
-      });
       _ut.test('test_undefinedNamedParameter', () {
         final __test = new CompileTimeErrorCodeTest();
         runJUnitTest(__test, __test.test_undefinedNamedParameter);
@@ -16037,10 +16197,9 @@
 
   @override
   void setUp() {
-    super.setUp();
     AnalysisOptionsImpl options = new AnalysisOptionsImpl();
     options.hint = false;
-    analysisContext.analysisOptions = options;
+    resetWithOptions(options);
   }
 
   void test_assert_is() {
@@ -21278,7 +21437,23 @@
    *
    * @return the analysis context that was created
    */
-  static AnalysisContextImpl contextWithCore() => initContextWithCore(new DelegatingAnalysisContextImpl());
+  static AnalysisContextImpl contextWithCore() {
+    AnalysisContextImpl context = new AnalysisContextImpl_AnalysisContextFactory_contextWithCore();
+    return initContextWithCore(context);
+  }
+
+  /**
+   * Create an analysis context that uses the given options and has a fake core library already
+   * resolved.
+   *
+   * @param options the options to be applied to the context
+   * @return the analysis context that was created
+   */
+  static AnalysisContextImpl contextWithCoreAndOptions(AnalysisOptions options) {
+    AnalysisContextImpl context = new AnalysisContextImpl();
+    context.analysisOptions = options;
+    return initContextWithCore(context);
+  }
 
   /**
    * Initialize the given analysis context with a fake core library already resolved.
@@ -21287,16 +21462,19 @@
    * @return the analysis context that was created
    */
   static AnalysisContextImpl initContextWithCore(AnalysisContextImpl context) {
-    AnalysisContext sdkContext = DirectoryBasedDartSdk.defaultSdk.context;
-    SourceFactory sourceFactory = sdkContext.sourceFactory;
+    SourceFactory sourceFactory = new SourceFactory([
+        new DartUriResolver(DirectoryBasedDartSdk.defaultSdk),
+        new FileUriResolver()]);
+    context.sourceFactory = sourceFactory;
     //
     // dart:core
     //
     TestTypeProvider provider = new TestTypeProvider();
     CompilationUnitElementImpl coreUnit = new CompilationUnitElementImpl("core.dart");
     Source coreSource = sourceFactory.forUri(DartSdk.DART_CORE);
-    sdkContext.setContents(coreSource, "");
+    context.setContents(coreSource, "");
     coreUnit.source = coreSource;
+    ClassElementImpl proxyClassElement = ElementFactory.classElement2("_Proxy", []);
     coreUnit.types = <ClassElement> [
         provider.boolType.element,
         provider.deprecatedType.element,
@@ -21308,12 +21486,13 @@
         provider.nullType.element,
         provider.numType.element,
         provider.objectType.element,
+        proxyClassElement,
         provider.stackTraceType.element,
         provider.stringType.element,
         provider.symbolType.element,
         provider.typeType.element];
     coreUnit.functions = <FunctionElement> [ElementFactory.functionElement3("identical", provider.boolType.element, <ClassElement> [provider.objectType.element, provider.objectType.element], null)];
-    TopLevelVariableElement proxyTopLevelVariableElt = ElementFactory.topLevelVariableElement3("proxy", true, false, ElementFactory.classElement2("_Proxy", []).type);
+    TopLevelVariableElement proxyTopLevelVariableElt = ElementFactory.topLevelVariableElement3("proxy", true, false, proxyClassElement.type);
     TopLevelVariableElement deprecatedTopLevelVariableElt = ElementFactory.topLevelVariableElement3("deprecated", true, false, provider.deprecatedType);
     coreUnit.accessors = <PropertyAccessorElement> [
         proxyTopLevelVariableElt.getter,
@@ -21321,14 +21500,14 @@
         deprecatedTopLevelVariableElt.getter,
         deprecatedTopLevelVariableElt.setter];
     coreUnit.topLevelVariables = <TopLevelVariableElement> [proxyTopLevelVariableElt, deprecatedTopLevelVariableElt];
-    LibraryElementImpl coreLibrary = new LibraryElementImpl(sdkContext, AstFactory.libraryIdentifier2(["dart", "core"]));
+    LibraryElementImpl coreLibrary = new LibraryElementImpl(context, AstFactory.libraryIdentifier2(["dart", "core"]));
     coreLibrary.definingCompilationUnit = coreUnit;
     //
     // dart:html
     //
     CompilationUnitElementImpl htmlUnit = new CompilationUnitElementImpl("html_dartium.dart");
     Source htmlSource = sourceFactory.forUri(DartSdk.DART_HTML);
-    sdkContext.setContents(htmlSource, "");
+    context.setContents(htmlSource, "");
     htmlUnit.source = htmlSource;
     ClassElementImpl elementElement = ElementFactory.classElement2("Element", []);
     InterfaceType elementType = elementElement.type;
@@ -21349,20 +21528,28 @@
     TopLevelVariableElementImpl document = ElementFactory.topLevelVariableElement3("document", false, true, htmlDocumentElement.type);
     htmlUnit.topLevelVariables = <TopLevelVariableElement> [document];
     htmlUnit.accessors = <PropertyAccessorElement> [document.getter];
-    LibraryElementImpl htmlLibrary = new LibraryElementImpl(sdkContext, AstFactory.libraryIdentifier2(["dart", "dom", "html"]));
+    LibraryElementImpl htmlLibrary = new LibraryElementImpl(context, AstFactory.libraryIdentifier2(["dart", "dom", "html"]));
     htmlLibrary.definingCompilationUnit = htmlUnit;
     Map<Source, LibraryElement> elementMap = new Map<Source, LibraryElement>();
     elementMap[coreSource] = coreLibrary;
     elementMap[htmlSource] = htmlLibrary;
-    (sdkContext as AnalysisContextImpl).recordLibraryElements(elementMap);
-    sourceFactory = new SourceFactory([
-        new DartUriResolver(sdkContext.sourceFactory.dartSdk),
-        new FileUriResolver()]);
-    context.sourceFactory = sourceFactory;
+    context.recordLibraryElements(elementMap);
     return context;
   }
 }
 
+class AnalysisContextImpl_AnalysisContextFactory_contextWithCore extends AnalysisContextImpl {
+  @override
+  void set analysisOptions(AnalysisOptions options) {
+    AnalysisOptions currentOptions = analysisOptions;
+    bool needsRecompute = currentOptions.analyzeFunctionBodies != options.analyzeFunctionBodies || currentOptions.generateSdkErrors != options.generateSdkErrors || currentOptions.dart2jsHint != options.dart2jsHint || (currentOptions.hint && !options.hint) || currentOptions.preserveComments != options.preserveComments;
+    if (needsRecompute) {
+      JUnitTestCase.fail("Cannot set options that cause the sources to be reanalyzed in a test context");
+    }
+    super.analysisOptions = options;
+  }
+}
+
 class LibraryImportScopeTest extends ResolverTestCase {
   void test_conflictingImports() {
     AnalysisContext context = new AnalysisContextImpl();
@@ -22786,8 +22973,8 @@
       EngineTestCase.assertSizeOfMap(0, namedTypes);
     } else {
       EngineTestCase.assertSizeOfMap(expectedNamedTypes.length, namedTypes);
-      for (MapIterator<String, DartType> iter = SingleMapIterator.forMap(expectedNamedTypes); iter.moveNext();) {
-        JUnitTestCase.assertSame(iter.value, namedTypes[iter.key]);
+      for (MapEntry<String, DartType> entry in getMapEntrySet(expectedNamedTypes)) {
+        JUnitTestCase.assertSame(entry.getValue(), namedTypes[entry.getKey()]);
       }
     }
     JUnitTestCase.assertSame(expectedReturnType, functionType.returnType);
@@ -25591,6 +25778,79 @@
   }
 }
 
+class ChangeSetTest extends EngineTestCase {
+  void test_changedContent() {
+    TestSource source = new TestSource();
+    String content = "";
+    ChangeSet changeSet = new ChangeSet();
+    changeSet.changedContent(source, content);
+    EngineTestCase.assertSizeOfList(0, changeSet.addedSources);
+    EngineTestCase.assertSizeOfList(0, changeSet.changedSources);
+    Map<Source, String> map = changeSet.changedContents;
+    EngineTestCase.assertSizeOfMap(1, map);
+    JUnitTestCase.assertSame(content, map[source]);
+    EngineTestCase.assertSizeOfMap(0, changeSet.changedRanges);
+    EngineTestCase.assertSizeOfList(0, changeSet.deletedSources);
+    EngineTestCase.assertSizeOfList(0, changeSet.removedSources);
+    EngineTestCase.assertSizeOfList(0, changeSet.removedContainers);
+  }
+
+  void test_changedRange() {
+    TestSource source = new TestSource();
+    String content = "";
+    ChangeSet changeSet = new ChangeSet();
+    changeSet.changedRange(source, content, 1, 2, 3);
+    EngineTestCase.assertSizeOfList(0, changeSet.addedSources);
+    EngineTestCase.assertSizeOfList(0, changeSet.changedSources);
+    EngineTestCase.assertSizeOfMap(0, changeSet.changedContents);
+    Map<Source, ChangeSet_ContentChange> map = changeSet.changedRanges;
+    EngineTestCase.assertSizeOfMap(1, map);
+    ChangeSet_ContentChange change = map[source];
+    JUnitTestCase.assertNotNull(change);
+    JUnitTestCase.assertEquals(content, change.contents);
+    JUnitTestCase.assertEquals(1, change.offset);
+    JUnitTestCase.assertEquals(2, change.oldLength);
+    JUnitTestCase.assertEquals(3, change.newLength);
+    EngineTestCase.assertSizeOfList(0, changeSet.deletedSources);
+    EngineTestCase.assertSizeOfList(0, changeSet.removedSources);
+    EngineTestCase.assertSizeOfList(0, changeSet.removedContainers);
+  }
+
+  void test_toString() {
+    ChangeSet changeSet = new ChangeSet();
+    changeSet.addedSource(new TestSource());
+    changeSet.changedSource(new TestSource());
+    changeSet.changedContent(new TestSource(), "");
+    changeSet.changedRange(new TestSource(), "", 0, 0, 0);
+    changeSet.deletedSource(new TestSource());
+    changeSet.removedSource(new TestSource());
+    changeSet.removedContainer(new SourceContainer_ChangeSetTest_test_toString());
+    JUnitTestCase.assertNotNull(changeSet.toString());
+  }
+
+  static dartSuite() {
+    _ut.group('ChangeSetTest', () {
+      _ut.test('test_changedContent', () {
+        final __test = new ChangeSetTest();
+        runJUnitTest(__test, __test.test_changedContent);
+      });
+      _ut.test('test_changedRange', () {
+        final __test = new ChangeSetTest();
+        runJUnitTest(__test, __test.test_changedRange);
+      });
+      _ut.test('test_toString', () {
+        final __test = new ChangeSetTest();
+        runJUnitTest(__test, __test.test_toString);
+      });
+    });
+  }
+}
+
+class SourceContainer_ChangeSetTest_test_toString implements SourceContainer {
+  @override
+  bool contains(Source source) => false;
+}
+
 class ScopeBuilderTest extends EngineTestCase {
   void test_scopeFor_ClassDeclaration() {
     GatheringErrorListener listener = new GatheringErrorListener();
@@ -25848,4 +26108,5 @@
 //  StaticWarningCodeTest.dartSuite();
 //  StrictModeTest.dartSuite();
 //  TypePropagationTest.dartSuite();
+//  ChangeSetTest.dartSuite();
 }
\ No newline at end of file
diff --git a/pkg/analyzer/test/generated/test_support.dart b/pkg/analyzer/test/generated/test_support.dart
index 435d3ce..f3c93be 100644
--- a/pkg/analyzer/test/generated/test_support.dart
+++ b/pkg/analyzer/test/generated/test_support.dart
@@ -10,7 +10,6 @@
 import 'package:analyzer/src/generated/java_core.dart';
 import 'package:analyzer/src/generated/java_junit.dart';
 import 'package:analyzer/src/generated/java_engine.dart';
-import 'package:analyzer/src/generated/utilities_collection.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/error.dart';
 import 'package:analyzer/src/generated/scanner.dart';
@@ -143,9 +142,9 @@
     //
     // Compare the expected and actual number of each type of error.
     //
-    for (MapIterator<ErrorCode, int> iter = SingleMapIterator.forMap(expectedCounts); iter.moveNext();) {
-      ErrorCode code = iter.key;
-      int expectedCount = iter.value;
+    for (MapEntry<ErrorCode, int> entry in getMapEntrySet(expectedCounts)) {
+      ErrorCode code = entry.getKey();
+      int expectedCount = entry.getValue();
       int actualCount;
       List<AnalysisError> list = errorsByCode.remove(code);
       if (list == null) {
@@ -169,9 +168,9 @@
     //
     // Check that there are no more errors in the actual-errors map, otherwise, record message.
     //
-    for (MapIterator<ErrorCode, List<AnalysisError>> iter = SingleMapIterator.forMap(errorsByCode); iter.moveNext();) {
-      ErrorCode code = iter.key;
-      List<AnalysisError> actualErrors = iter.value;
+    for (MapEntry<ErrorCode, List<AnalysisError>> entry in getMapEntrySet(errorsByCode)) {
+      ErrorCode code = entry.getKey();
+      List<AnalysisError> actualErrors = entry.getValue();
       int actualCount = actualErrors.length;
       if (builder.length == 0) {
         builder.append("Expected ");
@@ -821,6 +820,8 @@
 }
 
 class TestSource implements Source {
+  String _name;
+  TestSource([this._name = '/test.dart']);
   int get hashCode => 0;
   bool operator ==(Object object) {
     return object is TestSource;
@@ -832,10 +833,10 @@
     throw new UnsupportedOperationException();
   }
   String get fullName {
-    throw new UnsupportedOperationException();
+    return _name;
   }
   String get shortName {
-    throw new UnsupportedOperationException();
+    return _name;
   }
   String get encoding {
     throw new UnsupportedOperationException();
diff --git a/pkg/analyzer/test/services/formatter_test.dart b/pkg/analyzer/test/services/formatter_test.dart
index e0a982d..d571119 100644
--- a/pkg/analyzer/test/services/formatter_test.dart
+++ b/pkg/analyzer/test/services/formatter_test.dart
@@ -29,11 +29,12 @@
   });
 
   /// Data-driven compilation unit tests
-  group('cu_tests.data', () {
-    runTests('cu_tests.data', (input, expectedOutput) {
-      expectCUFormatsTo(input, expectedOutput);
-    });
-  });
+  /// TODO(scheglov) https://code.google.com/p/dart/issues/detail?id=18315
+//  group('cu_tests.data', () {
+//    runTests('cu_tests.data', (input, expectedOutput) {
+//      expectCUFormatsTo(input, expectedOutput);
+//    });
+//  });
 
   /// Data-driven Style Guide acceptance tests
   group('style_guide_tests.data', () {
diff --git a/pkg/barback/CHANGELOG.md b/pkg/barback/CHANGELOG.md
index c62245e..4a46d38 100644
--- a/pkg/barback/CHANGELOG.md
+++ b/pkg/barback/CHANGELOG.md
@@ -2,6 +2,13 @@
 
 * Only run `Transformer.isPrimary` once for each asset.
 
+* Don't warn if a lazy or declaring transformer doesn't emit outputs that it has
+  declared. This is valid for transformers like dart2js that need to read their
+  primary input in order to determine whether they should run.
+
+* Fix a deadlock bug when a lazy primary input to a lazy transformer became
+  dirty while the transformer's `apply` method was running.
+
 ## 0.13.0
 
 * `Transformer.isPrimary` now takes an `AssetId` rather than an `Asset`.
diff --git a/pkg/barback/lib/src/asset_node.dart b/pkg/barback/lib/src/asset_node.dart
index 5e79443..16aff40 100644
--- a/pkg/barback/lib/src/asset_node.dart
+++ b/pkg/barback/lib/src/asset_node.dart
@@ -58,6 +58,11 @@
   /// lazy.
   Function _lazyCallback;
 
+  /// Whether this asset's transform is deferred.
+  ///
+  /// See [TransformNode.deferred].
+  bool get deferred => transform != null && transform.deferred;
+
   /// A broadcast stream that emits an event whenever the node changes state.
   ///
   /// This stream is synchronous to ensure that when a source asset is modified
@@ -191,9 +196,14 @@
   /// Marks the node as [AssetState.DIRTY].
   void setDirty() {
     assert(node._state != AssetState.REMOVED);
-    node._state = AssetState.DIRTY;
     node._asset = null;
     node._lazyCallback = null;
+
+    // Don't re-emit a dirty event to avoid cases where we try to dispatch an
+    // event while handling another event (e.g. an output is marked lazy, which
+    // causes it to be forced, which causes it to be marked dirty).
+    if (node._state.isDirty) return;
+    node._state = AssetState.DIRTY;
     node._stateChangeController.add(AssetState.DIRTY);
   }
 
diff --git a/pkg/barback/lib/src/phase.dart b/pkg/barback/lib/src/phase.dart
index aa7dc78..9f48aa9 100644
--- a/pkg/barback/lib/src/phase.dart
+++ b/pkg/barback/lib/src/phase.dart
@@ -170,8 +170,6 @@
   void addInput(AssetNode node) {
     if (_inputs.containsKey(node.id)) _inputs[node.id].remove();
 
-    node.force();
-
     // Each group is one channel along which an asset may be forwarded, as is
     // each transformer.
     var forwarder = new PhaseForwarder(
diff --git a/pkg/barback/lib/src/phase_input.dart b/pkg/barback/lib/src/phase_input.dart
index db33779..0f8a365 100644
--- a/pkg/barback/lib/src/phase_input.dart
+++ b/pkg/barback/lib/src/phase_input.dart
@@ -38,6 +38,9 @@
   /// The asset node for this input.
   AssetNode get input => _inputForwarder.node;
 
+  /// The subscription to [input]'s [AssetNode.onStateChange] stream.
+  StreamSubscription _inputSubscription;
+
   /// A stream that emits an event whenever [this] is no longer dirty.
   ///
   /// This is synchronous in order to guarantee that it will emit an event as
@@ -53,7 +56,8 @@
   final _onAssetPool = new StreamPool<AssetNode>.broadcast();
 
   /// Whether [this] is dirty and still has more processing to do.
-  bool get isDirty => _transforms.any((transform) => transform.isDirty);
+  bool get isDirty => (input.state.isDirty && !input.deferred) ||
+      _transforms.any((transform) => transform.isDirty);
 
   /// A stream that emits an event whenever any transforms that use [input] as
   /// their primary input log an entry.
@@ -62,13 +66,20 @@
 
   PhaseInput(this._phase, AssetNode input, this._location)
       : _inputForwarder = new AssetForwarder(input) {
-    input.whenRemoved(remove);
+    _inputSubscription = input.onStateChange.listen((state) {
+      if (state.isRemoved) {
+        remove();
+      } else if (state.isAvailable) {
+        if (!isDirty) _onDoneController.add(null);
+      }
+    });
   }
 
   /// Removes this input.
   ///
   /// This marks all outputs of the input as removed.
   void remove() {
+    _inputSubscription.cancel();
     _onDoneController.close();
     _onAssetPool.close();
     _onLogPool.close();
diff --git a/pkg/barback/lib/src/serialize.dart b/pkg/barback/lib/src/serialize.dart
index 7114039..adc365d 100644
--- a/pkg/barback/lib/src/serialize.dart
+++ b/pkg/barback/lib/src/serialize.dart
@@ -45,10 +45,8 @@
   return callbackStream(() {
     var receivePort = new ReceivePort();
     sendPort.send(receivePort.sendPort);
-    // TODO(nweiz): use a const constructor for StreamTransformer when issue
-    // 14971 is fixed.
     return receivePort.transform(
-        new StreamTransformer(_deserializeTransformer));
+        const StreamTransformer(_deserializeTransformer));
   });
 }
 
diff --git a/pkg/barback/lib/src/transform_node.dart b/pkg/barback/lib/src/transform_node.dart
index ecd8d8f..1cd5d71 100644
--- a/pkg/barback/lib/src/transform_node.dart
+++ b/pkg/barback/lib/src/transform_node.dart
@@ -45,10 +45,25 @@
   StreamSubscription<AssetNode> _phaseSubscription;
 
   /// Whether [this] is dirty and still has more processing to do.
-  bool get isDirty => _state != _State.NOT_PRIMARY && _state != _State.APPLIED;
+  bool get isDirty => _state != _State.NOT_PRIMARY &&
+      _state != _State.APPLIED && _state != _State.DECLARED;
 
-  /// Whether [transformer] is lazy and this transform has yet to be forced.
-  bool _isLazy;
+  /// Whether this transform is deferred.
+  ///
+  /// A transform is deferred if either its transformer is lazy or if its
+  /// transformer is declaring and its primary input comes from a deferred
+  /// transformer.
+  final bool deferred;
+
+  /// Whether this is a deferred transform waiting for [force] to be called to
+  /// generate inputs.
+  ///
+  /// This defaults to `true` for deferred transforms and `false` otherwise.
+  /// During or after running `isPrimary` or `declareOutputs`, this may become
+  /// `false`, indicating that the transform has been forced and should generate
+  /// outputs as soon as possible. It will only be set back to `true` if an
+  /// input changes *after* `apply` has completed.
+  bool _awaitingForce;
 
   /// The subscriptions to each input's [AssetNode.onStateChange] stream.
   final _inputSubscriptions = new Map<AssetId, StreamSubscription>();
@@ -96,7 +111,7 @@
   final _onLogController = new StreamController<LogEntry>.broadcast(sync: true);
 
   /// The current state of [this].
-  var _state = _State.COMPUTING_IS_PRIMARY;
+  var _state = _State.DECLARING;
 
   /// Whether [this] has been marked as removed.
   bool get _isRemoved => _onAssetController.isClosed;
@@ -105,7 +120,7 @@
   /// consumes the primary input.
   ///
   /// Defaults to `false`. This is not meaningful unless [_state] is
-  /// [_State.APPLIED].
+  /// [_State.APPLIED] or [_State.DECLARED].
   bool _consumePrimary = false;
 
   /// The set of output ids that [transformer] declared it would emit.
@@ -114,22 +129,32 @@
   /// [declareOutputs] has been run successfully.
   Set<AssetId> _declaredOutputs;
 
-  TransformNode(this.phase, Transformer transformer, this.primary,
+  TransformNode(this.phase, Transformer transformer, AssetNode primary,
       this._location)
       : transformer = transformer,
-        _isLazy = transformer is LazyTransformer {
+        primary = primary,
+        deferred = transformer is LazyTransformer ||
+            (transformer is DeclaringTransformer && primary.deferred) {
+    _awaitingForce = deferred;
+
     _onLogPool.add(_onLogController.stream);
 
     _primarySubscription = primary.onStateChange.listen((state) {
       if (state.isRemoved) {
         remove();
       } else {
+        if (state.isDirty && !deferred) primary.force();
+        // If this is deferred but applying, that means it must have been
+        // forced, so we should ensure its input remains forced as well.
+        if (deferred && _state == _State.APPLYING) primary.force();
         _dirty();
       }
     });
 
     _phaseSubscription = phase.previous.onAsset.listen((node) {
-      if (_missingInputs.contains(node.id)) _dirty();
+      if (!_missingInputs.contains(node.id)) return;
+      if (!deferred) node.force();
+      _dirty();
     });
 
     _isPrimary();
@@ -161,13 +186,12 @@
     }
   }
 
-  /// If [transformer] is lazy, ensures that its concrete outputs will be
+  /// If [this] is deferred, ensures that its concrete outputs will be
   /// generated.
   void force() {
-    // TODO(nweiz): we might want to have a timeout after which, if the
-    // transform's outputs have gone unused, we switch it back to lazy mode.
-    if (!_isLazy) return;
-    _isLazy = false;
+    if (!_awaitingForce) return;
+    primary.force();
+    _awaitingForce = false;
     _dirty();
   }
 
@@ -179,14 +203,34 @@
       _emitPassThrough();
       return;
     }
-    if (_state == _State.COMPUTING_IS_PRIMARY || _isLazy) return;
+
+    // If we're in the process of running [isPrimary] or [declareOutputs], we
+    // already know that [apply] needs to be run so there's nothing we need to
+    // mark as dirty.
+    if (_state == _State.DECLARING) return;
+
+    // If we're waiting until [force] is called to run [apply], we don't want to
+    // run [apply] too early.
+    if (_awaitingForce) return;
+
+    if (_state == _State.APPLIED && deferred) {
+      // Transition to DECLARED, indicating that we know what outputs [apply]
+      // will emit but we're waiting to emit them concretely until [force] is
+      // called.
+      _state = _State.DECLARED;
+      _awaitingForce = true;
+      for (var controller in _outputControllers.values) {
+        controller.setLazy(force);
+      }
+      return;
+    }
 
     if (_passThroughController != null) _passThroughController.setDirty();
     for (var controller in _outputControllers.values) {
       controller.setDirty();
     }
 
-    if (_state == _State.APPLIED) {
+    if (_state == _State.APPLIED || _state == _State.DECLARED) {
       _apply();
     } else {
       _state = _State.NEEDS_APPLY;
@@ -210,10 +254,11 @@
     }).then((isPrimary) {
       if (_isRemoved) return null;
       if (isPrimary) {
+        if (!deferred) primary.force();
         return _declareOutputs().then((_) {
           if (_isRemoved) return;
-          if (_isLazy) {
-            _state = _State.APPLIED;
+          if (_awaitingForce) {
+            _state = _State.DECLARED;
             _onDoneController.add(null);
           } else {
             _apply();
@@ -253,7 +298,7 @@
       if (!_declaredOutputs.contains(primary.id)) _emitPassThrough();
 
       for (var id in _declaredOutputs) {
-        var controller = transformer is LazyTransformer
+        var controller = _awaitingForce
             ? new AssetNodeController.lazy(id, force, this)
             : new AssetNodeController(id, this);
         _outputControllers[id] = controller;
@@ -267,7 +312,7 @@
 
   /// Applies this transform.
   void _apply() {
-    assert(!_isRemoved && !_isLazy);
+    assert(!_isRemoved && !_awaitingForce);
 
     // Clear input subscriptions here as well as in [_process] because [_apply]
     // may be restarted independently if only a secondary input changes.
@@ -315,7 +360,7 @@
       }
 
       _inputSubscriptions.putIfAbsent(node.id, () {
-        return node.onStateChange.listen((_) => _dirty());
+        return node.onStateChange.listen((state) => _dirty());
       });
 
       return node.asset;
@@ -369,16 +414,6 @@
       phase.cascade.reportError(new InvalidOutputException(info, id));
     }
 
-    if (_declaredOutputs != null) {
-      var missingOutputs = _declaredOutputs.difference(
-          newOutputs.map((asset) => asset.id).toSet());
-      if (missingOutputs.isNotEmpty) {
-        _warn("This transformer didn't emit declared "
-            "${pluralize('output asset', missingOutputs.length)} "
-            "${toSentence(missingOutputs)}.");
-      }
-    }
-
     // Remove outputs that used to exist but don't anymore.
     for (var id in _outputControllers.keys.toList()) {
       if (newOutputs.containsId(id)) continue;
@@ -466,12 +501,23 @@
 
 /// The enum of states that [TransformNode] can be in.
 class _State {
-  /// The transform is running [Transformer.isPrimary].
+  /// The transform is running [Transformer.isPrimary] followed by
+  /// [DeclaringTransformer.declareOutputs] (for a [DeclaringTransformer]).
   ///
-  /// This is the initial state of the transformer. Once [Transformer.isPrimary]
-  /// finishes running, this will transition to [APPLYING] if the input is
-  /// primary, or [NOT_PRIMARY] if it's not.
-  static final COMPUTING_IS_PRIMARY = const _State._("computing isPrimary");
+  /// This is the initial state of the transformer, and it will only occur once
+  /// since [Transformer.isPrimary] and [DeclaringTransformer.declareOutputs]
+  /// are independent of the contents of the primary input. Once the two methods
+  /// finish running, this will transition to [NOT_PRIMARY] if the input isn't
+  /// primary, [DECLARED] if the transform is deferred, and [APPLYING]
+  /// otherwise.
+  static final DECLARING = const _State._("computing isPrimary");
+
+  /// The transform is deferred and has run
+  /// [DeclaringTransformer.declareOutputs] but hasn't yet been forced.
+  ///
+  /// This will transition to [APPLYING] when one of the outputs has been
+  /// forced.
+  static final DECLARED = const _State._("declared");
 
   /// The transform is running [Transformer.apply].
   ///
@@ -490,11 +536,12 @@
   /// The transform has finished running [Transformer.apply], whether or not it
   /// emitted an error.
   ///
-  /// If the transformer is lazy, the [TransformNode] can also be in this state
-  /// when [Transformer.declareOutputs] has been run but [Transformer.apply] has
-  /// not.
+  /// If the transformer is deferred, the [TransformNode] can also be in this
+  /// state when [Transformer.declareOutputs] has been run but
+  /// [Transformer.apply] has not.
   ///
-  /// If an input changes, this will transition to [APPLYING].
+  /// If an input changes, this will transition to [DECLARED] if the transform
+  /// is deferred and [APPLYING] otherwise.
   static final APPLIED = const _State._("applied");
 
   /// The transform has finished running [Transformer.isPrimary], which returned
diff --git a/pkg/barback/test/package_graph/declaring_transformer_test.dart b/pkg/barback/test/package_graph/declaring_transformer_test.dart
index 94aef14..19ac9ce 100644
--- a/pkg/barback/test/package_graph/declaring_transformer_test.dart
+++ b/pkg/barback/test/package_graph/declaring_transformer_test.dart
@@ -61,18 +61,19 @@
     buildShouldSucceed();
   });
 
-  test("fails to get a consumed asset before apply is finished", () {
-    var transformer = new DeclaringRewriteTransformer("blub", "blab")
-        ..consumePrimary = true;
-    initGraph(["app|foo.blub"], {"app": [[transformer]]});
-
-    transformer.pauseApply();
-    updateSources(["app|foo.blub"]);
-    expectNoAsset("app|foo.blub");
-
-    transformer.resumeApply();
-    buildShouldSucceed();
-  });
+  // TODO(nweiz): Enable this test when issue 18226 is fixed.
+  // test("fails to get a consumed asset before apply is finished", () {
+  //   var transformer = new DeclaringRewriteTransformer("blub", "blab")
+  //       ..consumePrimary = true;
+  //   initGraph(["app|foo.blub"], {"app": [[transformer]]});
+  //
+  //   transformer.pauseApply();
+  //   updateSources(["app|foo.blub"]);
+  //   expectNoAsset("app|foo.blub");
+  //
+  //   transformer.resumeApply();
+  //   buildShouldSucceed();
+  // });
 
   test("waits until apply is finished to get an overwritten asset", () {
     var transformer = new DeclaringRewriteTransformer("blub", "blub");
diff --git a/pkg/barback/test/package_graph/get_all_assets_test.dart b/pkg/barback/test/package_graph/get_all_assets_test.dart
index 7cadbdf..08da238 100644
--- a/pkg/barback/test/package_graph/get_all_assets_test.dart
+++ b/pkg/barback/test/package_graph/get_all_assets_test.dart
@@ -4,6 +4,9 @@
 
 library barback.test.barback_test;
 
+import 'dart:async';
+
+import 'package:barback/barback.dart';
 import 'package:scheduled_test/scheduled_test.dart';
 
 import '../utils.dart';
@@ -74,4 +77,24 @@
       isTransformerException(equals(BadTransformer.ERROR))
     ]));
   });
+
+  // Regression test.
+  test("getAllAssets() is called synchronously after after initializing "
+      "barback", () {
+    var provider = new MockProvider({
+      "app|a.txt": "a",
+      "app|b.txt": "b",
+      "app|c.txt": "c"
+    });
+    var barback = new Barback(provider);
+    barback.updateSources([
+      new AssetId.parse("app|a.txt"),
+      new AssetId.parse("app|b.txt"),
+      new AssetId.parse("app|c.txt")
+    ]);
+
+    expect(barback.getAllAssets().then((assets) {
+      return Future.wait(assets.map((asset) => asset.readAsString()));
+    }), completion(unorderedEquals(["a", "b", "c"])));
+  });
 }
diff --git a/pkg/barback/test/package_graph/lazy_transformer_test.dart b/pkg/barback/test/package_graph/lazy_transformer_test.dart
index 2f08772..24ae2e5 100644
--- a/pkg/barback/test/package_graph/lazy_transformer_test.dart
+++ b/pkg/barback/test/package_graph/lazy_transformer_test.dart
@@ -82,29 +82,139 @@
     expect(transformer.numRuns, completion(equals(1)));
   });
 
-  // test("a lazy asset piped into a declaring transformer isn't eagerly "
-  //     "compiled", () {
-  //   var transformer1 = new LazyRewriteTransformer("blub", "blab");
-  //   var transformer2 = new DeclaringRewriteTransformer("blab", "blib");
-  //   initGraph(["app|foo.blub"], {"app": [
-  //     [transformer1], [transformer2]
-  //   ]});
-  //   updateSources(["app|foo.blub"]);
-  //   buildShouldSucceed();
-  //   expect(transformer1.numRuns, completion(equals(0)));
-  //   expect(transformer2.numRuns, completion(equals(0)));
-  // });
-  //
-  // test("a lazy asset piped into a declaring transformer is compiled "
-  //     "on-demand", () {
-  //   initGraph(["app|foo.blub"], {"app": [
-  //     [new LazyRewriteTransformer("blub", "blab")],
-  //     [new DeclaringRewriteTransformer("blab", "blib")]
-  //   ]});
-  //   updateSources(["app|foo.blub"]);
-  //   expectAsset("app|foo.blib", "foo.blab.blib");
-  //   buildShouldSucceed();
-  // });
+  test("a lazy asset piped into a declaring transformer isn't eagerly "
+      "compiled", () {
+    var transformer1 = new LazyRewriteTransformer("blub", "blab");
+    var transformer2 = new DeclaringRewriteTransformer("blab", "blib");
+    initGraph(["app|foo.blub"], {"app": [
+      [transformer1], [transformer2]
+    ]});
+    updateSources(["app|foo.blub"]);
+    buildShouldSucceed();
+    expect(transformer1.numRuns, completion(equals(0)));
+    expect(transformer2.numRuns, completion(equals(0)));
+  });
+
+  test("a lazy asset piped into a declaring transformer is compiled "
+      "on-demand", () {
+    initGraph(["app|foo.blub"], {"app": [
+      [new LazyRewriteTransformer("blub", "blab")],
+      [new DeclaringRewriteTransformer("blab", "blib")]
+    ]});
+    updateSources(["app|foo.blub"]);
+    expectAsset("app|foo.blib", "foo.blab.blib");
+    buildShouldSucceed();
+  });
+
+  test("a lazy asset piped through many declaring transformers isn't eagerly "
+      "compiled", () {
+    var transformer1 = new LazyRewriteTransformer("one", "two");
+    var transformer2 = new DeclaringRewriteTransformer("two", "three");
+    var transformer3 = new DeclaringRewriteTransformer("three", "four");
+    var transformer4 = new DeclaringRewriteTransformer("four", "five");
+    initGraph(["app|foo.one"], {"app": [
+      [transformer1], [transformer2], [transformer3], [transformer4]
+    ]});
+    updateSources(["app|foo.one"]);
+    buildShouldSucceed();
+    expect(transformer1.numRuns, completion(equals(0)));
+    expect(transformer2.numRuns, completion(equals(0)));
+    expect(transformer3.numRuns, completion(equals(0)));
+    expect(transformer4.numRuns, completion(equals(0)));
+  });
+
+  test("a lazy asset piped through many declaring transformers is compiled "
+      "on-demand", () {
+    initGraph(["app|foo.one"], {"app": [
+      [new LazyRewriteTransformer("one", "two")],
+      [new DeclaringRewriteTransformer("two", "three")],
+      [new DeclaringRewriteTransformer("three", "four")],
+      [new DeclaringRewriteTransformer("four", "five")]
+    ]});
+    updateSources(["app|foo.one"]);
+    expectAsset("app|foo.five", "foo.two.three.four.five");
+    buildShouldSucceed();
+  });
+
+  test("a lazy asset piped into a non-lazy transformer that doesn't use its "
+      "outputs isn't eagerly compiled", () {
+    var transformer = new LazyRewriteTransformer("blub", "blab");
+    initGraph(["app|foo.blub"], {"app": [
+      [transformer],
+      [new RewriteTransformer("txt", "out")]
+    ]});
+    updateSources(["app|foo.blub"]);
+    buildShouldSucceed();
+    expect(transformer.numRuns, completion(equals(0)));
+  });
+
+  test("a lazy asset piped into a non-lazy transformer that doesn't use its "
+      "outputs is compiled on-demand", () {
+    initGraph(["app|foo.blub"], {"app": [
+      [new LazyRewriteTransformer("blub", "blab")],
+      [new RewriteTransformer("txt", "out")]
+    ]});
+    updateSources(["app|foo.blub"]);
+    expectAsset("app|foo.blab", "foo.blab");
+    buildShouldSucceed();
+  });
+
+  test("a lazy transformer followed by a non-lazy transformer is re-run "
+      "eagerly", () {
+    var rewrite = new LazyRewriteTransformer("one", "two");
+    initGraph(["app|foo.one"], {"app": [
+      [rewrite],
+      [new RewriteTransformer("two", "three")]
+    ]});
+
+    updateSources(["app|foo.one"]);
+    expectAsset("app|foo.three", "foo.two.three");
+    buildShouldSucceed();
+
+    updateSources(["app|foo.one"]);
+    buildShouldSucceed();
+
+    expect(rewrite.numRuns, completion(equals(2)));
+  });
+
+  test("a lazy transformer followed by a declaring transformer isn't re-run "
+      "eagerly", () {
+    var rewrite = new LazyRewriteTransformer("one", "two");
+    initGraph(["app|foo.one"], {"app": [
+      [rewrite],
+      [new DeclaringRewriteTransformer("two", "three")]
+    ]});
+
+    updateSources(["app|foo.one"]);
+    expectAsset("app|foo.three", "foo.two.three");
+    buildShouldSucceed();
+
+    updateSources(["app|foo.one"]);
+    buildShouldSucceed();
+
+    expect(rewrite.numRuns, completion(equals(1)));
+  });
+
+  test("a declaring transformer added after a materialized lazy transformer "
+      "is still deferred", () {
+    var lazy = new LazyRewriteTransformer("one", "two");
+    var declaring = new DeclaringRewriteTransformer("two", "three");
+    initGraph(["app|foo.one"], {"app": [[lazy]]});
+
+    updateSources(["app|foo.one"]);
+    expectAsset("app|foo.two", "foo.two");
+    buildShouldSucceed();
+
+    updateTransformers("app", [[lazy], [declaring]]);
+    expectAsset("app|foo.three", "foo.two.three");
+    buildShouldSucceed();
+
+    updateSources(["app|foo.one"]);
+    buildShouldSucceed();
+
+    expect(lazy.numRuns, completion(equals(1)));
+    expect(declaring.numRuns, completion(equals(1)));
+  });
 
   test("a lazy asset works as a cross-package input", () {
     initGraph({
@@ -134,8 +244,7 @@
     buildShouldSucceed();
   });
 
-  test("once a lazy transformer is materialized, it runs eagerly afterwards",
-      () {
+  test("after being materialized a lazy transformer is still lazy", () {
     var transformer = new LazyRewriteTransformer("blub", "blab");
     initGraph(["app|foo.blub"], {"app": [[transformer]]});
 
@@ -149,7 +258,25 @@
     updateSources(["app|foo.blub"]);
     buildShouldSucceed();
 
-    expect(transformer.numRuns, completion(equals(2)));
+    expect(transformer.numRuns, completion(equals(1)));
+  });
+
+  test("after being materialized a lazy transformer can be materialized again",
+      () {
+    var transformer = new LazyRewriteTransformer("blub", "blab");
+    initGraph(["app|foo.blub"], {"app": [[transformer]]});
+
+    updateSources(["app|foo.blub"]);
+    buildShouldSucceed();
+
+    // Request the asset once to force it to be materialized.
+    expectAsset("app|foo.blab", "foo.blab");
+    buildShouldSucceed();
+
+    modifyAsset("app|foo.blub", "bar");
+    updateSources(["app|foo.blub"]);
+    expectAsset("app|foo.blab", "bar.blab");
+    buildShouldSucceed();
   });
 
   test("an error emitted in a lazy transformer's declareOutputs method is "
@@ -227,7 +354,8 @@
     buildShouldSucceed();
   });
 
-  // Regression test.
+  // Regression tests.
+
   test("a lazy transformer that doesn't apply updates its passed-through asset",
       () {
     initGraph(["app|foo.txt"], {"app": [
@@ -249,4 +377,36 @@
     expectAsset("app|foo.txt", "bar");
     buildShouldSucceed();
   });
+
+  test("a lazy transformer is forced while the previous lazy transformer is "
+      "available, then the previous transformer becomes unavailable", () {
+    var assets = new LazyAssetsTransformer(["app|out.one", "app|out.two"]);
+    var rewrite = new LazyRewriteTransformer("two", "three");
+    initGraph(["app|foo.in"], {"app": [[assets], [rewrite]]});
+
+    updateSources(["app|foo.in"]);
+    // Request out.one so that [assets] runs but the second does not.
+    expectAsset("app|out.one", "app|out.one");
+    buildShouldSucceed();
+
+    // Start the [rewrite] running. The output from [assets] should still be
+    // available.
+    rewrite.pauseApply();
+    expectAssetDoesNotComplete("app|out.three");
+
+    // Mark [assets] as dirty. It should re-run, since [rewrite] still needs its
+    // input.
+    updateSources(["app|foo.in"]);
+    rewrite.resumeApply();
+
+    expectAsset("app|out.three", "app|out.two.three");
+    buildShouldSucceed();
+
+    // [assets] should run once for each time foo.in was updated.
+    expect(assets.numRuns, completion(equals(2)));
+
+    // [rewrite] should run once against [assets]'s original output and once
+    // against its new output.
+    expect(rewrite.numRuns, completion(equals(2)));
+  });
 }
diff --git a/pkg/barback/test/transformer/declare_assets.dart b/pkg/barback/test/transformer/declare_assets.dart
index 9a14525..d1158b8 100644
--- a/pkg/barback/test/transformer/declare_assets.dart
+++ b/pkg/barback/test/transformer/declare_assets.dart
@@ -22,9 +22,10 @@
   /// These assets' contents will be identical to their ids.
   final List<AssetId> emitted;
 
-  DeclareAssetsTransformer(Iterable<String> declared, Iterable<String> emitted)
+  DeclareAssetsTransformer(Iterable<String> declared, [Iterable<String> emitted])
       : this.declared = declared.map((id) => new AssetId.parse(id)).toList(),
-        this.emitted = emitted.map((id) => new AssetId.parse(id)).toList();
+        this.emitted = (emitted == null ? declared : emitted)
+            .map((id) => new AssetId.parse(id)).toList();
 
   Future<bool> doIsPrimary(AssetId id) => new Future.value(true);
 
diff --git a/pkg/barback/test/transformer/declaring_rewrite.dart b/pkg/barback/test/transformer/declaring_rewrite.dart
index 063ebbe..3dd3f64 100644
--- a/pkg/barback/test/transformer/declaring_rewrite.dart
+++ b/pkg/barback/test/transformer/declaring_rewrite.dart
@@ -12,7 +12,7 @@
 
 /// Like [RewriteTransformer], but declares its assets ahead of time.
 class DeclaringRewriteTransformer extends RewriteTransformer
-    implements LazyTransformer {
+    implements DeclaringTransformer {
   DeclaringRewriteTransformer(String from, String to)
       : super(from, to);
 
diff --git a/pkg/barback/test/transformer/lazy_assets.dart b/pkg/barback/test/transformer/lazy_assets.dart
new file mode 100644
index 0000000..b245d83
--- /dev/null
+++ b/pkg/barback/test/transformer/lazy_assets.dart
@@ -0,0 +1,18 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library barback.test.transformer.lazy_assets;
+
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+import 'declare_assets.dart';
+
+/// Like [DeclareAssetsTransformer], but lazy.
+class LazyAssetsTransformer extends DeclareAssetsTransformer
+    implements LazyTransformer {
+  LazyAssetsTransformer(Iterable<String> declared, [Iterable<String> emitted])
+      : super(declared, emitted);
+}
diff --git a/pkg/barback/test/utils.dart b/pkg/barback/test/utils.dart
index 66c6abf..ee038fc 100644
--- a/pkg/barback/test/utils.dart
+++ b/pkg/barback/test/utils.dart
@@ -28,6 +28,7 @@
 export 'transformer/declaring_rewrite.dart';
 export 'transformer/emit_nothing.dart';
 export 'transformer/has_input.dart';
+export 'transformer/lazy_assets.dart';
 export 'transformer/lazy_bad.dart';
 export 'transformer/lazy_many_to_one.dart';
 export 'transformer/lazy_rewrite.dart';
@@ -78,30 +79,7 @@
   if (assets == null) assets = [];
   if (transformers == null) transformers = {};
 
-  var assetList;
-  if (assets is Map) {
-    assetList = assets.keys.map((asset) {
-      var id = new AssetId.parse(asset);
-      return new _MockAsset(id, assets[asset]);
-    });
-  } else if (assets is Iterable) {
-    assetList = assets.map((asset) {
-      var id = new AssetId.parse(asset);
-      var contents = pathos.basenameWithoutExtension(id.path);
-      return new _MockAsset(id, contents);
-    });
-  }
-
-  var assetMap = mapMapValues(groupBy(assetList, (asset) => asset.id.package),
-      (package, assets) => new AssetSet.from(assets));
-
-  // Make sure that packages that have transformers but no assets are considered
-  // by MockProvider to exist.
-  for (var package in transformers.keys) {
-    assetMap.putIfAbsent(package, () => new AssetSet());
-  }
-
-  _provider = new MockProvider(assetMap);
+  _provider = new MockProvider(assets, additionalPackages: transformers.keys);
   _barback = new Barback(_provider);
   // Add a dummy listener to the log so it doesn't print to stdout.
   _barback.log.listen((_) {});
@@ -484,7 +462,7 @@
 class MockProvider implements PackageProvider {
   Iterable<String> get packages => _assets.keys;
 
-  Map<String, AssetSet> _assets;
+  final Map<String, AssetSet> _assets;
 
   /// The set of assets for which [MockLoadException]s should be emitted if
   /// they're loaded.
@@ -508,13 +486,40 @@
     _pauseCompleter = null;
   }
 
-  MockProvider(this._assets) {
+  MockProvider(assets, {Iterable<String> additionalPackages})
+      : _assets = _normalizeAssets(assets, additionalPackages);
+
+  static Map<String, AssetSet> _normalizeAssets(assets,
+      Iterable<String> additionalPackages) {
+    var assetList;
+    if (assets is Map) {
+      assetList = assets.keys.map((asset) {
+        var id = new AssetId.parse(asset);
+        return new _MockAsset(id, assets[asset]);
+      });
+    } else if (assets is Iterable) {
+      assetList = assets.map((asset) {
+        var id = new AssetId.parse(asset);
+        var contents = pathos.basenameWithoutExtension(id.path);
+        return new _MockAsset(id, contents);
+      });
+    }
+
+    var assetMap = mapMapValues(groupBy(assetList, (asset) => asset.id.package),
+        (package, assets) => new AssetSet.from(assets));
+
+    // Make sure that packages that have transformers but no assets are
+    // considered by MockProvider to exist.
+    if (additionalPackages != null) {
+      for (var package in additionalPackages) {
+        assetMap.putIfAbsent(package, () => new AssetSet());
+      }
+    }
+
     // If there are no assets or transformers, add a dummy package. This better
     // simulates the real world, where there'll always be at least the
     // entrypoint package.
-    if (_assets.isEmpty) {
-      _assets = {"app": new AssetSet()};
-    }
+    return assetMap.isEmpty ? {"app": new AssetSet()} : assetMap;
   }
 
   void _modifyAsset(String name, String contents) {
diff --git a/pkg/code_transformers/lib/src/entry_point.dart b/pkg/code_transformers/lib/src/entry_point.dart
index ee7868e..ed9eb28 100644
--- a/pkg/code_transformers/lib/src/entry_point.dart
+++ b/pkg/code_transformers/lib/src/entry_point.dart
@@ -7,21 +7,29 @@
 import 'package:analyzer/src/generated/ast.dart';
 import 'package:barback/barback.dart';
 
-/// Checks to see if the provided Asset is a Dart entry point.
+/// Checks to see if the provided AssetId is a Dart file in a directory which
+/// may contain entry points.
 ///
-/// Assets are considered entry points if they are Dart files located in
-/// web/, test/, benchmark/ or example/ and have a main() function.
+/// Directories are considered entry points if they are Dart files located in
+/// web/, test/, benchmark/ or example/.
+bool isPossibleDartEntryId(AssetId id) {
+  if (id.extension != '.dart') return false;
+
+  return ['benchmark', 'example', 'test', 'web']
+      .any((dir) => id.path.startsWith("$dir/"));
+}
+
+/// Checks to see if the provided Asset is possibly a Dart entry point.
+///
+/// Assets are considered entry points if they pass [isPossibleDartEntryId] and
+/// have a main() method.
 ///
 /// Because this only analyzes the primary asset this may return true for files
 /// which are not dart entries if the file does not have a main() but does have
 /// parts or exports.
 Future<bool> isPossibleDartEntry(Asset asset) {
-  if (asset.id.extension != '.dart') return new Future.value(false);
+  if (!isPossibleDartEntryId(asset.id)) return new Future.value(false);
 
-  if (!['benchmark', 'example', 'test', 'web']
-      .any((dir) => asset.id.path.startsWith("$dir/"))) {
-    return new Future.value(false);
-  }
   return asset.readAsString().then((contents) {
     return _couldBeEntrypoint(
         analyzer.parseCompilationUnit(contents, suppressErrors: true));
diff --git a/pkg/code_transformers/lib/src/resolver_impl.dart b/pkg/code_transformers/lib/src/resolver_impl.dart
index ad73da4..10e323c 100644
--- a/pkg/code_transformers/lib/src/resolver_impl.dart
+++ b/pkg/code_transformers/lib/src/resolver_impl.dart
@@ -159,8 +159,11 @@
       _context.applyChanges(changeSet);
       // Force resolve each entry point (the getter will ensure the library is
       // computed first).
-      _entryLibraries = entryPoints
-          .map((id) => _context.computeLibraryElement(sources[id])).toList();
+      _entryLibraries = entryPoints.map((id) {
+        var source = sources[id];
+        if (source == null) return null;
+        return _context.computeLibraryElement(source);
+      }).toList();
     });
   }
 
diff --git a/pkg/code_transformers/lib/src/resolvers.dart b/pkg/code_transformers/lib/src/resolvers.dart
index 077ea5b..4b768d4 100644
--- a/pkg/code_transformers/lib/src/resolvers.dart
+++ b/pkg/code_transformers/lib/src/resolvers.dart
@@ -5,8 +5,9 @@
 library code_transformers.src.resolvers;
 
 import 'dart:async';
-import 'package:barback/barback.dart' show AssetId, Transformer, Transform;
+import 'package:barback/barback.dart';
 
+import 'entry_point.dart';
 import 'resolver.dart';
 import 'resolver_impl.dart';
 
@@ -47,6 +48,24 @@
   /// The cache of resolvers- must be set from subclass.
   Resolvers resolvers;
 
+  /// By default only process prossible entry point assets.
+  ///
+  /// This is only a preliminary check based on the asset ID.
+  Future<bool> isPrimary(assetOrId) {
+    // assetOrId is to handle the transition from Asset to AssetID between
+    // pub 1.3 and 1.4. Once support for 1.3 is dropped this should only
+    // support AssetId.
+    var id = assetOrId is AssetId ? assetOrId : assetOrId.id;
+    return new Future.value(isPossibleDartEntryId(id));
+  }
+
+  /// Check to see if this should apply with the resolver on the provided asset.
+  ///
+  /// By default this will only apply on possible Dart entry points (see
+  /// [isPossibleDartEntry]).
+  Future<bool> shouldApplyResolver(Asset asset) => isPossibleDartEntry(asset);
+
+
   /// This provides a default implementation of `Transformer.apply` that will
   /// get and release resolvers automatically. Internally this:
   ///   * Gets a resolver associated with the transform primary input.
@@ -56,7 +75,11 @@
   ///
   /// Use [applyToEntryPoints] instead if you need to override the entry points
   /// to run the resolver on.
-  Future apply(Transform transform) => applyToEntryPoints(transform);
+  Future apply(Transform transform) =>
+      shouldApplyResolver(transform.primaryInput).then((result) {
+        if (result) return applyToEntryPoints(transform);
+      });
+
 
   /// Helper function to make it easy to write an `Transformer.apply` method
   /// that automatically gets and releases the resolver. This is typically used
diff --git a/pkg/code_transformers/pubspec.yaml b/pkg/code_transformers/pubspec.yaml
index 5792858..497ff67 100644
--- a/pkg/code_transformers/pubspec.yaml
+++ b/pkg/code_transformers/pubspec.yaml
@@ -1,5 +1,5 @@
 name: code_transformers
-version: 0.1.2-dev
+version: 0.1.3
 author: "Dart Team <misc@dartlang.org>"
 description: Collection of utilities related to creating barback transformers.
 homepage: http://www.dartlang.org
diff --git a/pkg/code_transformers/test/resolver_test.dart b/pkg/code_transformers/test/resolver_test.dart
index 1403b1f..3ab00e7 100644
--- a/pkg/code_transformers/test/resolver_test.dart
+++ b/pkg/code_transformers/test/resolver_test.dart
@@ -27,10 +27,10 @@
 
   group('Resolver', () {
 
-    test('should handle empty files', () {
+    test('should handle initial files', () {
       return validateResolver(
           inputs: {
-            'a|web/main.dart': '',
+            'a|web/main.dart': ' main() {}',
           },
           validator: (resolver) {
             var source = resolver.sources[entryPoint];
@@ -38,14 +38,14 @@
 
             var lib = resolver.getLibrary(entryPoint);
             expect(lib, isNotNull);
-            expect(lib.entryPoint, isNull);
           });
     });
 
     test('should update when sources change', () {
       return validateResolver(
           inputs: {
-            'a|web/main.dart': ''' main() {} ''',
+            'a|web/main.dart': ''' main() {
+                } ''',
           },
           validator: (resolver) {
             var source = resolver.sources[entryPoint];
diff --git a/pkg/custom_element/lib/custom_element.dart b/pkg/custom_element/lib/custom_element.dart
index b0a3b5b..fbf80eb 100644
--- a/pkg/custom_element/lib/custom_element.dart
+++ b/pkg/custom_element/lib/custom_element.dart
@@ -183,6 +183,8 @@
     host.children = value;
   }
 
+  String get baseUri => host.baseUri;
+
   List<Element> get children => host.children;
 
   set children(List<Element> value) {
diff --git a/pkg/docgen/bin/docgen.dart b/pkg/docgen/bin/docgen.dart
index eba859c..f55a807 100644
--- a/pkg/docgen/bin/docgen.dart
+++ b/pkg/docgen/bin/docgen.dart
@@ -8,6 +8,7 @@
 import 'package:logging/logging.dart';
 import 'package:path/path.dart' as path;
 
+// Must use relative paths because library imports mirrors via relative paths
 import '../lib/docgen.dart';
 
 /**
@@ -35,15 +36,15 @@
       path.join(options['sdk'], 'bin', 'dart') : 'dart';
 
   var excludedLibraries = options['exclude-lib'];
-  if(excludedLibraries == null) excludedLibraries = [];
+  if (excludedLibraries == null) excludedLibraries = [];
+
+  var indentJSON = options['indent-json'] as bool;
 
   docgen(files,
       packageRoot: options['package-root'],
-      outputToYaml: !options['json'],
       includePrivate: options['include-private'],
       includeSdk: includeSdk,
       parseSdk: options['parse-sdk'],
-      append: options['append'] && new Directory(options['out']).existsSync(),
       introFileName: introduction,
       out: options['out'],
       excludeLibraries: excludedLibraries,
@@ -53,7 +54,8 @@
       dartBinary: dartBinary,
       pubScript: pubScript,
       noDocs: options['no-docs'],
-      startPage: startPage);
+      startPage: startPage,
+      indentJSON: indentJSON);
 }
 
 /**
@@ -102,11 +104,6 @@
       callback: (verbose) {
         if (verbose) Logger.root.level = Level.FINEST;
       });
-  parser.addFlag('json', abbr: 'j',
-      help: 'Outputs to JSON. If negated, outputs to YAML. '
-        'If --append is used, it takes the file-format of the previous '
-        'run stated in library_list.json, ignoring the flag.',
-      negatable: true, defaultsTo: true);
   parser.addFlag('include-private',
       help: 'Flag to include private declarations.', negatable: false);
   parser.addFlag('include-sdk',
@@ -118,9 +115,6 @@
       defaultsTo: false, negatable: false);
   parser.addOption('package-root',
       help: 'Sets the package root of the library being analyzed.');
-  parser.addFlag('append',
-      help: 'Append to the docs folder, library_list.json and index.txt',
-      defaultsTo: false, negatable: false);
   parser.addFlag('compile', help: 'Clone the documentation viewer repo locally '
       '(if not already present) and compile with dart2js', defaultsTo: false,
       negatable: false);
@@ -153,6 +147,9 @@
         'of the package in this argument, e.g. --start-page=intl will make '
         'the start page of the viewer be the intl package.',
         defaultsTo: null);
+  parser.addFlag('indent-json',
+      help: 'Indents each level of JSON output by two spaces',
+      defaultsTo: false, negatable: true);
 
   return parser;
 }
diff --git a/pkg/docgen/lib/docgen.dart b/pkg/docgen/lib/docgen.dart
index f56bf69..c23cd7d 100644
--- a/pkg/docgen/lib/docgen.dart
+++ b/pkg/docgen/lib/docgen.dart
@@ -38,23 +38,23 @@
 ///
 /// Returned Future completes with true if document generation is successful.
 Future<bool> docgen(List<String> files, {String packageRoot,
-    bool outputToYaml: false, bool includePrivate: false,
-    bool includeSdk: false, bool parseSdk: false, bool append: false,
+    bool includePrivate: false, bool includeSdk: false, bool parseSdk: false,
     String introFileName: '', String out: gen.DEFAULT_OUTPUT_DIRECTORY,
     List<String> excludeLibraries: const [],
     bool includeDependentPackages: false, bool compile: false,
     bool serve: false, bool noDocs: false, String startPage, String pubScript,
-    String dartBinary}) {
+    String dartBinary, bool indentJSON: false}) {
   var result;
   if (!noDocs) {
     viewer.ensureMovedViewerCode();
     result = gen.generateDocumentation(files, packageRoot: packageRoot,
-        outputToYaml: outputToYaml, includePrivate: includePrivate,
-        includeSdk: includeSdk, parseSdk: parseSdk, append: append,
+        includePrivate: includePrivate,
+        includeSdk: includeSdk, parseSdk: parseSdk,
         introFileName: introFileName, out: out,
         excludeLibraries: excludeLibraries,
         includeDependentPackages: includeDependentPackages,
-        startPage: startPage, pubScript: pubScript, dartBinary: dartBinary);
+        startPage: startPage, pubScript: pubScript, dartBinary: dartBinary,
+        indentJSON: indentJSON);
     viewer.addBackViewerCode();
     if (compile || serve) {
       result.then((success) {
diff --git a/pkg/docgen/lib/src/dart2yaml.dart b/pkg/docgen/lib/src/dart2yaml.dart
deleted file mode 100644
index ac0a8ee..0000000
--- a/pkg/docgen/lib/src/dart2yaml.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-/**
- * This library is used to convert data from a map to a YAML string.
- */
-library docgen.dart2yaml;
-
-/**
- * Gets a String representing the input Map in YAML format.
- */
-String getYamlString(Map documentData) {
-  StringBuffer yaml = new StringBuffer();
-  _addLevel(yaml, documentData, 0);
-  return yaml.toString();
-}
-
-/**
- * This recursive function builds a YAML string from [documentData] and
- * adds it to [yaml].
- * The [level] input determines the indentation of the block being processed.
- * The [isList] input determines whether [documentData] is a member of an outer
- * lists of maps. A map must be preceeded with a '-' if it is to exist at the
- * same level of indentation in the YAML output as other members of the list.
- */
-void _addLevel(StringBuffer yaml, Map documentData, int level,
-               {bool isList: false}) {
-  // The order of the keys could be nondeterministic, but it is insufficient
-  // to just sort the keys no matter what, as their order could be significant
-  // (i.e. parameters to a method). The order of the keys should be enforced
-  // by the caller of this function.
-  var keys = documentData.keys.toList();
-  keys.forEach((key) {
-    _calcSpaces(level, yaml);
-    // Only the first entry of the map should be preceeded with a '-' since
-    // the map is a member of an outer list and the map as a whole must be
-    // marked as a single member of that list. See example 2.4 at
-    // http://www.yaml.org/spec/1.2/spec.html#id2759963
-    if (isList && key == keys.first) {
-      yaml.write("- ");
-      level++;
-    }
-    yaml.write("\"$key\" : ");
-    if (documentData[key] is Map) {
-      yaml.write("\n");
-      _addLevel(yaml, documentData[key], level + 1);
-    } else if (documentData[key] is List) {
-      var elements = documentData[key];
-      yaml.write("\n");
-      elements.forEach( (element) {
-        if (element is Map) {
-          _addLevel(yaml, element, level + 1, isList: true);
-        } else {
-          _calcSpaces(level + 1, yaml);
-          yaml.write("- ${_processElement(element)}");
-        }
-      });
-    } else {
-      yaml.write(_processElement(documentData[key]));
-    }
-  });
-}
-
-/**
- * Returns an escaped String form of the inputted element.
- */
-String _processElement(var element) {
-  var contents = element.toString()
-      .replaceAll('\\', r'\\')
-      .replaceAll('"', r'\"')
-      .replaceAll('\n', r'\n');
-  return '"$contents"\n';
-}
-
-/**
- * Based on the depth in the file, this function returns the correct spacing
- * for an element in the YAML output.
- */
-void _calcSpaces(int spaceLevel, StringBuffer yaml) {
-  for (int i = 0; i < spaceLevel; i++) {
-    yaml.write("  ");
-  }
-}
\ No newline at end of file
diff --git a/pkg/docgen/lib/src/exports/dart2js_mirrors.dart b/pkg/docgen/lib/src/exports/dart2js_mirrors.dart
new file mode 100644
index 0000000..894dd51
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/dart2js_mirrors.dart
@@ -0,0 +1,7 @@
+// 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 docgen.exports.dart2js_mirrors;
+
+export '../../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart';
diff --git a/pkg/docgen/lib/src/exports/libraries.dart b/pkg/docgen/lib/src/exports/libraries.dart
new file mode 100644
index 0000000..00163df
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/libraries.dart
@@ -0,0 +1,7 @@
+// 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 docgen.exports.libraries;
+
+export '../../../../../sdk/lib/_internal/libraries.dart';
diff --git a/pkg/docgen/lib/src/exports/mirrors_util.dart b/pkg/docgen/lib/src/exports/mirrors_util.dart
new file mode 100644
index 0000000..2479fbe
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/mirrors_util.dart
@@ -0,0 +1,7 @@
+// 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 docgen.exports.mirrors_util;
+
+export '../../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart';
diff --git a/pkg/docgen/lib/src/exports/source_mirrors.dart b/pkg/docgen/lib/src/exports/source_mirrors.dart
new file mode 100644
index 0000000..7c2ee9c
--- /dev/null
+++ b/pkg/docgen/lib/src/exports/source_mirrors.dart
@@ -0,0 +1,8 @@
+// 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 docgen.exports.source_mirrors;
+
+export '../../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart'
+    hide SourceLocation;
diff --git a/pkg/docgen/lib/src/generator.dart b/pkg/docgen/lib/src/generator.dart
index a77531b..9778b51 100644
--- a/pkg/docgen/lib/src/generator.dart
+++ b/pkg/docgen/lib/src/generator.dart
@@ -5,6 +5,7 @@
 library docgen.generator;
 
 import 'dart:async';
+import 'dart:collection';
 import 'dart:convert';
 import 'dart:io';
 
@@ -15,15 +16,13 @@
 import '../../../../sdk/lib/_internal/compiler/implementation/filenames.dart';
 import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/analyze.dart'
     as dart2js;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart'
-    as dart2js_mirrors;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
-    as dart2js_util;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
 import '../../../../sdk/lib/_internal/compiler/implementation/source_file_provider.dart';
-import '../../../../sdk/lib/_internal/libraries.dart';
 
-import 'dart2yaml.dart';
+import 'exports/dart2js_mirrors.dart' as dart2js_mirrors;
+import 'exports/libraries.dart';
+import 'exports/mirrors_util.dart' as dart2js_util;
+import 'exports/source_mirrors.dart';
+
 import 'io.dart';
 import 'library_helpers.dart';
 import 'models.dart';
@@ -59,19 +58,19 @@
 /// This option is useful when only the SDK libraries are needed.
 ///
 /// Returned Future completes with true if document generation is successful.
-Future<bool> generateDocumentation(List<String> files, {String packageRoot, bool
-    outputToYaml: true, bool includePrivate: false, bool includeSdk: false, bool
-    parseSdk: false, bool append: false, String introFileName: '', out:
-    DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries: const [], bool
-    includeDependentPackages: false, String startPage, String dartBinary, String
-    pubScript}) {
+Future<bool> generateDocumentation(List<String> files, {String packageRoot,
+    bool outputToYaml: true, bool includePrivate: false, bool includeSdk: false,
+    bool parseSdk: false, String introFileName: '',
+    out: DEFAULT_OUTPUT_DIRECTORY, List<String> excludeLibraries: const [], bool
+    includeDependentPackages: false, String startPage, String dartBinary,
+    String pubScript, bool indentJSON: false}) {
   _excluded = excludeLibraries;
   _pubScript = pubScript;
   _dartBinary = dartBinary;
 
   logger.onRecord.listen((record) => print(record.message));
 
-  _ensureOutputDirectory(out, append);
+  _ensureOutputDirectory(out);
   var updatedPackageRoot = _obtainPackageRoot(packageRoot, parseSdk, files);
 
   var requestedLibraries = _findLibrariesToDocument(files,
@@ -103,9 +102,8 @@
     librariesToDocument.addAll((includeSdk || parseSdk) ? sdkLibraries : []);
     librariesToDocument.removeWhere((x) => _excluded.contains(
         dart2js_util.nameOf(x)));
-    _documentLibraries(librariesToDocument, includeSdk: includeSdk,
-        outputToYaml: outputToYaml, append: append, parseSdk: parseSdk,
-        introFileName: introFileName, startPage: startPage);
+    _documentLibraries(librariesToDocument, includeSdk, parseSdk, introFileName,
+        startPage, indentJSON);
     return true;
   });
 }
@@ -128,32 +126,20 @@
     sdkRoot = path.normalize(path.absolute(path.join(root, 'dart-sdk')));
   }
   logger.info('SDK Root: ${sdkRoot}');
-  return analyzeLibraries(libraries, sdkRoot,
-      packageRoot: packageRoot);
+  return analyzeLibraries(libraries, sdkRoot, packageRoot: packageRoot);
 }
 
 /// Writes [text] to a file in the output directory.
-void _writeToFile(String text, String filename, {bool append: false}) {
+void _writeToFile(String text, String filename) {
   if (text == null) return;
-  Directory dir = new Directory(_outputDirectory);
-  if (!dir.existsSync()) {
-    dir.createSync();
-  }
-  if (path.split(filename).length > 1) {
-    var splitList = path.split(filename);
-    for (int i = 0; i < splitList.length; i++) {
-      var level = splitList[i];
-    }
-    for (var level in path.split(filename)) {
-      var subdir = new Directory(path.join(_outputDirectory, path.dirname(
-          filename)));
-      if (!subdir.existsSync()) {
-        subdir.createSync();
-      }
-    }
-  }
-  File file = new File(path.join(_outputDirectory, filename));
-  file.writeAsStringSync(text, mode: append ? FileMode.APPEND : FileMode.WRITE);
+
+  var filePath = path.join(_outputDirectory, filename);
+
+  var parentDir = new Directory(path.dirname(filePath));
+  if (!parentDir.existsSync()) parentDir.createSync(recursive: true);
+
+  new File(filePath)
+      .writeAsStringSync(text, mode: FileMode.WRITE);
 }
 
 /// Resolve all the links in the introductory comments for a given library or
@@ -171,10 +157,24 @@
       inlineSyntaxes: MARKDOWN_SYNTAXES);
 }
 
+int _indexableComparer(Indexable a, Indexable b) {
+  if (a is Library && b is Library) {
+    var compare = a.packageName.compareTo(b.packageName);
+    if (compare == 0) {
+      compare = a.name.compareTo(b.name);
+    }
+    return compare;
+  }
+
+  if (a is Library) return -1;
+  if (b is Library) return 1;
+
+  return a.qualifiedName.compareTo(b.qualifiedName);
+}
+
 /// Creates documentation for filtered libraries.
-void _documentLibraries(List<LibraryMirror> libs, {bool includeSdk: false, bool
-    outputToYaml: true, bool append: false, bool parseSdk: false, String
-    introFileName: '', String startPage}) {
+void _documentLibraries(List<LibraryMirror> libs, bool includeSdk,
+  bool parseSdk, String introFileName, String startPage, bool indentJson) {
   libs.forEach((lib) {
     // Files belonging to the SDK have a uri that begins with 'dart:'.
     if (includeSdk || !lib.uri.toString().startsWith('dart:')) {
@@ -182,101 +182,76 @@
     }
   });
 
-  var filteredEntities = new Set<Indexable>();
-  for (Map<String, Set<Indexable>> firstLevel in mirrorToDocgen.values) {
-    for (Set<Indexable> items in firstLevel.values) {
-      for (Indexable item in items) {
-        if (isFullChainVisible(item)) {
-          if (item is! Method ||
-              (item is Method && item.methodInheritedFrom == null)) {
-            filteredEntities.add(item);
-          }
-        }
+  var filteredEntities = new SplayTreeSet<Indexable>(_indexableComparer);
+  for (Indexable item in allIndexables) {
+    if (isFullChainVisible(item)) {
+      if (item is! Method ||
+          (item is Method && item.methodInheritedFrom == null)) {
+        filteredEntities.add(item);
       }
     }
   }
 
   // Outputs a JSON file with all libraries and their preview comments.
   // This will help the viewer know what libraries are available to read in.
-  Map<String, dynamic> libraryMap;
-
-  if (append) {
-    var docsDir = listDir(_outputDirectory);
-    if (!docsDir.contains('$_outputDirectory/library_list.json')) {
-      throw new StateError('No library_list.json');
-    }
-    libraryMap = JSON.decode(new File('$_outputDirectory/library_list.json'
-        ).readAsStringSync());
-    libraryMap['libraries'].addAll(filteredEntities.where((e) => e is Library
-        ).map((e) => e.previewMap));
-    var intro = libraryMap['introduction'];
-    var spacing = intro.isEmpty ? '' : '<br/><br/>';
-    libraryMap['introduction'] =
-        "$intro$spacing${_readIntroductionFile(introFileName, includeSdk)}";
-    outputToYaml = libraryMap['filetype'] == 'yaml';
-  } else {
-    libraryMap = {
+  Map<String, dynamic> libraryMap = {
       'libraries': filteredEntities.where((e) => e is Library).map((e) =>
           e.previewMap).toList(),
       'introduction': _readIntroductionFile(introFileName, includeSdk),
-      'filetype': outputToYaml ? 'yaml' : 'json'
+      'filetype': 'json'
     };
-  }
-  _writeOutputFiles(libraryMap, filteredEntities, outputToYaml, append,
-      startPage);
+
+  var encoder = new JsonEncoder.withIndent(indentJson ? '  ' : null);
+
+  _writeOutputFiles(libraryMap, filteredEntities, startPage, encoder);
 }
 
-/// Output all of the libraries and classes into json or yaml files for
-/// consumption by a viewer.
+/// Output all of the libraries and classes into json files for consumption by a
+/// viewer.
 void _writeOutputFiles(Map<String, dynamic> libraryMap, Iterable<Indexable>
-    filteredEntities, bool outputToYaml, bool append, String startPage) {
+    filteredEntities, String startPage, JsonEncoder encoder) {
   if (startPage != null) libraryMap['start-page'] = startPage;
 
-  _writeToFile(JSON.encode(libraryMap), 'library_list.json');
+  _writeToFile(encoder.convert(libraryMap), 'library_list.json');
 
   // Output libraries and classes to file after all information is generated.
   filteredEntities.where((e) => e is Class || e is Library).forEach((output) {
-    _writeIndexableToFile(output, outputToYaml);
+    _writeIndexableToFile(output, encoder);
   });
 
   // Outputs all the qualified names documented with their type.
   // This will help generate search results.
-  var sortedEntities = filteredEntities.map((e) =>
-      '${e.qualifiedName} ${e.typeName}').toList()..sort();
+  var sortedEntities = filteredEntities
+      .map((e) => '${e.qualifiedName} ${e.typeName}')
+      .toList();
 
-  _writeToFile(sortedEntities.join('\n') + '\n', 'index.txt', append: append);
-  var index = new Map.fromIterables(filteredEntities.map((e) => e.qualifiedName
-      ), filteredEntities.map((e) => e.typeName));
-  if (append) {
-    var previousIndex = JSON.decode(new File('$_outputDirectory/index.json'
-        ).readAsStringSync());
-    index.addAll(previousIndex);
-  }
-  _writeToFile(JSON.encode(index), 'index.json');
+  sortedEntities.sort();
+
+  var buffer = new StringBuffer()
+      ..writeAll(sortedEntities, '\n')
+      ..write('\n');
+
+  _writeToFile(buffer.toString(), 'index.txt');
+
+  var index = new SplayTreeMap.fromIterable(filteredEntities,
+      key: (e) => e.qualifiedName, value: (e) => e.typeName);
+
+  _writeToFile(encoder.convert(index), 'index.json');
 }
 
 /// Helper method to serialize the given Indexable out to a file.
-void _writeIndexableToFile(Indexable result, bool outputToYaml) {
-  var outputFile = result.fileName;
-  var output;
-  if (outputToYaml) {
-    output = getYamlString(result.toMap());
-    outputFile = outputFile + '.yaml';
-  } else {
-    output = JSON.encode(result.toMap());
-    outputFile = outputFile + '.json';
-  }
+void _writeIndexableToFile(Indexable result, JsonEncoder encoder) {
+  var outputFile = result.qualifiedName + '.json';
+  var output = encoder.convert(result.toMap());
   _writeToFile(output, outputFile);
 }
 
 /// Set the location of the ouput directory, and ensure that the location is
 /// available on the file system.
-void _ensureOutputDirectory(String outputDirectory, bool append) {
+void _ensureOutputDirectory(String outputDirectory) {
   _outputDirectory = outputDirectory;
-  if (!append) {
-    var dir = new Directory(_outputDirectory);
-    if (dir.existsSync()) dir.deleteSync(recursive: true);
-  }
+  var dir = new Directory(_outputDirectory);
+  if (dir.existsSync()) dir.deleteSync(recursive: true);
 }
 
 /// Analyzes set of libraries and provides a mirror system which can be used
@@ -309,8 +284,8 @@
 ///
 /// If packageRoot is not explicitly passed, we examine the files we're
 /// documenting to attempt to find a package root.
-String _obtainPackageRoot(String packageRoot, bool parseSdk, List<String> files)
-    {
+String _obtainPackageRoot(String packageRoot, bool parseSdk,
+    List<String> files) {
   if (packageRoot == null && !parseSdk) {
     var type = FileSystemEntity.typeSync(files.first);
     if (type == FileSystemEntityType.DIRECTORY) {
@@ -438,7 +413,6 @@
   logger.fine('Generated library for ${result.name}');
 }
 
-
 /// If we can't find the SDK introduction text, which will happen if running
 /// from a snapshot and using --parse-sdk or --include-sdk, then use this
 /// hard-coded version. This should be updated to be consistent with the text
diff --git a/pkg/docgen/lib/src/library_helpers.dart b/pkg/docgen/lib/src/library_helpers.dart
index 3bb5672..05d6f4b 100644
--- a/pkg/docgen/lib/src/library_helpers.dart
+++ b/pkg/docgen/lib/src/library_helpers.dart
@@ -7,11 +7,12 @@
 import 'package:logging/logging.dart';
 import 'package:markdown/markdown.dart' as markdown;
 
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
-    as dart2js_util;
+import 'exports/source_mirrors.dart';
+import 'exports/mirrors_util.dart' as dart2js_util;
 
-import 'models.dart';
+import 'models/indexable.dart';
+import 'models/library.dart';
+import 'models/dummy_mirror.dart';
 
 typedef DeclarationMirror LookupFunction(DeclarationSourceMirror declaration,
     String name);
@@ -40,7 +41,7 @@
 /// Return true if this item and all of its owners are all visible.
 bool isFullChainVisible(Indexable item) {
   return includePrivateMembers || (!item.isPrivate && (item.owner != null ?
-    isFullChainVisible(item.owner) : true));
+      isFullChainVisible(item.owner) : true));
 }
 
 /// Logger for printing out progress of documentation generation.
@@ -55,15 +56,6 @@
 Iterable<LibraryMirror> get sdkLibraries => _sdkLibraries;
 Iterable<LibraryMirror> _sdkLibraries;
 
-/// Index of all the dart2js mirrors examined to corresponding MirrorBased
-/// docgen objects.
-///
-/// Used for lookup because of the dart2js mirrors exports
-/// issue. The second level map is indexed by owner docName for faster lookup.
-/// Why two levels of lookup? Speed, man. Speed.
-final Map<String, Map<String, Set<Indexable>>> mirrorToDocgen = new Map<String,
-    Map<String, Set<Indexable>>>();
-
 ////// Top level resolution functions
 /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
 markdown.Node globalFixReference(String name) {
@@ -150,8 +142,8 @@
 /// [DummyMirror] that simply returns the original mirror's qualifiedName
 /// while behaving like a MirrorBased object.
 Indexable getDocgenObject(DeclarationMirror mirror, [Indexable owner]) {
-  Map<String, Set<Indexable>> docgenObj =
-      mirrorToDocgen[dart2js_util.qualifiedNameOf(mirror)];
+  Map<String, Indexable> docgenObj = lookupIndexableMap(mirror);
+
   if (docgenObj == null) {
     return new DummyMirror(mirror, owner);
   }
@@ -159,14 +151,12 @@
   var setToExamine = new Set();
   if (owner != null) {
     var firstSet = docgenObj[owner.docName];
-    if (firstSet != null) setToExamine.addAll(firstSet);
+    if (firstSet != null) setToExamine.add(firstSet);
     if (_coreLibrary != null && docgenObj[_coreLibrary.docName] != null) {
-      setToExamine.addAll(docgenObj[_coreLibrary.docName]);
+      setToExamine.add(docgenObj[_coreLibrary.docName]);
     }
   } else {
-    for (var value in docgenObj.values) {
-      setToExamine.addAll(value);
-    }
+    setToExamine.addAll(docgenObj.values);
   }
 
   Set<Indexable> results = new Set<Indexable>();
diff --git a/pkg/docgen/lib/src/mdn.dart b/pkg/docgen/lib/src/mdn.dart
index 073dbdd..f5fd69b 100644
--- a/pkg/docgen/lib/src/mdn.dart
+++ b/pkg/docgen/lib/src/mdn.dart
@@ -17,24 +17,24 @@
 
 /// Generates MDN comments from database.json.
 String mdnComment(String root, Logger logger, String domName) {
-   //Check if MDN is loaded.
- if (_mdn == null) {
-   // Reading in MDN related json file.
-   var mdnPath = p.join(root, 'utils/apidoc/mdn/database.json');
-   var mdnFile = new File(mdnPath);
-   if (mdnFile.existsSync()) {
-     _mdn = JSON.decode(mdnFile.readAsStringSync());
-   } else {
-     logger.warning("Cannot find MDN docs expected at $mdnPath");
-     _mdn = {};
-   }
- }
+  //Check if MDN is loaded.
+  if (_mdn == null) {
+    // Reading in MDN related json file.
+    var mdnPath = p.join(root, 'utils/apidoc/mdn/database.json');
+    var mdnFile = new File(mdnPath);
+    if (mdnFile.existsSync()) {
+      _mdn = JSON.decode(mdnFile.readAsStringSync());
+    } else {
+      logger.warning("Cannot find MDN docs expected at $mdnPath");
+      _mdn = {};
+    }
+  }
 
- var parts = domName.split('.');
- if (parts.length == 2) return _mdnMemberComment(parts[0], parts[1]);
- if (parts.length == 1) return _mdnTypeComment(parts[0]);
+  var parts = domName.split('.');
+  if (parts.length == 2) return _mdnMemberComment(parts[0], parts[1]);
+  if (parts.length == 1) return _mdnTypeComment(parts[0]);
 
- throw new StateError('More than two items is not supported: $parts');
+  throw new StateError('More than two items is not supported: $parts');
 }
 
 /// Generates the MDN Comment for variables and method DOM elements.
diff --git a/pkg/docgen/lib/src/models.dart b/pkg/docgen/lib/src/models.dart
index 2e59a61..ad61145 100644
--- a/pkg/docgen/lib/src/models.dart
+++ b/pkg/docgen/lib/src/models.dart
@@ -4,1070 +4,10 @@
 
 library docgen.models;
 
-import 'dart:io';
-
-import 'package:markdown/markdown.dart' as markdown;
-
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
-    as dart2js_util;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart'
-    as dart2js_mirrors;
-
-import 'library_helpers.dart';
-import 'mdn.dart';
-import 'model_helpers.dart';
-import 'package_helpers.dart';
-
-/// Docgen representation of an item to be documented, that wraps around a
-/// dart2js mirror.
-abstract class MirrorBased<TMirror extends DeclarationMirror> {
-  /// The original dart2js mirror around which this object wraps.
-  TMirror get mirror;
-
-  /// Return an informative [Object.toString] for debugging.
-  String toString() => "${super.toString()} - $mirror";
-}
-
-/// A Docgen wrapper around the dart2js mirror for a generic type.
-class Generic extends MirrorBased<TypeVariableMirror> {
-  final TypeVariableMirror mirror;
-
-  Generic(this.mirror);
-
-  Map toMap() => {
-    'name': dart2js_util.nameOf(mirror),
-    'type': dart2js_util.qualifiedNameOf(mirror.upperBound)
-  };
-}
-
-/// For types that we do not explicitly create or have not yet created in our
-/// entity map (like core types).
-class DummyMirror implements Indexable {
-  final DeclarationMirror mirror;
-  /// The library that contains this element, if any. Used as a hint to help
-  /// determine which object we're referring to when looking up this mirror in
-  /// our map.
-  final Indexable owner;
-  DummyMirror(this.mirror, [this.owner]);
-
-  String get docName {
-    if (mirror == null) return '';
-    if (mirror is LibraryMirror) {
-      return dart2js_util.qualifiedNameOf(mirror).replaceAll('.','-');
-    }
-    var mirrorOwner = mirror.owner;
-    if (mirrorOwner == null) return dart2js_util.qualifiedNameOf(mirror);
-    var simpleName = dart2js_util.nameOf(mirror);
-    if (mirror is MethodMirror && (mirror as MethodMirror).isConstructor) {
-      // We name constructors specially -- repeating the class name and a
-      // "-" to separate the constructor from its name (if any).
-      simpleName = '${dart2js_util.nameOf(mirrorOwner)}-$simpleName';
-    }
-    return getDocgenObject(mirrorOwner, owner).docName + '.' +
-        simpleName;
-  }
-
-  bool get isPrivate => mirror == null? false : mirror.isPrivate;
-
-  String get packageName {
-    var libMirror = _getOwningLibraryFromMirror(mirror);
-    if (libMirror != null) {
-      return getPackageName(libMirror);
-    }
-    return '';
-  }
-
-  String get packagePrefix => packageName == null || packageName.isEmpty ?
-      '' : '$packageName/';
-
-  LibraryMirror _getOwningLibraryFromMirror(DeclarationMirror mirror) {
-    if (mirror is LibraryMirror) return mirror;
-    if (mirror == null) return null;
-    return _getOwningLibraryFromMirror(mirror.owner);
-  }
-
-  noSuchMethod(Invocation invocation) {
-    throw new UnimplementedError(invocation.memberName.toString());
-  }
-}
-
-/// An item that is categorized in our mirrorToDocgen map, as a distinct,
-/// searchable element.
-///
-/// These are items that refer to concrete entities (a Class, for example,
-/// but not a Type, which is a "pointer" to a class) that we wish to be
-/// globally resolvable. This includes things such as class methods and
-/// variables, but parameters for methods are not "Indexable" as we do not want
-/// the user to be able to search for a method based on its parameter names!
-/// The set of indexable items also includes Typedefs, since the user can refer
-/// to them as concrete entities in a particular scope.
-abstract class Indexable<TMirror extends DeclarationMirror>
-    extends MirrorBased<TMirror> {
-
-  Library get owningLibrary => owner.owningLibrary;
-
-  String get qualifiedName => fileName;
-  final TMirror mirror;
-  final bool isPrivate;
-  /// The comment text pre-resolution. We keep this around because inherited
-  /// methods need to resolve links differently from the superclass.
-  String _unresolvedComment = '';
-
-  Indexable(TMirror mirror)
-      : this.mirror = mirror,
-        this.isPrivate = isHidden(mirror) {
-
-    var map = mirrorToDocgen[dart2js_util.qualifiedNameOf(this.mirror)];
-    if (map == null) map = new Map<String, Set<Indexable>>();
-
-    var set = map[owner.docName];
-    if (set == null) set = new Set<Indexable>();
-    set.add(this);
-    map[owner.docName] = set;
-    mirrorToDocgen[dart2js_util.qualifiedNameOf(this.mirror)] = map;
-  }
-
-  /// Returns this object's qualified name, but following the conventions
-  /// we're using in Dartdoc, which is that library names with dots in them
-  /// have them replaced with hyphens.
-  String get docName;
-
-  /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
-  markdown.Node fixReference(String name) {
-    // Attempt the look up the whole name up in the scope.
-    String elementName = findElementInScope(name);
-    if (elementName != null) {
-      return new markdown.Element.text('a', elementName);
-    }
-    return fixComplexReference(name);
-  }
-
-  /// Look for the specified name starting with the current member, and
-  /// progressively working outward to the current library scope.
-  String findElementInScope(String name) =>
-      findElementInScopeWithPrefix(name, packagePrefix);
-
-  /// The reference to this element based on where it is printed as a
-  /// documentation file and also the unique URL to refer to this item.
-  ///
-  /// The qualified name (for URL purposes) and the file name are the same,
-  /// of the form packageName/ClassName or packageName/ClassName.methodName.
-  /// This defines both the URL and the directory structure.
-  String get fileName =>  packagePrefix + ownerPrefix + name;
-
-  /// The full docName of the owner element, appended with a '.' for this
-  /// object's name to be appended.
-  String get ownerPrefix => owner.docName != '' ? owner.docName + '.' : '';
-
-  /// The prefix String to refer to the package that this item is in, for URLs
-  /// and comment resolution.
-  ///
-  /// The prefix can be prepended to a qualified name to get a fully unique
-  /// name among all packages.
-  String get packagePrefix => '';
-
-  /// Documentation comment with converted markdown and all links resolved.
-  String _comment;
-
-  /// Accessor to documentation comment with markdown converted to html and all
-  /// links resolved.
-  String get comment {
-    if (_comment != null) return _comment;
-
-    _comment = _commentToHtml();
-    if (_comment.isEmpty) {
-      _comment = _mdnComment();
-    }
-    return _comment;
-  }
-
-  void set comment(x) {
-    _comment = x;
-  }
-
-  /// The simple name to refer to this item.
-  String get name => dart2js_util.nameOf(mirror);
-
-  /// Accessor to the parent item that owns this item.
-  ///
-  /// "Owning" is defined as the object one scope-level above which this item
-  /// is defined. Ex: The owner for a top level class, would be its enclosing
-  /// library. The owner of a local variable in a method would be the enclosing
-  /// method.
-  Indexable get owner => new DummyMirror(mirror.owner);
-
-  /// Generates MDN comments from database.json.
-  String _mdnComment();
-
-  /// The type of this member to be used in index.txt.
-  String get typeName => '';
-
-  /// Creates a [Map] with this [Indexable]'s name and a preview comment.
-  Map get previewMap {
-    var finalMap = { 'name' : name, 'qualifiedName' : qualifiedName };
-    var preview = _preview;
-    if(preview != null) finalMap['preview'] = preview;
-    return finalMap;
-  }
-
-  String get _preview {
-    if (comment != '') {
-      var index = comment.indexOf('</p>');
-      return index > 0 ?
-          '${comment.substring(0, index)}</p>' :
-          '<p><i>Comment preview not available</i></p>';
-    }
-    return null;
-  }
-
-  /// Accessor to obtain the raw comment text for a given item, _without_ any
-  /// of the links resolved.
-  String get _commentText {
-    String commentText;
-    mirror.metadata.forEach((metadata) {
-      if (metadata is CommentInstanceMirror) {
-        CommentInstanceMirror comment = metadata;
-        if (comment.isDocComment) {
-          if (commentText == null) {
-            commentText = comment.trimmedText;
-          } else {
-            commentText = '$commentText\n${comment.trimmedText}';
-          }
-        }
-      }
-    });
-    return commentText;
-  }
-
-  /// Returns any documentation comments associated with a mirror with
-  /// simple markdown converted to html.
-  ///
-  /// By default we resolve any comment references within our own scope.
-  /// However, if a method is inherited, we want the inherited comments, but
-  /// links to the subclasses's version of the methods.
-  String _commentToHtml([Indexable resolvingScope]) {
-    if (resolvingScope == null) resolvingScope = this;
-    var commentText = _commentText;
-    _unresolvedComment = commentText;
-
-    var linkResolver = (name) => resolvingScope.fixReference(name);
-    commentText = commentText == null ? '' :
-        markdown.markdownToHtml(commentText.trim(), linkResolver: linkResolver,
-            inlineSyntaxes: MARKDOWN_SYNTAXES);
-    return commentText;
-  }
-
-  /// Return a map representation of this type.
-  Map toMap();
-
-  /// Expand the method map [mapToExpand] into a more detailed map that
-  /// separates out setters, getters, constructors, operators, and methods.
-  Map _expandMethodMap(Map<String, Method> mapToExpand) => {
-    'setters': recurseMap(filterMap(mapToExpand,
-        (key, val) => val.mirror.isSetter)),
-    'getters': recurseMap(filterMap(mapToExpand,
-        (key, val) => val.mirror.isGetter)),
-    'constructors': recurseMap(filterMap(mapToExpand,
-        (key, val) => val.mirror.isConstructor)),
-    'operators': recurseMap(filterMap(mapToExpand,
-        (key, val) => val.mirror.isOperator)),
-    'methods': recurseMap(filterMap(mapToExpand,
-        (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator))
-  };
-
-  /// Accessor to determine if this item and all of its owners are visible.
-  bool get isVisible => isFullChainVisible(this);
-
-  /// Returns true if [mirror] is the correct type of mirror that this Docgen
-  /// object wraps. (Workaround for the fact that Types are not first class.)
-  bool isValidMirror(DeclarationMirror mirror);
-}
-
-/// A class containing contents of a Dart library.
-class Library extends Indexable {
-  final Map<String, Class> classes = {};
-  final Map<String, Typedef> typedefs = {};
-  final Map<String, Class> errors = {};
-
-  /// Top-level variables in the library.
-  Map<String, Variable> variables;
-
-  /// Top-level functions in the library.
-  Map<String, Method> functions;
-
-  String packageName = '';
-  bool _hasBeenCheckedForPackage = false;
-  String packageIntro;
-
-  Library get owningLibrary => this;
-
-  /// Returns the [Library] for the given [mirror] if it has already been
-  /// created, else creates it.
-  factory Library(LibraryMirror mirror) {
-    var library = getDocgenObject(mirror);
-    if (library is DummyMirror) {
-      library = new Library._(mirror);
-    }
-    return library;
-  }
-
-  Library._(LibraryMirror libraryMirror) : super(libraryMirror) {
-    var exported = calcExportedItems(libraryMirror);
-    var exportedClasses = addAll(exported['classes'],
-        dart2js_util.typesOf(libraryMirror.declarations));
-    updateLibraryPackage(mirror);
-    exportedClasses.forEach((String mirrorName, TypeMirror mirror) {
-        if (mirror is TypedefMirror) {
-          // This is actually a Dart2jsTypedefMirror, and it does define value,
-          // but we don't have visibility to that type.
-          if (includePrivateMembers || !mirror.isPrivate) {
-            typedefs[dart2js_util.nameOf(mirror)] = new Typedef(mirror, this);
-          }
-        } else if (mirror is ClassMirror) {
-          var clazz = new Class(mirror, this);
-
-          if (clazz.isError()) {
-            errors[dart2js_util.nameOf(mirror)] = clazz;
-          } else {
-            classes[dart2js_util.nameOf(mirror)] = clazz;
-          }
-        } else {
-          throw new ArgumentError(
-              '${dart2js_util.nameOf(mirror)} - no class type match. ');
-        }
-    });
-    this.functions = createMethods(addAll(exported['methods'],
-        libraryMirror.declarations.values.where(
-            (mirror) => mirror is MethodMirror)).values, this);
-    this.variables = createVariables(addAll(exported['variables'],
-        dart2js_util.variablesOf(libraryMirror.declarations)).values, this);
-  }
-
-  /// Look for the specified name starting with the current member, and
-  /// progressively working outward to the current library scope.
-  String findElementInScope(String name) {
-    var lookupFunc = determineLookupFunc(name);
-    var libraryScope = lookupFunc(mirror, name);
-    if (libraryScope != null) {
-      var result = getDocgenObject(libraryScope, this);
-      if (result is DummyMirror) return packagePrefix + result.docName;
-      return result.packagePrefix + result.docName;
-    }
-    return super.findElementInScope(name);
-  }
-
-  String _mdnComment() => '';
-
-  /// For a library's [mirror], determine the name of the package (if any) we
-  /// believe it came from (because of its file URI).
-  ///
-  /// If no package could be determined, we return an empty string.
-  void updateLibraryPackage(LibraryMirror mirror) {
-    if (mirror == null) return;
-    if (_hasBeenCheckedForPackage) return;
-    _hasBeenCheckedForPackage = true;
-    if (mirror.uri.scheme != 'file') return;
-    packageName = getPackageName(mirror);
-    // Associate the package readme with all the libraries. This is a bit
-    // wasteful, but easier than trying to figure out which partial match
-    // is best.
-    packageIntro = _packageIntro(getPackageDirectory(mirror));
-  }
-
-  String _packageIntro(packageDir) {
-    if (packageDir == null) return null;
-    var dir = new Directory(packageDir);
-    var files = dir.listSync();
-    var readmes = files.where((FileSystemEntity each) => (each is File &&
-        each.path.substring(packageDir.length + 1, each.path.length)
-          .startsWith('README'))).toList();
-    if (readmes.isEmpty) return '';
-    // If there are multiples, pick the shortest name.
-    readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
-    var readme = readmes.first;
-    var linkResolver = (name) => globalFixReference(name);
-    var contents = markdown.markdownToHtml(readme
-      .readAsStringSync(), linkResolver: linkResolver,
-      inlineSyntaxes: MARKDOWN_SYNTAXES);
-    return contents;
-  }
-
-  String get packagePrefix => packageName == null || packageName.isEmpty ?
-      '' : '$packageName/';
-
-  Map get previewMap {
-    var basic = super.previewMap;
-    basic['packageName'] = packageName;
-    if (packageIntro != null) {
-      basic['packageIntro'] = packageIntro;
-    }
-    return basic;
-  }
-
-  String get name => docName;
-
-  String get docName {
-    return dart2js_util.qualifiedNameOf(mirror).replaceAll('.','-');
-  }
-
-  /// Checks if the given name is a key for any of the Class Maps.
-  bool containsKey(String name) =>
-      classes.containsKey(name) || errors.containsKey(name);
-
-  /// Generates a map describing the [Library] object.
-  Map toMap() => {
-    'name': name,
-    'qualifiedName': qualifiedName,
-    'comment': comment,
-    'variables': recurseMap(variables),
-    'functions': _expandMethodMap(functions),
-    'classes': {
-      'class': classes.values.where((c) => c.isVisible)
-        .map((e) => e.previewMap).toList(),
-      'typedef': recurseMap(typedefs),
-      'error': errors.values.where((e) => e.isVisible)
-          .map((e) => e.previewMap).toList()
-    },
-    'packageName': packageName,
-    'packageIntro' : packageIntro
-  };
-
-  String get typeName => 'library';
-
-  bool isValidMirror(DeclarationMirror mirror) => mirror is LibraryMirror;
-}
-
-abstract class OwnedIndexable<TMirror extends DeclarationMirror>
-    extends Indexable<TMirror> {
-  /// List of the meta annotations on this item.
-  final List<Annotation> annotations;
-
-  /// The object one scope-level above which this item is defined.
-  ///
-  /// Ex: The owner for a top level class, would be its enclosing library.
-  /// The owner of a local variable in a method would be the enclosing method.
-  final Indexable owner;
-
-  /// Returns this object's qualified name, but following the conventions
-  /// we're using in Dartdoc, which is that library names with dots in them
-  /// have them replaced with hyphens.
-  String get docName => owner.docName + '.' + dart2js_util.nameOf(mirror);
-
-  OwnedIndexable(DeclarationMirror mirror, Indexable owner)
-      : annotations = createAnnotations(mirror, owner.owningLibrary),
-        this.owner = owner,
-        super(mirror);
-
-  /// Generates MDN comments from database.json.
-  String _mdnComment() {
-    var domAnnotation = this.annotations.firstWhere(
-        (e) => e.mirror.qualifiedName == #metadata.DomName,
-        orElse: () => null);
-    if (domAnnotation == null) return '';
-    var domName = domAnnotation.parameters.single;
-
-    return mdnComment(rootDirectory, logger, domName);
-  }
-
-  String get packagePrefix => owner.packagePrefix;
-}
-
-/// A class containing contents of a Dart class.
-class Class
-    extends OwnedIndexable<dart2js_mirrors.Dart2JsInterfaceTypeMirror>
-    implements Comparable<Class> {
-
-  /// List of the names of interfaces that this class implements.
-  List<Class> interfaces = [];
-
-  /// Names of classes that extends or implements this class.
-  Set<Class> subclasses = new Set<Class>();
-
-  /// Top-level variables in the class.
-  Map<String, Variable> variables;
-
-  /// Inherited variables in the class.
-  final Map<String, Variable> inheritedVariables = {};
-
-  /// Methods in the class.
-  Map<String, Method> methods;
-
-  final Map<String, Method> inheritedMethods = new Map<String, Method>();
-
-  /// Generic infomation about the class.
-  final Map<String, Generic> generics;
-
-  Class superclass;
-  bool get isAbstract => mirror.isAbstract;
-
-  /// Make sure that we don't check for inherited comments more than once.
-  bool _commentsEnsured = false;
-
-  /// Returns the [Class] for the given [mirror] if it has already been created,
-  /// else creates it.
-  factory Class(ClassMirror mirror, Library owner) {
-    var clazz = getDocgenObject(mirror, owner);
-    if (clazz is DummyMirror) {
-      clazz = new Class._(mirror, owner);
-    }
-    return clazz;
-  }
-
-  /// Called when we are constructing a superclass or interface class, but it
-  /// is not known if it belongs to the same owner as the original class. In
-  /// this case, we create an object whose owner is what the original mirror
-  /// says it is.
-  factory Class._possiblyDifferentOwner(ClassMirror mirror,
-      Library originalOwner) {
-    if (mirror.owner is LibraryMirror) {
-      var realOwner = getDocgenObject(mirror.owner);
-      if (realOwner is Library) {
-        return new Class(mirror, realOwner);
-      } else {
-        return new Class(mirror, originalOwner);
-      }
-    } else {
-      return new Class(mirror, originalOwner);
-    }
-  }
-
-  Class._(ClassSourceMirror classMirror, Indexable owner)
-      : generics = createGenerics(classMirror),
-        super(classMirror, owner) {
-
-    // The reason we do this madness is the superclass and interface owners may
-    // not be this class's owner!! Example: BaseClient in http pkg.
-    var superinterfaces = classMirror.superinterfaces.map(
-        (interface) => new Class._possiblyDifferentOwner(interface, owner));
-    this.superclass = classMirror.superclass == null? null :
-        new Class._possiblyDifferentOwner(classMirror.superclass, owner);
-
-    interfaces = superinterfaces.toList();
-    variables = createVariables(
-        dart2js_util.variablesOf(classMirror.declarations), this);
-    methods = createMethods(classMirror.declarations.values.where(
-        (mirror) => mirror is MethodMirror), this);
-
-    // Tell superclass that you are a subclass, unless you are not
-    // visible or an intermediary mixin class.
-    if (!classMirror.isNameSynthetic && isVisible && superclass != null) {
-      superclass.addSubclass(this);
-    }
-
-    if (this.superclass != null) addInherited(superclass);
-    interfaces.forEach((interface) => addInherited(interface));
-  }
-
-  String _lookupInClassAndSuperclasses(String name) {
-    var lookupFunc = determineLookupFunc(name);
-    var classScope = this;
-    while (classScope != null) {
-      var classFunc = lookupFunc(classScope.mirror, name);
-      if (classFunc != null) {
-        return packagePrefix + getDocgenObject(classFunc, owner).docName;
-      }
-      classScope = classScope.superclass;
-    }
-    return null;
-  }
-
-  /// Look for the specified name starting with the current member, and
-  /// progressively working outward to the current library scope.
-  String findElementInScope(String name) {
-    var lookupFunc = determineLookupFunc(name);
-    var result = _lookupInClassAndSuperclasses(name);
-    if (result != null) {
-      return result;
-    }
-    result = owner.findElementInScope(name);
-    return result == null ? super.findElementInScope(name) : result;
-  }
-
-  String get typeName => 'class';
-
-  /// Add all inherited variables and methods from the provided superclass.
-  /// If [_includePrivate] is true, it also adds the variables and methods from
-  /// the superclass.
-  void addInherited(Class superclass) {
-    inheritedVariables.addAll(superclass.inheritedVariables);
-    inheritedVariables.addAll(_allButStatics(superclass.variables));
-    addInheritedMethod(superclass, this);
-  }
-
-  /** [newParent] refers to the actual class is currently using these methods.
-   * which may be different because with the mirror system, we only point to the
-   * original canonical superclasse's method.
-   */
-  void addInheritedMethod(Class parent, Class newParent) {
-    parent.inheritedMethods.forEach((name, method) {
-      if(!method.mirror.isConstructor){
-        inheritedMethods[name] = new Method(method.mirror, newParent, method);
-      }}
-    );
-    _allButStatics(parent.methods).forEach((name, method) {
-      if (!method.mirror.isConstructor) {
-        inheritedMethods[name] = new Method(method.mirror, newParent, method);
-      }}
-    );
-  }
-
-  /// Remove statics from the map of inherited items before adding them.
-  Map _allButStatics(Map items) {
-    var result = {};
-    items.forEach((name, item) {
-      if (!item.isStatic) {
-        result[name] = item;
-      }
-    });
-    return result;
-  }
-
-  /// Add the subclass to the class.
-  ///
-  /// If [this] is private (or an intermediary mixin class), it will add the
-  /// subclass to the list of subclasses in the superclasses.
-  void addSubclass(Class subclass) {
-    if (docName == 'dart-core.Object') return;
-
-    if (!includePrivateMembers && isPrivate || mirror.isNameSynthetic) {
-      if (superclass != null) superclass.addSubclass(subclass);
-      interfaces.forEach((interface) {
-        interface.addSubclass(subclass);
-      });
-    } else {
-      subclasses.add(subclass);
-    }
-  }
-
-  /// Check if this [Class] is an error or exception.
-  bool isError() {
-    if (qualifiedName == 'dart-core.Error' ||
-        qualifiedName == 'dart-core.Exception')
-      return true;
-    for (var interface in interfaces) {
-      if (interface.isError()) return true;
-    }
-    if (superclass == null) return false;
-    return superclass.isError();
-  }
-
-  /// Makes sure that all methods with inherited equivalents have comments.
-  void ensureComments() {
-    if (_commentsEnsured) return;
-    _commentsEnsured = true;
-    if (superclass != null) superclass.ensureComments();
-    inheritedMethods.forEach((qualifiedName, inheritedMethod) {
-      var method = methods[qualifiedName];
-      if (method != null) {
-        // if we have overwritten this method in this class, we still provide
-        // the opportunity to inherit the comments.
-        method.ensureCommentFor(inheritedMethod);
-      }
-    });
-    // we need to populate the comments for all methods. so that the subclasses
-    // can get for their inherited versions the comments.
-    methods.forEach((qualifiedName, method) {
-      if (!method.mirror.isConstructor) method.ensureCommentFor(method);
-    });
-  }
-
-  /// If a class extends a private superclass, find the closest public
-  /// superclass of the private superclass.
-  String validSuperclass() {
-    if (superclass == null) return 'dart-core.Object';
-    if (superclass.isVisible) return superclass.qualifiedName;
-    return superclass.validSuperclass();
-  }
-
-  /// Generates a map describing the [Class] object.
-  Map toMap() => {
-    'name': name,
-    'qualifiedName': qualifiedName,
-    'comment': comment,
-    'isAbstract' : isAbstract,
-    'superclass': validSuperclass(),
-    'implements': interfaces.where((i) => i.isVisible)
-        .map((e) => e.qualifiedName).toList(),
-    'subclass': (subclasses.toList()..sort())
-        .map((x) => x.qualifiedName).toList(),
-    'variables': recurseMap(variables),
-    'inheritedVariables': recurseMap(inheritedVariables),
-    'methods': _expandMethodMap(methods),
-    'inheritedMethods': _expandMethodMap(inheritedMethods),
-    'annotations': annotations.map((a) => a.toMap()).toList(),
-    'generics': recurseMap(generics)
-  };
-
-  int compareTo(Class other) => name.compareTo(other.name);
-
-  bool isValidMirror(DeclarationMirror mirror) => mirror is ClassMirror;
-}
-
-class Typedef extends OwnedIndexable {
-  final String returnType;
-
-  final Map<String, Parameter> parameters;
-
-  /// Generic information about the typedef.
-  final Map<String, Generic> generics;
-
-  /// Returns the [Library] for the given [mirror] if it has already been
-  /// created, else creates it.
-  factory Typedef(TypedefMirror mirror, Library owningLibrary) {
-    var aTypedef = getDocgenObject(mirror, owningLibrary);
-    if (aTypedef is DummyMirror) {
-      aTypedef = new Typedef._(mirror, owningLibrary);
-    }
-    return aTypedef;
-  }
-
-  Typedef._(TypedefMirror mirror, Library owningLibrary)
-      : returnType = getDocgenObject(mirror.referent.returnType).docName,
-        generics = createGenerics(mirror),
-        parameters = createParameters(mirror.referent.parameters,
-            owningLibrary),
-        super(mirror, owningLibrary);
-
-  Map toMap() {
-    var map = {
-      'name': name,
-      'qualifiedName': qualifiedName,
-      'comment': comment,
-      'return': returnType,
-      'parameters': recurseMap(parameters),
-      'annotations': annotations.map((a) => a.toMap()).toList(),
-      'generics': recurseMap(generics)
-    };
-
-    // Typedef is displayed on the library page as a class, so a preview is
-    // added manually
-    var preview = _preview;
-    if(preview != null) map['preview'] = preview;
-
-    return map;
-  }
-
-  markdown.Node fixReference(String name) => null;
-
-  String get typeName => 'typedef';
-
-  bool isValidMirror(DeclarationMirror mirror) => mirror is TypedefMirror;
-}
-
-/// A class containing properties of a Dart variable.
-class Variable extends OwnedIndexable {
-
-  bool isFinal;
-  bool isStatic;
-  bool isConst;
-  Type type;
-  String _variableName;
-
-  factory Variable(String variableName, VariableMirror mirror,
-      Indexable owner) {
-    var variable = getDocgenObject(mirror);
-    if (variable is DummyMirror) {
-      return new Variable._(variableName, mirror, owner);
-    }
-    return variable;
-  }
-
-  Variable._(this._variableName, VariableMirror mirror, Indexable owner) :
-      super(mirror, owner) {
-    isFinal = mirror.isFinal;
-    isStatic = mirror.isStatic;
-    isConst = mirror.isConst;
-    type = new Type(mirror.type, owner.owningLibrary);
-  }
-
-  String get name => _variableName;
-
-  /// Generates a map describing the [Variable] object.
-  Map toMap() => {
-    'name': name,
-    'qualifiedName': qualifiedName,
-    'comment': comment,
-    'final': isFinal,
-    'static': isStatic,
-    'constant': isConst,
-    'type': new List.filled(1, type.toMap()),
-    'annotations': annotations.map((a) => a.toMap()).toList()
-  };
-
-  String get typeName => 'property';
-
-  get comment {
-    if (_comment != null) return _comment;
-    if (owner is Class) {
-      (owner as Class).ensureComments();
-    }
-    return super.comment;
-  }
-
-  String findElementInScope(String name) {
-    var lookupFunc = determineLookupFunc(name);
-    var result = lookupFunc(mirror, name);
-    if (result != null) {
-      result = getDocgenObject(result);
-      if (result is DummyMirror) return packagePrefix + result.docName;
-      return result.packagePrefix + result.docName;
-    }
-
-    if (owner != null) {
-      var result = owner.findElementInScope(name);
-      if (result != null) {
-        return result;
-      }
-    }
-    return super.findElementInScope(name);
-  }
-
-  bool isValidMirror(DeclarationMirror mirror) => mirror is VariableMirror;
-}
-
-/// A class containing properties of a Dart method.
-class Method extends OwnedIndexable {
-
-  /// Parameters for this method.
-  final Map<String, Parameter> parameters;
-
-  final bool isStatic;
-  final bool isAbstract;
-  final bool isConst;
-  final Type returnType;
-  Method methodInheritedFrom;
-
-  /// Qualified name to state where the comment is inherited from.
-  String commentInheritedFrom = "";
-
-  factory Method(MethodMirror mirror, Indexable owner,
-      [Method methodInheritedFrom]) {
-    var method = getDocgenObject(mirror, owner);
-    if (method is DummyMirror) {
-      method = new Method._(mirror, owner, methodInheritedFrom);
-    }
-    return method;
-  }
-
-  Method._(MethodMirror mirror, Indexable owner, this.methodInheritedFrom)
-      : returnType = new Type(mirror.returnType, owner.owningLibrary),
-        isStatic = mirror.isStatic,
-        isAbstract = mirror.isAbstract,
-        isConst = mirror.isConstConstructor,
-        parameters = createParameters(mirror.parameters, owner),
-        super(mirror, owner);
-
-  Method get originallyInheritedFrom => methodInheritedFrom == null ?
-      this : methodInheritedFrom.originallyInheritedFrom;
-
-  /// Look for the specified name starting with the current member, and
-  /// progressively working outward to the current library scope.
-  String findElementInScope(String name) {
-    var lookupFunc = determineLookupFunc(name);
-
-    var memberScope = lookupFunc(this.mirror, name);
-    if (memberScope != null) {
-      // do we check for a dummy mirror returned here and look up with an owner
-      // higher ooooor in getDocgenObject do we include more things in our
-      // lookup
-      var result = getDocgenObject(memberScope, owner);
-      if (result is DummyMirror && owner.owner != null
-          && owner.owner is! DummyMirror) {
-        var aresult = getDocgenObject(memberScope, owner.owner);
-        if (aresult is! DummyMirror) result = aresult;
-      }
-      if (result is DummyMirror) return packagePrefix + result.docName;
-      return result.packagePrefix + result.docName;
-    }
-
-    if (owner != null) {
-      var result = owner.findElementInScope(name);
-      if (result != null) return result;
-    }
-    return super.findElementInScope(name);
-  }
-
-  String get docName {
-    if ((mirror as MethodMirror).isConstructor) {
-      // We name constructors specially -- including the class name again and a
-      // "-" to separate the constructor from its name (if any).
-      return '${owner.docName}.${dart2js_util.nameOf(mirror.owner)}-'
-             '${dart2js_util.nameOf(mirror)}';
-    }
-    return super.docName;
-  }
-
-  String get fileName => packagePrefix + docName;
-
-  /// Makes sure that the method with an inherited equivalent have comments.
-  void ensureCommentFor(Method inheritedMethod) {
-    if (comment.isNotEmpty) return;
-
-    comment = inheritedMethod._commentToHtml(this);
-    _unresolvedComment = inheritedMethod._unresolvedComment;
-    commentInheritedFrom = inheritedMethod.commentInheritedFrom == '' ?
-        new DummyMirror(inheritedMethod.mirror).docName :
-        inheritedMethod.commentInheritedFrom;
-  }
-
-  /// Generates a map describing the [Method] object.
-  Map toMap() => {
-    'name': name,
-    'qualifiedName': qualifiedName,
-    'comment': comment,
-    'commentFrom': (methodInheritedFrom != null &&
-        commentInheritedFrom == methodInheritedFrom.docName ? ''
-        : commentInheritedFrom),
-    'inheritedFrom': (methodInheritedFrom == null? '' :
-        originallyInheritedFrom.docName),
-    'static': isStatic,
-    'abstract': isAbstract,
-    'constant': isConst,
-    'return': [returnType.toMap()],
-    'parameters': recurseMap(parameters),
-    'annotations': annotations.map((a) => a.toMap()).toList()
-  };
-
-  String get typeName {
-    MethodMirror theMirror = mirror;
-    if (theMirror.isConstructor) return 'constructor';
-    if (theMirror.isGetter) return 'getter';
-    if (theMirror.isSetter) return'setter';
-    if (theMirror.isOperator) return 'operator';
-    return 'method';
-  }
-
-  get comment {
-    if (_comment != null) return _comment;
-    if (owner is Class) {
-      (owner as Class).ensureComments();
-    }
-    var result = super.comment;
-    if (result == '' && methodInheritedFrom != null) {
-      // This should be NOT from the MIRROR, but from the COMMENT.
-      methodInheritedFrom.comment; // Ensure comment field has been populated.
-      _unresolvedComment = methodInheritedFrom._unresolvedComment;
-
-      var linkResolver = (name) => fixReference(name);
-      comment = _unresolvedComment == null ? '' :
-        markdown.markdownToHtml(_unresolvedComment.trim(),
-            linkResolver: linkResolver, inlineSyntaxes: MARKDOWN_SYNTAXES);
-      commentInheritedFrom = comment != '' ?
-          methodInheritedFrom.commentInheritedFrom : '';
-      result = comment;
-    }
-    return result;
-  }
-
-  bool isValidMirror(DeclarationMirror mirror) => mirror is MethodMirror;
-}
-
-/// Docgen wrapper around the dart2js mirror for a Dart
-/// method/function parameter.
-class Parameter extends MirrorBased {
-  final ParameterMirror mirror;
-  final String name;
-  final bool isOptional;
-  final bool isNamed;
-  final bool hasDefaultValue;
-  final Type type;
-  final String defaultValue;
-  /// List of the meta annotations on the parameter.
-  final List<Annotation> annotations;
-
-  Parameter(ParameterMirror mirror, Library owningLibrary)
-      : this.mirror = mirror,
-        name = dart2js_util.nameOf(mirror),
-        isOptional = mirror.isOptional,
-        isNamed = mirror.isNamed,
-        hasDefaultValue = mirror.hasDefaultValue,
-        defaultValue = '${mirror.defaultValue}',
-        type = new Type(mirror.type, owningLibrary),
-        annotations = createAnnotations(mirror, owningLibrary);
-
-  /// Generates a map describing the [Parameter] object.
-  Map toMap() => {
-    'name': name,
-    'optional': isOptional,
-    'named': isNamed,
-    'default': hasDefaultValue,
-    'type': new List.filled(1, type.toMap()),
-    'value': defaultValue,
-    'annotations': annotations.map((a) => a.toMap()).toList()
-  };
-}
-
-/// Docgen wrapper around the mirror for a return type, and/or its generic
-/// type parameters.
-///
-/// Return types are of a form [outer]<[inner]>.
-/// If there is no [inner] part, [inner] will be an empty list.
-///
-/// For example:
-///        int size()
-///          "return" :
-///            - "outer" : "dart-core.int"
-///              "inner" :
-///
-///        List<String> toList()
-///          "return" :
-///            - "outer" : "dart-core.List"
-///              "inner" :
-///                - "outer" : "dart-core.String"
-///                  "inner" :
-///
-///        Map<String, List<int>>
-///          "return" :
-///            - "outer" : "dart-core.Map"
-///              "inner" :
-///                - "outer" : "dart-core.String"
-///                  "inner" :
-///                - "outer" : "dart-core.List"
-///                  "inner" :
-///                    - "outer" : "dart-core.int"
-///                      "inner" :
-class Type extends MirrorBased {
-  final TypeMirror mirror;
-  final Library owningLibrary;
-
-  Type(this.mirror, this.owningLibrary);
-
-  Map toMap() {
-    var result = getDocgenObject(mirror, owningLibrary);
-    return {
-      // We may encounter types whose corresponding library has not been
-      // processed yet, so look up with the owningLibrary at the last moment.
-      'outer': result.packagePrefix + result.docName,
-      'inner': _createTypeGenerics(mirror).map((e) => e.toMap()).toList(),
-    };
-  }
-
-  /// Returns a list of [Type] objects constructed from TypeMirrors.
-  List<Type> _createTypeGenerics(TypeMirror mirror) {
-    if (mirror is! ClassMirror) return [];
-    return mirror.typeArguments.map((e) => new Type(e, owningLibrary)).toList();
-  }
-}
-
-/// Holds the name of the annotation, and its parameters.
-class Annotation extends MirrorBased {
-  /// The class of this annotation.
-  final ClassMirror mirror;
-  final Library owningLibrary;
-  List<String> parameters;
-
-  Annotation(InstanceMirror originalMirror, this.owningLibrary)
-      : mirror = originalMirror.type {
-    parameters = dart2js_util.variablesOf(originalMirror.type.declarations)
-        .where((e) => e.isFinal)
-        .map((e) => originalMirror.getField(e.simpleName).reflectee)
-        .where((e) => e != null)
-        .toList();
-  }
-
-  Map toMap() => {
-    'name': getDocgenObject(mirror, owningLibrary).docName,
-    'parameters': parameters
-  };
-}
+export 'models/class.dart';
+export 'models/indexable.dart';
+export 'models/library.dart';
+export 'models/method.dart';
+export 'models/parameter.dart';
+export 'models/typedef.dart';
+export 'models/variable.dart';
diff --git a/pkg/docgen/lib/src/models/annotation.dart b/pkg/docgen/lib/src/models/annotation.dart
new file mode 100644
index 0000000..86cd274
--- /dev/null
+++ b/pkg/docgen/lib/src/models/annotation.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.annotation;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'library.dart';
+import 'mirror_based.dart';
+
+/// Holds the name of the annotation, and its parameters.
+class Annotation extends MirrorBased<ClassMirror> {
+  /// The class of this annotation.
+  final ClassMirror mirror;
+  final Library owningLibrary;
+  final List<String> parameters;
+
+  Annotation(InstanceMirror originalMirror, this.owningLibrary)
+      : mirror = originalMirror.type,
+        parameters = _createParamaters(originalMirror);
+
+  Map toMap() => {
+    'name': getDocgenObject(mirror, owningLibrary).docName,
+    'parameters': parameters
+  };
+}
+
+List<String> _createParamaters(InstanceMirror originalMirror) {
+  var curMirror = originalMirror.type;
+  Map<Symbol, DeclarationMirror> allDeclarations =
+      new Map.from(curMirror.declarations);
+  // This method assumes that our users aren't creating deep inheritance
+  // chains of custom annotation inheritance. If this is not the case,
+  // re-write this section for performance.
+  while (curMirror.superclass !=  null &&
+      curMirror.superclass.simpleName.toString() != 'Object') {
+    allDeclarations.addAll(curMirror.superclass.declarations);
+    curMirror = curMirror.superclass;
+  }
+
+  // TODO(efortuna): Some originalMirrors, such as the
+  // Dart2JsMapConstantMirror and Dart2JsListConstantMirror don't have a
+  // reflectee field, but we want the value of the parameter from them.
+  // Gross workaround is to assemble the object manually.
+  // See issue 18346.
+  return dart2js_util.variablesOf(allDeclarations)
+      .where((e) => e.isFinal &&
+      originalMirror.getField(e.simpleName).hasReflectee)
+        .map((e) => originalMirror.getField(e.simpleName).reflectee)
+        .where((e) => e != null)
+        .toList();
+}
diff --git a/pkg/docgen/lib/src/models/class.dart b/pkg/docgen/lib/src/models/class.dart
new file mode 100644
index 0000000..555b1dd
--- /dev/null
+++ b/pkg/docgen/lib/src/models/class.dart
@@ -0,0 +1,245 @@
+// 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 docgen.models.clazz;
+
+import '../exports/dart2js_mirrors.dart' as dart2js_mirrors;
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'dummy_mirror.dart';
+import 'generic.dart';
+import 'library.dart';
+import 'method.dart';
+import 'model_helpers.dart';
+import 'owned_indexable.dart';
+import 'variable.dart';
+
+/// A class containing contents of a Dart class.
+class Class extends OwnedIndexable<dart2js_mirrors.Dart2JsInterfaceTypeMirror>
+    implements Comparable<Class> {
+
+  /// List of the names of interfaces that this class implements.
+  List<Class> interfaces = [];
+
+  /// Names of classes that extends or implements this class.
+  Set<Class> subclasses = new Set<Class>();
+
+  /// Top-level variables in the class.
+  Map<String, Variable> variables;
+
+  /// Inherited variables in the class.
+  final Map<String, Variable> inheritedVariables = {};
+
+  /// Methods in the class.
+  Map<String, Method> methods;
+
+  final Map<String, Method> inheritedMethods = new Map<String, Method>();
+
+  /// Generic infomation about the class.
+  final Map<String, Generic> generics;
+
+  Class _superclass;
+  bool get isAbstract => mirror.isAbstract;
+
+  /// Make sure that we don't check for inherited comments more than once.
+  bool _commentsEnsured = false;
+
+  /// Returns the [Class] for the given [mirror] if it has already been created,
+  /// else creates it.
+  factory Class(ClassMirror mirror, Library owner) {
+    var clazz = getDocgenObject(mirror, owner);
+    if (clazz is DummyMirror) {
+      clazz = new Class._(mirror, owner);
+    }
+    return clazz;
+  }
+
+  /// Called when we are constructing a superclass or interface class, but it
+  /// is not known if it belongs to the same owner as the original class. In
+  /// this case, we create an object whose owner is what the original mirror
+  /// says it is.
+  factory Class._possiblyDifferentOwner(ClassMirror mirror,
+      Library originalOwner) {
+    var realOwner = getDocgenObject(mirror.owner);
+    if (realOwner is Library) {
+      return new Class(mirror, realOwner);
+    } else {
+      return new Class(mirror, originalOwner);
+    }
+  }
+
+  Class._(ClassSourceMirror classMirror, Library owner)
+      : generics = createGenerics(classMirror),
+        super(classMirror, owner) {
+
+    // The reason we do this madness is the superclass and interface owners may
+    // not be this class's owner!! Example: BaseClient in http pkg.
+    var superinterfaces = classMirror.superinterfaces.map(
+        (interface) => new Class._possiblyDifferentOwner(interface, owner));
+    this._superclass = classMirror.superclass == null? null :
+        new Class._possiblyDifferentOwner(classMirror.superclass, owner);
+
+    interfaces = superinterfaces.toList();
+    variables = createVariables(
+        dart2js_util.variablesOf(classMirror.declarations), this);
+    methods = createMethods(classMirror.declarations.values.where(
+        (mirror) => mirror is MethodMirror), this);
+
+    // Tell superclass that you are a subclass, unless you are not
+    // visible or an intermediary mixin class.
+    if (!classMirror.isNameSynthetic && isVisible && _superclass != null) {
+      _superclass.addSubclass(this);
+    }
+
+    if (this._superclass != null) addInherited(_superclass);
+    interfaces.forEach((interface) => addInherited(interface));
+  }
+
+  String _lookupInClassAndSuperclasses(String name) {
+    var lookupFunc = determineLookupFunc(name);
+    var classScope = this;
+    while (classScope != null) {
+      var classFunc = lookupFunc(classScope.mirror, name);
+      if (classFunc != null) {
+        return packagePrefix + getDocgenObject(classFunc, owner).docName;
+      }
+      classScope = classScope._superclass;
+    }
+    return null;
+  }
+
+  /// Look for the specified name starting with the current member, and
+  /// progressively working outward to the current library scope.
+  String findElementInScope(String name) {
+    var lookupFunc = determineLookupFunc(name);
+    var result = _lookupInClassAndSuperclasses(name);
+    if (result != null) {
+      return result;
+    }
+    result = owner.findElementInScope(name);
+    return result == null ? super.findElementInScope(name) : result;
+  }
+
+  String get typeName => 'class';
+
+  /// Add all inherited variables and methods from the provided superclass.
+  /// If [_includePrivate] is true, it also adds the variables and methods from
+  /// the superclass.
+  void addInherited(Class superclass) {
+    inheritedVariables.addAll(superclass.inheritedVariables);
+    inheritedVariables.addAll(_allButStatics(superclass.variables));
+    addInheritedMethod(superclass, this);
+  }
+
+  /** [newParent] refers to the actual class is currently using these methods.
+   * which may be different because with the mirror system, we only point to the
+   * original canonical superclasse's method.
+   */
+  void addInheritedMethod(Class parent, Class newParent) {
+    parent.inheritedMethods.forEach((name, method) {
+      if (!method.mirror.isConstructor) {
+        inheritedMethods[name] = new Method(method.mirror, newParent, method);
+      }
+    });
+    _allButStatics(parent.methods).forEach((name, method) {
+      if (!method.mirror.isConstructor) {
+        inheritedMethods[name] = new Method(method.mirror, newParent, method);
+      }
+    });
+  }
+
+  /// Remove statics from the map of inherited items before adding them.
+  Map _allButStatics(Map items) {
+    var result = {};
+    items.forEach((name, item) {
+      if (!item.isStatic) {
+        result[name] = item;
+      }
+    });
+    return result;
+  }
+
+  /// Add the subclass to the class.
+  ///
+  /// If [this] is private (or an intermediary mixin class), it will add the
+  /// subclass to the list of subclasses in the superclasses.
+  void addSubclass(Class subclass) {
+    if (docName == 'dart-core.Object') return;
+
+    if (!includePrivateMembers && isPrivate || mirror.isNameSynthetic) {
+      if (_superclass != null) _superclass.addSubclass(subclass);
+      interfaces.forEach((interface) {
+        interface.addSubclass(subclass);
+      });
+    } else {
+      subclasses.add(subclass);
+    }
+  }
+
+  /// Check if this [Class] is an error or exception.
+  bool isError() {
+    if (qualifiedName == 'dart-core.Error' ||
+        qualifiedName == 'dart-core.Exception')
+      return true;
+    for (var interface in interfaces) {
+      if (interface.isError()) return true;
+    }
+    if (_superclass == null) return false;
+    return _superclass.isError();
+  }
+
+  /// Makes sure that all methods with inherited equivalents have comments.
+  void ensureComments() {
+    if (_commentsEnsured) return;
+    _commentsEnsured = true;
+    if (_superclass != null) _superclass.ensureComments();
+    inheritedMethods.forEach((qualifiedName, inheritedMethod) {
+      var method = methods[qualifiedName];
+      if (method != null) {
+        // if we have overwritten this method in this class, we still provide
+        // the opportunity to inherit the comments.
+        method.ensureCommentFor(inheritedMethod);
+      }
+    });
+    // we need to populate the comments for all methods. so that the subclasses
+    // can get for their inherited versions the comments.
+    methods.forEach((qualifiedName, method) {
+      if (!method.mirror.isConstructor) method.ensureCommentFor(method);
+    });
+  }
+
+  /// If a class extends a private superclass, find the closest public
+  /// superclass of the private superclass.
+  String validSuperclass() {
+    if (_superclass == null) return 'dart-core.Object';
+    if (_superclass.isVisible) return _superclass.qualifiedName;
+    return _superclass.validSuperclass();
+  }
+
+  /// Generates a map describing the [Class] object.
+  Map toMap() => {
+    'name': name,
+    'qualifiedName': qualifiedName,
+    'comment': comment,
+    'isAbstract' : isAbstract,
+    'superclass': validSuperclass(),
+    'implements': interfaces.where((i) => i.isVisible)
+        .map((e) => e.qualifiedName).toList(),
+    'subclass': (subclasses.toList()..sort())
+        .map((x) => x.qualifiedName).toList(),
+    'variables': recurseMap(variables),
+    'inheritedVariables': recurseMap(inheritedVariables),
+    'methods': expandMethodMap(methods),
+    'inheritedMethods': expandMethodMap(inheritedMethods),
+    'annotations': annotations.map((a) => a.toMap()).toList(),
+    'generics': recurseMap(generics)
+  };
+
+  int compareTo(Class other) => name.compareTo(other.name);
+
+  bool isValidMirror(DeclarationMirror mirror) => mirror is ClassMirror;
+}
diff --git a/pkg/docgen/lib/src/models/doc_gen_type.dart b/pkg/docgen/lib/src/models/doc_gen_type.dart
new file mode 100644
index 0000000..15fd51d
--- /dev/null
+++ b/pkg/docgen/lib/src/models/doc_gen_type.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.doc_gen_type;
+
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'library.dart';
+import 'mirror_based.dart';
+
+/// Docgen wrapper around the mirror for a return type, and/or its generic
+/// type parameters.
+///
+/// Return types are of a form [outer]<[inner]>.
+/// If there is no [inner] part, [inner] will be an empty list.
+///
+/// For example:
+///        int size()
+///          "return" :
+///            - "outer" : "dart-core.int"
+///              "inner" :
+///
+///        List<String> toList()
+///          "return" :
+///            - "outer" : "dart-core.List"
+///              "inner" :
+///                - "outer" : "dart-core.String"
+///                  "inner" :
+///
+///        Map<String, List<int>>
+///          "return" :
+///            - "outer" : "dart-core.Map"
+///              "inner" :
+///                - "outer" : "dart-core.String"
+///                  "inner" :
+///                - "outer" : "dart-core.List"
+///                  "inner" :
+///                    - "outer" : "dart-core.int"
+///                      "inner" :
+class DocGenType extends MirrorBased {
+  final TypeMirror mirror;
+  final Library owningLibrary;
+
+  DocGenType(this.mirror, this.owningLibrary);
+
+  Map toMap() {
+    var result = getDocgenObject(mirror, owningLibrary);
+    return {
+      // We may encounter types whose corresponding library has not been
+      // processed yet, so look up with the owningLibrary at the last moment.
+      'outer': result.packagePrefix + result.docName,
+      'inner': _createTypeGenerics(mirror).map((e) => e.toMap()).toList(),
+    };
+  }
+
+  /// Returns a list of [DocGenType] objects constructed from TypeMirrors.
+  List<DocGenType> _createTypeGenerics(TypeMirror mirror) {
+    if (mirror is! ClassMirror) return [];
+    return mirror.typeArguments
+        .map((e) => new DocGenType(e, owningLibrary))
+        .toList();
+  }
+}
diff --git a/pkg/docgen/lib/src/models/dummy_mirror.dart b/pkg/docgen/lib/src/models/dummy_mirror.dart
new file mode 100644
index 0000000..9ad612a
--- /dev/null
+++ b/pkg/docgen/lib/src/models/dummy_mirror.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.dummy_mirror;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'indexable.dart';
+import 'model_helpers.dart';
+
+/// For types that we do not explicitly create or have not yet created in our
+/// entity map (like core types).
+class DummyMirror implements Indexable {
+  final DeclarationMirror mirror;
+  /// The library that contains this element, if any. Used as a hint to help
+  /// determine which object we're referring to when looking up this mirror in
+  /// our map.
+  final Indexable owner;
+
+  DummyMirror(this.mirror, [this.owner]);
+
+  String get docName {
+    if (mirror is LibraryMirror) {
+      return getLibraryDocName(mirror);
+    }
+    var mirrorOwner = mirror.owner;
+    if (mirrorOwner == null) return dart2js_util.qualifiedNameOf(mirror);
+    var simpleName = dart2js_util.nameOf(mirror);
+    if (mirror is MethodMirror && (mirror as MethodMirror).isConstructor) {
+      // We name constructors specially -- repeating the class name and a
+      // "-" to separate the constructor from its name (if any).
+      simpleName = '${dart2js_util.nameOf(mirrorOwner)}-$simpleName';
+    }
+    return getDocgenObject(mirrorOwner, owner).docName + '.' +
+        simpleName;
+  }
+
+  bool get isPrivate => mirror.isPrivate;
+
+  String get packageName {
+    var libMirror = _getOwningLibraryFromMirror(mirror);
+    if (libMirror != null) {
+      return getPackageName(libMirror);
+    }
+    return '';
+  }
+
+  String get packagePrefix => packageName == null || packageName.isEmpty ?
+      '' : '$packageName/';
+
+  // This is a known incomplete implementation of Indexable
+  // overriding noSuchMethod to remove static warnings
+  noSuchMethod(Invocation invocation) {
+    throw new UnimplementedError(invocation.memberName.toString());
+  }
+}
+
+LibraryMirror _getOwningLibraryFromMirror(DeclarationMirror mirror) {
+  if (mirror == null) return null;
+  if (mirror is LibraryMirror) return mirror;
+  return _getOwningLibraryFromMirror(mirror.owner);
+}
diff --git a/pkg/docgen/lib/src/models/generic.dart b/pkg/docgen/lib/src/models/generic.dart
new file mode 100644
index 0000000..46a48a1
--- /dev/null
+++ b/pkg/docgen/lib/src/models/generic.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.generic;
+
+import '../exports/source_mirrors.dart';
+import '../exports/mirrors_util.dart' as dart2js_util;
+
+import 'mirror_based.dart';
+
+/// A Docgen wrapper around the dart2js mirror for a generic type.
+class Generic extends MirrorBased<TypeVariableMirror> {
+  final TypeVariableMirror mirror;
+
+  Generic(this.mirror);
+
+  Map toMap() => {
+    'name': dart2js_util.nameOf(mirror),
+    'type': dart2js_util.qualifiedNameOf(mirror.upperBound)
+  };
+}
diff --git a/pkg/docgen/lib/src/models/indexable.dart b/pkg/docgen/lib/src/models/indexable.dart
new file mode 100644
index 0000000..53441d0
--- /dev/null
+++ b/pkg/docgen/lib/src/models/indexable.dart
@@ -0,0 +1,216 @@
+// 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 docgen.models.indexable;
+
+import 'dart:collection';
+
+import 'package:markdown/markdown.dart' as markdown;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+import 'library.dart';
+import 'mirror_based.dart';
+import 'model_helpers.dart';
+
+/// An item that is categorized in our mirrorToDocgen map, as a distinct,
+/// searchable element.
+///
+/// These are items that refer to concrete entities (a Class, for example,
+/// but not a Type, which is a "pointer" to a class) that we wish to be
+/// globally resolvable. This includes things such as class methods and
+/// variables, but parameters for methods are not "Indexable" as we do not want
+/// the user to be able to search for a method based on its parameter names!
+/// The set of indexable items also includes Typedefs, since the user can refer
+/// to them as concrete entities in a particular scope.
+abstract class Indexable<TMirror extends DeclarationMirror>
+    extends MirrorBased<TMirror> {
+
+  Library get owningLibrary => owner.owningLibrary;
+
+  /// The reference to this element based on where it is printed as a
+  /// documentation file and also the unique URL to refer to this item.
+  ///
+  /// The qualified name (for URL purposes) and the file name are the same,
+  /// of the form packageName/ClassName or packageName/ClassName.methodName.
+  /// This defines both the URL and the directory structure.
+  String get qualifiedName => packagePrefix + ownerPrefix + name;
+
+  final TMirror mirror;
+  final bool isPrivate;
+  /// The comment text pre-resolution. We keep this around because inherited
+  /// methods need to resolve links differently from the superclass.
+  String unresolvedComment = '';
+
+  Indexable(TMirror mirror)
+      : this.mirror = mirror,
+        this.isPrivate = isHidden(mirror) {
+
+    var mirrorQualifiedName = dart2js_util.qualifiedNameOf(this.mirror);
+
+    var map = _mirrorToDocgen.putIfAbsent(mirrorQualifiedName,
+        () => new HashMap<String, Indexable>());
+
+    var added = false;
+    map.putIfAbsent(owner.docName, () {
+      added = true;
+      return this;
+    });
+
+    if (!added) {
+      throw new StateError('An indexable has already been stored for '
+          '${owner.docName}');
+    }
+  }
+
+  /// Returns this object's qualified name, but following the conventions
+  /// we're using in Dartdoc, which is that library names with dots in them
+  /// have them replaced with hyphens.
+  String get docName;
+
+  /// Converts all [foo] references in comments to <a>libraryName.foo</a>.
+  markdown.Node fixReference(String name) {
+    // Attempt the look up the whole name up in the scope.
+    String elementName = findElementInScope(name);
+    if (elementName != null) {
+      return new markdown.Element.text('a', elementName);
+    }
+    return fixComplexReference(name);
+  }
+
+  /// Look for the specified name starting with the current member, and
+  /// progressively working outward to the current library scope.
+  String findElementInScope(String name) =>
+      findElementInScopeWithPrefix(name, packagePrefix);
+
+  /// The full docName of the owner element, appended with a '.' for this
+  /// object's name to be appended.
+  String get ownerPrefix => owner.docName != '' ? owner.docName + '.' : '';
+
+  /// The prefix String to refer to the package that this item is in, for URLs
+  /// and comment resolution.
+  ///
+  /// The prefix can be prepended to a qualified name to get a fully unique
+  /// name among all packages.
+  String get packagePrefix;
+
+  /// Documentation comment with converted markdown and all links resolved.
+  String commentField;
+
+  /// Accessor to documentation comment with markdown converted to html and all
+  /// links resolved.
+  String get comment {
+    if (commentField != null) return commentField;
+
+    commentField = commentToHtml();
+    if (commentField.isEmpty) {
+      commentField = getMdnComment();
+    }
+    return commentField;
+  }
+
+  void set comment(x) {
+    commentField = x;
+  }
+
+  /// The simple name to refer to this item.
+  String get name => dart2js_util.nameOf(mirror);
+
+  /// Accessor to the parent item that owns this item.
+  ///
+  /// "Owning" is defined as the object one scope-level above which this item
+  /// is defined. Ex: The owner for a top level class, would be its enclosing
+  /// library. The owner of a local variable in a method would be the enclosing
+  /// method.
+  Indexable get owner;
+
+  /// Generates MDN comments from database.json.
+  String getMdnComment();
+
+  /// The type of this member to be used in index.txt.
+  String get typeName;
+
+  /// Creates a [Map] with this [Indexable]'s name and a preview comment.
+  Map get previewMap {
+    var finalMap = { 'name' : name, 'qualifiedName' : qualifiedName };
+    var pre = preview;
+    if (pre != null) finalMap['preview'] = pre;
+    return finalMap;
+  }
+
+  String get preview {
+    if (comment != '') {
+      var index = comment.indexOf('</p>');
+      return index > 0 ?
+          '${comment.substring(0, index)}</p>' :
+          '<p><i>Comment preview not available</i></p>';
+    }
+    return null;
+  }
+
+  /// Accessor to obtain the raw comment text for a given item, _without_ any
+  /// of the links resolved.
+  String get _commentText {
+    String commentText;
+    mirror.metadata.forEach((metadata) {
+      if (metadata is CommentInstanceMirror) {
+        CommentInstanceMirror comment = metadata;
+        if (comment.isDocComment) {
+          if (commentText == null) {
+            commentText = comment.trimmedText;
+          } else {
+            commentText = '$commentText\n${comment.trimmedText}';
+          }
+        }
+      }
+    });
+    return commentText;
+  }
+
+  /// Returns any documentation comments associated with a mirror with
+  /// simple markdown converted to html.
+  ///
+  /// By default we resolve any comment references within our own scope.
+  /// However, if a method is inherited, we want the inherited comments, but
+  /// links to the subclasses's version of the methods.
+  String commentToHtml([Indexable resolvingScope]) {
+    if (resolvingScope == null) resolvingScope = this;
+    var commentText = _commentText;
+    unresolvedComment = commentText;
+
+    commentText = commentText == null ? '' :
+        markdown.markdownToHtml(commentText.trim(),
+            linkResolver: resolvingScope.fixReference,
+            inlineSyntaxes: MARKDOWN_SYNTAXES);
+    return commentText;
+  }
+
+  /// Return a map representation of this type.
+  Map toMap();
+
+  /// Accessor to determine if this item and all of its owners are visible.
+  bool get isVisible => isFullChainVisible(this);
+
+  /// Returns true if [mirror] is the correct type of mirror that this Docgen
+  /// object wraps. (Workaround for the fact that Types are not first class.)
+  bool isValidMirror(DeclarationMirror mirror);
+}
+
+/// Index of all the dart2js mirrors examined to corresponding MirrorBased
+/// docgen objects.
+///
+/// Used for lookup because of the dart2js mirrors exports
+/// issue. The second level map is indexed by owner docName for faster lookup.
+/// Why two levels of lookup? Speed, man. Speed.
+final Map<String, Map<String, Indexable>> _mirrorToDocgen =
+    new HashMap<String, Map<String, Indexable>>();
+
+Iterable<Indexable> get allIndexables =>
+    _mirrorToDocgen.values.expand((map) => map.values);
+
+Map<String, Indexable> lookupIndexableMap(DeclarationMirror mirror) {
+  return _mirrorToDocgen[dart2js_util.qualifiedNameOf(mirror)];
+}
diff --git a/pkg/docgen/lib/src/models/library.dart b/pkg/docgen/lib/src/models/library.dart
new file mode 100644
index 0000000..826536f
--- /dev/null
+++ b/pkg/docgen/lib/src/models/library.dart
@@ -0,0 +1,190 @@
+// 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 docgen.models.library;
+
+import 'dart:io';
+
+import 'package:markdown/markdown.dart' as markdown;
+
+import '../exports/source_mirrors.dart';
+import '../exports/mirrors_util.dart' as dart2js_util;
+
+import '../library_helpers.dart';
+import '../package_helpers.dart';
+
+import 'class.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'method.dart';
+import 'model_helpers.dart';
+import 'typedef.dart';
+import 'variable.dart';
+
+/// A class containing contents of a Dart library.
+class Library extends Indexable {
+  final Map<String, Class> classes = {};
+  final Map<String, Typedef> typedefs = {};
+  final Map<String, Class> errors = {};
+
+  /// Top-level variables in the library.
+  Map<String, Variable> variables;
+
+  /// Top-level functions in the library.
+  Map<String, Method> functions;
+
+  String packageName = '';
+  bool _hasBeenCheckedForPackage = false;
+  String packageIntro;
+
+  Indexable get owner => const _LibraryOwner();
+
+  Library get owningLibrary => this;
+
+  /// Returns the [Library] for the given [mirror] if it has already been
+  /// created, else creates it.
+  factory Library(LibraryMirror mirror) {
+    var library = getDocgenObject(mirror);
+    if (library is DummyMirror) {
+      library = new Library._(mirror);
+    }
+    return library;
+  }
+
+  Library._(LibraryMirror libraryMirror) : super(libraryMirror) {
+    var exported = calcExportedItems(libraryMirror);
+    var exportedClasses = addAll(exported['classes'],
+        dart2js_util.typesOf(libraryMirror.declarations));
+    updateLibraryPackage(mirror);
+    exportedClasses.forEach((String mirrorName, TypeMirror mirror) {
+      if (mirror is TypedefMirror) {
+        // This is actually a Dart2jsTypedefMirror, and it does define value,
+        // but we don't have visibility to that type.
+        if (includePrivateMembers || !mirror.isPrivate) {
+          typedefs[dart2js_util.nameOf(mirror)] = new Typedef(mirror, this);
+        }
+      } else if (mirror is ClassMirror) {
+        var clazz = new Class(mirror, this);
+
+        if (clazz.isError()) {
+          errors[dart2js_util.nameOf(mirror)] = clazz;
+        } else {
+          classes[dart2js_util.nameOf(mirror)] = clazz;
+        }
+      } else {
+        throw new ArgumentError(
+            '${dart2js_util.nameOf(mirror)} - no class type match. ');
+      }
+    });
+    this.functions = createMethods(addAll(exported['methods'],
+        libraryMirror.declarations.values.where(
+            (mirror) => mirror is MethodMirror)).values, this);
+    this.variables = createVariables(addAll(exported['variables'],
+        dart2js_util.variablesOf(libraryMirror.declarations)).values, this);
+  }
+
+  /// Look for the specified name starting with the current member, and
+  /// progressively working outward to the current library scope.
+  String findElementInScope(String name) {
+    var lookupFunc = determineLookupFunc(name);
+    var libraryScope = lookupFunc(mirror, name);
+    if (libraryScope != null) {
+      var result = getDocgenObject(libraryScope, this);
+      if (result is DummyMirror) return packagePrefix + result.docName;
+      return result.packagePrefix + result.docName;
+    }
+    return super.findElementInScope(name);
+  }
+
+  String getMdnComment() => '';
+
+  /// For a library's [mirror], determine the name of the package (if any) we
+  /// believe it came from (because of its file URI).
+  ///
+  /// If no package could be determined, we return an empty string.
+  void updateLibraryPackage(LibraryMirror mirror) {
+    if (mirror == null) return;
+    if (_hasBeenCheckedForPackage) return;
+    _hasBeenCheckedForPackage = true;
+    if (mirror.uri.scheme != 'file') return;
+    packageName = getPackageName(mirror);
+    // Associate the package readme with all the libraries. This is a bit
+    // wasteful, but easier than trying to figure out which partial match
+    // is best.
+    packageIntro = _packageIntro(getPackageDirectory(mirror));
+  }
+
+  String _packageIntro(packageDir) {
+    if (packageDir == null) return null;
+    var dir = new Directory(packageDir);
+    var files = dir.listSync();
+    var readmes = files.where((FileSystemEntity each) => (each is File &&
+        each.path.substring(packageDir.length + 1, each.path.length)
+          .startsWith('README'))).toList();
+    if (readmes.isEmpty) return '';
+    // If there are multiples, pick the shortest name.
+    readmes.sort((a, b) => a.path.length.compareTo(b.path.length));
+    var readme = readmes.first;
+    var linkResolver = (name) => globalFixReference(name);
+    var contents = markdown.markdownToHtml(readme
+      .readAsStringSync(), linkResolver: linkResolver,
+      inlineSyntaxes: MARKDOWN_SYNTAXES);
+    return contents;
+  }
+
+  String get packagePrefix => packageName == null || packageName.isEmpty ?
+      '' : '$packageName/';
+
+  Map get previewMap {
+    var map = {'packageName': packageName};
+    map.addAll(super.previewMap);
+    if (packageIntro != null) {
+      map['packageIntro'] = packageIntro;
+    }
+    return map;
+  }
+
+  String get name => docName;
+
+  String get docName => getLibraryDocName(mirror);
+
+  /// Generates a map describing the [Library] object.
+  Map toMap() => {
+    'name': name,
+    'qualifiedName': qualifiedName,
+    'comment': comment,
+    'variables': recurseMap(variables),
+    'functions': expandMethodMap(functions),
+    'classes': {
+      'class': classes.values.where((c) => c.isVisible)
+        .map((e) => e.previewMap).toList(),
+      'typedef': recurseMap(typedefs),
+      'error': errors.values.where((e) => e.isVisible)
+          .map((e) => e.previewMap).toList()
+    },
+    'packageName': packageName,
+    'packageIntro': packageIntro
+  };
+
+  String get typeName => 'library';
+
+  bool isValidMirror(DeclarationMirror mirror) => mirror is LibraryMirror;
+}
+
+/// Dummy implementation of Indexable to represent the owner of a Library.
+class _LibraryOwner implements Indexable {
+  const _LibraryOwner();
+
+  String get docName => '';
+
+  bool get isPrivate => false;
+
+  Indexable get owner => null;
+
+  // This is a known incomplete implementation of Indexable
+  // overriding noSuchMethod to remove static warnings
+  noSuchMethod(Invocation invocation) {
+    throw new UnimplementedError(invocation.memberName.toString());
+  }
+}
diff --git a/pkg/docgen/lib/src/models/method.dart b/pkg/docgen/lib/src/models/method.dart
new file mode 100644
index 0000000..9483a37
--- /dev/null
+++ b/pkg/docgen/lib/src/models/method.dart
@@ -0,0 +1,156 @@
+// 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 docgen.models.method;
+
+import 'package:markdown/markdown.dart' as markdown;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'class.dart';
+import 'doc_gen_type.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'model_helpers.dart';
+import 'owned_indexable.dart';
+import 'parameter.dart';
+
+
+/// A class containing properties of a Dart method.
+class Method extends OwnedIndexable<MethodMirror> {
+
+  /// Parameters for this method.
+  final Map<String, Parameter> parameters;
+
+  final bool isStatic;
+  final bool isAbstract;
+  final bool isConst;
+  final DocGenType returnType;
+  Method methodInheritedFrom;
+
+  /// Qualified name to state where the comment is inherited from.
+  String commentInheritedFrom = "";
+
+  factory Method(MethodMirror mirror, Indexable owner,
+      [Method methodInheritedFrom]) {
+    var method = getDocgenObject(mirror, owner);
+    if (method is DummyMirror) {
+      method = new Method._(mirror, owner, methodInheritedFrom);
+    }
+    return method;
+  }
+
+  Method._(MethodMirror mirror, Indexable owner, this.methodInheritedFrom)
+      : returnType = new DocGenType(mirror.returnType, owner.owningLibrary),
+        isStatic = mirror.isStatic,
+        isAbstract = mirror.isAbstract,
+        isConst = mirror.isConstConstructor,
+        parameters = createParameters(mirror.parameters, owner),
+        super(mirror, owner);
+
+  Method get originallyInheritedFrom => methodInheritedFrom == null ?
+      this : methodInheritedFrom.originallyInheritedFrom;
+
+  /// Look for the specified name starting with the current member, and
+  /// progressively working outward to the current library scope.
+  String findElementInScope(String name) {
+    var lookupFunc = determineLookupFunc(name);
+
+    var memberScope = lookupFunc(this.mirror, name);
+    if (memberScope != null) {
+      // do we check for a dummy mirror returned here and look up with an owner
+      // higher ooooor in getDocgenObject do we include more things in our
+      // lookup
+      var result = getDocgenObject(memberScope, owner);
+      if (result is DummyMirror && owner.owner != null
+          && owner.owner is! DummyMirror) {
+        var aresult = getDocgenObject(memberScope, owner.owner);
+        if (aresult is! DummyMirror) result = aresult;
+      }
+      if (result is DummyMirror) return packagePrefix + result.docName;
+      return result.packagePrefix + result.docName;
+    }
+
+    if (owner != null) {
+      var result = owner.findElementInScope(name);
+      if (result != null) return result;
+    }
+    return super.findElementInScope(name);
+  }
+
+  String get docName {
+    if (mirror.isConstructor) {
+      // We name constructors specially -- including the class name again and a
+      // "-" to separate the constructor from its name (if any).
+      return '${owner.docName}.${dart2js_util.nameOf(mirror.owner)}-'
+          '${dart2js_util.nameOf(mirror)}';
+    }
+    return super.docName;
+  }
+
+  String get qualifiedName => packagePrefix + docName;
+
+  /// Makes sure that the method with an inherited equivalent have comments.
+  void ensureCommentFor(Method inheritedMethod) {
+    if (comment.isNotEmpty) return;
+
+    comment = inheritedMethod.commentToHtml(this);
+    unresolvedComment = inheritedMethod.unresolvedComment;
+    commentInheritedFrom = inheritedMethod.commentInheritedFrom == '' ?
+        new DummyMirror(inheritedMethod.mirror).docName :
+        inheritedMethod.commentInheritedFrom;
+  }
+
+  /// Generates a map describing the [Method] object.
+  Map toMap() => {
+    'name': name,
+    'qualifiedName': qualifiedName,
+    'comment': comment,
+    'commentFrom': (methodInheritedFrom != null &&
+        commentInheritedFrom == methodInheritedFrom.docName ? ''
+        : commentInheritedFrom),
+    'inheritedFrom': (methodInheritedFrom == null? '' :
+        originallyInheritedFrom.docName),
+    'static': isStatic,
+    'abstract': isAbstract,
+    'constant': isConst,
+    'return': [returnType.toMap()],
+    'parameters': recurseMap(parameters),
+    'annotations': annotations.map((a) => a.toMap()).toList()
+  };
+
+  String get typeName {
+    if (mirror.isConstructor) return 'constructor';
+    if (mirror.isGetter) return 'getter';
+    if (mirror.isSetter) return 'setter';
+    if (mirror.isOperator) return 'operator';
+    return 'method';
+  }
+
+  String get comment {
+    if (commentField != null) return commentField;
+    if (owner is Class) {
+      (owner as Class).ensureComments();
+    }
+    var result = super.comment;
+    if (result == '' && methodInheritedFrom != null) {
+      // This should be NOT from the MIRROR, but from the COMMENT.
+      methodInheritedFrom.comment; // Ensure comment field has been populated.
+      unresolvedComment = methodInheritedFrom.unresolvedComment;
+
+      comment = unresolvedComment == null ? '' :
+        markdown.markdownToHtml(unresolvedComment.trim(),
+            linkResolver: fixReference, inlineSyntaxes: MARKDOWN_SYNTAXES);
+      commentInheritedFrom = comment != '' ?
+          methodInheritedFrom.commentInheritedFrom : '';
+      result = comment;
+    }
+    return result;
+  }
+
+  bool isValidMirror(DeclarationMirror mirror) => mirror is MethodMirror;
+}
diff --git a/pkg/docgen/lib/src/models/mirror_based.dart b/pkg/docgen/lib/src/models/mirror_based.dart
new file mode 100644
index 0000000..eceac68
--- /dev/null
+++ b/pkg/docgen/lib/src/models/mirror_based.dart
@@ -0,0 +1,17 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.mirror_based;
+
+import '../exports/source_mirrors.dart';
+
+/// Docgen representation of an item to be documented, that wraps around a
+/// dart2js mirror.
+abstract class MirrorBased<TMirror extends DeclarationMirror> {
+  /// The original dart2js mirror around which this object wraps.
+  TMirror get mirror;
+
+  /// Return an informative [Object.toString] for debugging.
+  String toString() => "${super.toString()} - $mirror";
+}
diff --git a/pkg/docgen/lib/src/model_helpers.dart b/pkg/docgen/lib/src/models/model_helpers.dart
similarity index 77%
rename from pkg/docgen/lib/src/model_helpers.dart
rename to pkg/docgen/lib/src/models/model_helpers.dart
index 1c8a255..9f683d0 100644
--- a/pkg/docgen/lib/src/model_helpers.dart
+++ b/pkg/docgen/lib/src/models/model_helpers.dart
@@ -4,16 +4,76 @@
 
 library docgen.model_helpers;
 
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/dart2js_mirrors.dart'
-    as dart2js_mirrors;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
-    as dart2js_util;
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
-import '../../../../sdk/lib/_internal/libraries.dart';
+import 'dart:collection';
 
-import 'library_helpers.dart' show includePrivateMembers;
-import 'models.dart';
-import 'package_helpers.dart';
+import '../exports/dart2js_mirrors.dart' as dart2js_mirrors;
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+import '../exports/libraries.dart';
+
+import '../library_helpers.dart' show includePrivateMembers;
+import '../package_helpers.dart';
+
+import 'annotation.dart';
+import 'generic.dart';
+import 'indexable.dart';
+import 'library.dart';
+import 'method.dart';
+import 'parameter.dart';
+import 'variable.dart';
+
+String getLibraryDocName(LibraryMirror mirror) =>
+    dart2js_util.qualifiedNameOf(mirror).replaceAll('.', '-');
+
+/// Expand the method map [mapToExpand] into a more detailed map that
+/// separates out setters, getters, constructors, operators, and methods.
+Map expandMethodMap(Map<String, Method> mapToExpand) => {
+  'setters': recurseMap(filterMap(mapToExpand,
+      (key, val) => val.mirror.isSetter)),
+  'getters': recurseMap(filterMap(mapToExpand,
+      (key, val) => val.mirror.isGetter)),
+  'constructors': recurseMap(filterMap(mapToExpand,
+      (key, val) => val.mirror.isConstructor)),
+  'operators': recurseMap(filterMap(mapToExpand,
+      (key, val) => val.mirror.isOperator)),
+  'methods': recurseMap(filterMap(mapToExpand,
+      (key, val) => val.mirror.isRegularMethod && !val.mirror.isOperator))
+};
+
+String getDefaultValue(ParameterMirror mirror) {
+  if (!mirror.hasDefaultValue) return null;
+  return getDefaultValueFromConstMirror(mirror.defaultValue);
+}
+
+String getDefaultValueFromConstMirror(
+    dart2js_mirrors.Dart2JsConstantMirror valueMirror) {
+
+  if (valueMirror is dart2js_mirrors.Dart2JsStringConstantMirror) {
+    return '"${valueMirror.reflectee}"';
+  }
+
+  if (valueMirror is dart2js_mirrors.Dart2JsListConstantMirror) {
+    var buffer = new StringBuffer('[');
+
+    var values = new Iterable.generate(valueMirror.length,
+        (i) => valueMirror.getElement(i))
+        .map((e) => getDefaultValueFromConstMirror(e));
+
+    buffer.writeAll(values, ', ');
+
+    buffer.write(']');
+    return buffer.toString();
+  }
+
+  if (valueMirror is dart2js_mirrors.Dart2JsMapConstantMirror) {
+    // TODO(kevmoo) Handle non-empty case
+    if (valueMirror.length == 0) return '{}';
+  }
+
+  // TODO(kevmoo) Handle consts of non-core types
+
+  return '${valueMirror}';
+}
 
 /// Returns a list of meta annotations assocated with a mirror.
 List<Annotation> createAnnotations(DeclarationMirror mirror,
@@ -47,7 +107,7 @@
 
 /// Transforms the map by calling toMap on each value in it.
 Map recurseMap(Map inputMap) {
-  var outputMap = {};
+  var outputMap = new SplayTreeMap();
   inputMap.forEach((key, value) {
     if (value is Map) {
       outputMap[key] = recurseMap(value);
diff --git a/pkg/docgen/lib/src/models/owned_indexable.dart b/pkg/docgen/lib/src/models/owned_indexable.dart
new file mode 100644
index 0000000..ea3335d
--- /dev/null
+++ b/pkg/docgen/lib/src/models/owned_indexable.dart
@@ -0,0 +1,70 @@
+// 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 docgen.models.owned_indexable;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+import '../mdn.dart';
+import '../package_helpers.dart';
+
+import 'annotation.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'model_helpers.dart';
+
+abstract class OwnedIndexable<TMirror extends DeclarationMirror>
+    extends Indexable<TMirror> {
+  /// List of the meta annotations on this item.
+  final List<Annotation> annotations;
+
+  /// The object one scope-level above which this item is defined.
+  ///
+  /// Ex: The owner for a top level class, would be its enclosing library.
+  /// The owner of a local variable in a method would be the enclosing method.
+  final Indexable owner;
+
+  /// Returns this object's qualified name, but following the conventions
+  /// we're using in Dartdoc, which is that library names with dots in them
+  /// have them replaced with hyphens.
+  String get docName => owner.docName + '.' + dart2js_util.nameOf(mirror);
+
+  OwnedIndexable(DeclarationMirror mirror, Indexable owner)
+      : annotations = createAnnotations(mirror, owner.owningLibrary),
+        this.owner = owner,
+        super(mirror);
+
+  /// Generates MDN comments from database.json.
+  String getMdnComment() {
+    var domAnnotation = this.annotations.firstWhere(
+        (e) => e.mirror.qualifiedName == #metadata.DomName,
+        orElse: () => null);
+    if (domAnnotation == null) return '';
+    var domName = domAnnotation.parameters.single;
+
+    return mdnComment(rootDirectory, logger, domName);
+  }
+
+  String get packagePrefix => owner.packagePrefix;
+
+  String findElementInScope(String name) {
+    var lookupFunc = determineLookupFunc(name);
+    var result = lookupFunc(mirror, name);
+    if (result != null) {
+      result = getDocgenObject(result);
+      if (result is DummyMirror) return packagePrefix + result.docName;
+      return result.packagePrefix + result.docName;
+    }
+
+    if (owner != null) {
+      var result = owner.findElementInScope(name);
+      if (result != null) {
+        return result;
+      }
+    }
+    return super.findElementInScope(name);
+  }
+}
diff --git a/pkg/docgen/lib/src/models/parameter.dart b/pkg/docgen/lib/src/models/parameter.dart
new file mode 100644
index 0000000..2f8798c
--- /dev/null
+++ b/pkg/docgen/lib/src/models/parameter.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.parameter;
+
+import '../exports/mirrors_util.dart' as dart2js_util;
+import '../exports/source_mirrors.dart';
+
+import 'annotation.dart';
+import 'doc_gen_type.dart';
+import 'library.dart';
+import 'mirror_based.dart';
+import 'model_helpers.dart';
+
+/// Docgen wrapper around the dart2js mirror for a Dart
+/// method/function parameter.
+class Parameter extends MirrorBased {
+  final ParameterMirror mirror;
+  final String name;
+  final bool isOptional;
+  final bool isNamed;
+  final bool hasDefaultValue;
+  final DocGenType type;
+  final String defaultValue;
+  /// List of the meta annotations on the parameter.
+  final List<Annotation> annotations;
+
+  Parameter(ParameterMirror mirror, Library owningLibrary)
+      : this.mirror = mirror,
+        name = dart2js_util.nameOf(mirror),
+        isOptional = mirror.isOptional,
+        isNamed = mirror.isNamed,
+        hasDefaultValue = mirror.hasDefaultValue,
+        defaultValue = getDefaultValue(mirror),
+        type = new DocGenType(mirror.type, owningLibrary),
+        annotations = createAnnotations(mirror, owningLibrary);
+
+  /// Generates a map describing the [Parameter] object.
+  Map toMap() => {
+    'name': name,
+    'optional': isOptional,
+    'named': isNamed,
+    'default': hasDefaultValue,
+    'type': new List.filled(1, type.toMap()),
+    'value': defaultValue,
+    'annotations': annotations.map((a) => a.toMap()).toList()
+  };
+}
diff --git a/pkg/docgen/lib/src/models/typedef.dart b/pkg/docgen/lib/src/models/typedef.dart
new file mode 100644
index 0000000..17e4208
--- /dev/null
+++ b/pkg/docgen/lib/src/models/typedef.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.typedef;
+
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'dummy_mirror.dart';
+import 'library.dart';
+import 'model_helpers.dart';
+import 'generic.dart';
+import 'parameter.dart';
+import 'owned_indexable.dart';
+
+class Typedef extends OwnedIndexable<TypedefMirror> {
+  final String returnType;
+
+  final Map<String, Parameter> parameters;
+
+  /// Generic information about the typedef.
+  final Map<String, Generic> generics;
+
+  /// Returns the [Library] for the given [mirror] if it has already been
+  /// created, else creates it.
+  factory Typedef(TypedefMirror mirror, Library owningLibrary) {
+    var aTypedef = getDocgenObject(mirror, owningLibrary);
+    if (aTypedef is DummyMirror) {
+      aTypedef = new Typedef._(mirror, owningLibrary);
+    }
+    return aTypedef;
+  }
+
+  Typedef._(TypedefMirror mirror, Library owningLibrary)
+      : returnType = getDocgenObject(mirror.referent.returnType).docName,
+        generics = createGenerics(mirror),
+        parameters = createParameters(mirror.referent.parameters,
+            owningLibrary),
+        super(mirror, owningLibrary);
+
+  Map toMap() {
+    var map = {
+      'name': name,
+      'qualifiedName': qualifiedName,
+      'comment': comment,
+      'return': returnType,
+      'parameters': recurseMap(parameters),
+      'annotations': annotations.map((a) => a.toMap()).toList(),
+      'generics': recurseMap(generics)
+    };
+
+    // Typedef is displayed on the library page as a class, so a preview is
+    // added manually
+    var pre = preview;
+    if (pre != null) map['preview'] = pre;
+
+    return map;
+  }
+
+  String get typeName => 'typedef';
+
+  bool isValidMirror(DeclarationMirror mirror) => mirror is TypedefMirror;
+}
diff --git a/pkg/docgen/lib/src/models/variable.dart b/pkg/docgen/lib/src/models/variable.dart
new file mode 100644
index 0000000..2d8b02a
--- /dev/null
+++ b/pkg/docgen/lib/src/models/variable.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.models.variable;
+
+import '../exports/source_mirrors.dart';
+
+import '../library_helpers.dart';
+
+import 'class.dart';
+import 'doc_gen_type.dart';
+import 'dummy_mirror.dart';
+import 'indexable.dart';
+import 'owned_indexable.dart';
+
+
+/// A class containing properties of a Dart variable.
+class Variable extends OwnedIndexable<VariableMirror> {
+  final bool isFinal;
+  final bool isStatic;
+  final bool isConst;
+  final DocGenType type;
+  final String name;
+
+  factory Variable(String name, VariableMirror mirror, Indexable owner) {
+    var variable = getDocgenObject(mirror);
+    if (variable is DummyMirror) {
+      return new Variable._(name, mirror, owner);
+    }
+    return variable;
+  }
+
+  Variable._(this.name, VariableMirror mirror, Indexable owner)
+      : isFinal = mirror.isFinal,
+        isStatic = mirror.isStatic,
+        isConst = mirror.isConst,
+        type = new DocGenType(mirror.type, owner.owningLibrary),
+        super(mirror, owner);
+
+  /// Generates a map describing the [Variable] object.
+  Map toMap() => {
+    'name': name,
+    'qualifiedName': qualifiedName,
+    'comment': comment,
+    'final': isFinal,
+    'static': isStatic,
+    'constant': isConst,
+    'type': new List.filled(1, type.toMap()),
+    'annotations': annotations.map((a) => a.toMap()).toList()
+  };
+
+  String get typeName => 'property';
+
+  String get comment {
+    if (commentField != null) return commentField;
+    if (owner is Class) {
+      (owner as Class).ensureComments();
+    }
+    return super.comment;
+  }
+
+  bool isValidMirror(DeclarationMirror mirror) => mirror is VariableMirror;
+}
diff --git a/pkg/docgen/lib/src/package_helpers.dart b/pkg/docgen/lib/src/package_helpers.dart
index 599bb80..e8a36fc 100644
--- a/pkg/docgen/lib/src/package_helpers.dart
+++ b/pkg/docgen/lib/src/package_helpers.dart
@@ -4,7 +4,7 @@
 
 library docgen.package_helpers;
 
-import '../../../../sdk/lib/_internal/compiler/implementation/mirrors/source_mirrors.dart';
+import 'exports/source_mirrors.dart';
 
 import 'dart:io';
 import 'package:path/path.dart' as path;
diff --git a/pkg/docgen/pubspec.yaml b/pkg/docgen/pubspec.yaml
index 605766c..e6913dd 100644
--- a/pkg/docgen/pubspec.yaml
+++ b/pkg/docgen/pubspec.yaml
@@ -1,16 +1,13 @@
 name: docgen
-version: 0.9.2-dev
 author: Dart Team <misc@dartlang.org>
 description: A documentation generator for the Dart repository.
-homepage: https://github.com/dart-lang/dartdoc-viewer
-environment:
-  sdk: '>=0.8.10+6 <2.0.0'
+homepage: https://www.dartlang.org/
 dependencies:
   args: '>=0.9.0 <0.11.0'
   logging: '>=0.9.0 <0.10.0'
-  markdown: '0.6.0'
+  markdown: 0.7.0
   path: '>=0.9.0 <2.0.0'
   yaml: '>=0.9.0 <0.10.0'
 dev_dependencies:
-  scheduled_test: '>=0.10.0 <0.11.0'
+  scheduled_test: '>=0.10.0 <0.12.0'
   unittest: '>=0.9.0 <0.11.0'
diff --git a/pkg/docgen/test/constant_argument_test.dart b/pkg/docgen/test/constant_argument_test.dart
new file mode 100644
index 0000000..ca735f4
--- /dev/null
+++ b/pkg/docgen/test/constant_argument_test.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 docgen.test.typedef;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:scheduled_test/descriptor.dart' as d;
+import 'package:scheduled_test/scheduled_test.dart';
+
+import 'util.dart';
+import '../lib/docgen.dart' as dg;
+
+void main() {
+
+  setUp(() {
+    scheduleTempDir();
+  });
+
+  test('argument default values', () {
+    schedule(() {
+      var codeDir = getMultiLibraryCodePath();
+      expect(FileSystemEntity.isDirectorySync(codeDir), isTrue);
+      return dg.docgen([codeDir], out: p.join(d.defaultRoot, 'docs'));
+    });
+
+    schedule(() {
+      var path = p.join(d.defaultRoot, 'docs', 'test_lib.json');
+      var dartCoreJson = new File(path).readAsStringSync();
+
+      var testLibBar = JSON.decode(dartCoreJson) as Map<String, dynamic>;
+
+      //
+      // Validate function doc references
+      //
+      var functionDef =
+          testLibBar['functions']['methods']['positionalDefaultValues']
+          as Map<String, dynamic>;
+
+      var params = functionDef['parameters'] as Map<String, dynamic>;
+
+      var vals = {};
+      params.forEach((paramName, paramHash) {
+        expect(_PARAM_VALUES, contains(paramName));
+        expect(paramHash['value'], _PARAM_VALUES[paramName],
+            reason: 'Value for $paramName should match expected');
+      });
+    });
+  });
+}
+
+final _PARAM_VALUES = {
+  "boolConst": "true",
+  "intConst": "42",
+  "listConst": '[true, 42, "Shanna", null, 3.14, []]',
+  "mapConst": startsWith("Map"),
+  "emptyMap": '{}',
+  "stringConst": "\"Shanna\""
+};
diff --git a/pkg/docgen/test/generate_json_test.dart b/pkg/docgen/test/generate_json_test.dart
index 9149a96..a6ac53d 100644
--- a/pkg/docgen/test/generate_json_test.dart
+++ b/pkg/docgen/test/generate_json_test.dart
@@ -30,6 +30,8 @@
         d.matcherFile('index.json', isJsonMap),
         d.matcherFile('index.txt', hasSortedLines),
         d.matcherFile('library_list.json', isJsonMap),
+        d.matcherFile('library_list.json',
+            startsWith(_LIBRARY_LIST_UNINDENT_START)),
         d.matcherFile('test_lib-bar.C.json', isJsonMap),
         d.matcherFile('test_lib-bar.json', isJsonMap),
         d.matcherFile('test_lib-foo.B.json', isJsonMap),
@@ -39,6 +41,7 @@
         d.matcherFile('test_lib.C.json', isJsonMap),
         d.matcherFile('test_lib.json', isJsonMap),
     ]).validate();
-
   });
 }
+
+const _LIBRARY_LIST_UNINDENT_START = '{"libraries":[{"packageName":""';
diff --git a/pkg/docgen/test/inherited_comments_test.dart b/pkg/docgen/test/inherited_comments_test.dart
new file mode 100644
index 0000000..2f229f94
--- /dev/null
+++ b/pkg/docgen/test/inherited_comments_test.dart
@@ -0,0 +1,49 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library docgen.test.inherited_comments;
+
+import 'dart:convert';
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:scheduled_test/descriptor.dart' as d;
+import 'package:scheduled_test/scheduled_test.dart';
+
+import 'util.dart';
+import '../lib/docgen.dart' as dg;
+
+void main() {
+
+  setUp(() {
+    scheduleTempDir();
+  });
+
+  test('inherited comments', () {
+    schedule(() {
+      var codeDir = getMultiLibraryCodePath();
+      expect(FileSystemEntity.isDirectorySync(codeDir), isTrue);
+      return dg.docgen([codeDir], out: p.join(d.defaultRoot, 'docs'));
+    });
+
+    schedule(() {
+      var path = p.join(d.defaultRoot, 'docs', 'dart-core.Set.json');
+      var dartCoreSetJson = new File(path).readAsStringSync();
+
+      var dartCoreSet = JSON.decode(dartCoreSetJson) as Map<String, dynamic>;
+
+      var toListDetails = dartCoreSet['inheritedMethods']['methods']['toList']
+          as Map<String, dynamic>;
+
+      expect(toListDetails, containsPair('comment', _TO_LIST_COMMENT));
+      expect(toListDetails, containsPair('commentFrom', _TO_LIST_COMMENT_FROM));
+    });
+  });
+}
+
+const _TO_LIST_COMMENT = "<p>Creates a <a>dart-core.List</a> containing the "
+    "elements of this <a>dart-core.Iterable</a>.</p>\n<p>The elements are in "
+    "iteration order. The list is fixed-length\nif "
+    "<a>dart-core.Set.toList.growable</a> is false.</p>";
+const _TO_LIST_COMMENT_FROM = "dart-core.Iterable.toList";
diff --git a/pkg/docgen/test/multi_library_code/lib/root_lib.dart b/pkg/docgen/test/multi_library_code/lib/root_lib.dart
new file mode 100644
index 0000000..74423ab
--- /dev/null
+++ b/pkg/docgen/test/multi_library_code/lib/root_lib.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library root_lib;
+
+export 'test_lib.dart';
+export 'test_lib_foo.dart';
+export 'test_lib_bar.dart';
+
+class RootClass {
+  RootClass(int a, int b);
+}
diff --git a/pkg/docgen/test/multi_library_code/lib/test_lib.dart b/pkg/docgen/test/multi_library_code/lib/test_lib.dart
index 06d1ccf..0ecf007c 100644
--- a/pkg/docgen/test/multi_library_code/lib/test_lib.dart
+++ b/pkg/docgen/test/multi_library_code/lib/test_lib.dart
@@ -39,3 +39,22 @@
 B sampleMethod(C cInstance) {
   throw new UnimplementedError();
 }
+
+int positionalDefaultValues([int intConst = INT_CONST,
+    bool boolConst = BOOL_CONST, List listConst = LIST_CONST,
+    String stringConst = STRING_CONST, Map mapConst = MAP_CONST,
+    Map emptyMap = EMPTY_MAP_CONST]) {
+  throw new UnimplementedError();
+}
+
+const int INT_CONST = 42;
+
+const bool BOOL_CONST = true;
+
+const LIST_CONST = const [true, 42, 'Shanna', null, 3.14, const []];
+
+const STRING_CONST = 'Shanna';
+
+const MAP_CONST = const {'a':1, 2: true, 'c': const [1,null,true]};
+
+const EMPTY_MAP_CONST = const {};
diff --git a/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart b/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart
index 094d554..6f36d9b 100644
--- a/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart
+++ b/pkg/docgen/test/multi_library_code/lib/test_lib_bar.dart
@@ -5,6 +5,7 @@
 library test_lib.bar;
 
 import 'test_lib.dart';
+import 'test_lib_foo.dart';
 
 /*
  * Normal comment for class C.
@@ -12,12 +13,18 @@
 class C {
 }
 
-/// [input] is of type [C] returns an [A].
-A generateFoo(C input) {
+/// Processes an [input] of type [C] instance for testing.
+///
+/// To eliminate import warnings for [A] and to test typedefs.
+///
+/// It's important that the [List<A>] for param [listOfA] is not empty.
+A testMethod(C input, List<A> listOfA, B aBee) {
   throw 'noop';
 }
 
-/// Processes a [C] instance for testing.
+/// Processes an [input] of type [C] instance for testing.
 ///
 /// To eliminate import warnings for [A] and to test typedefs.
-typedef A AnATransformer(C other);
+///
+/// It's important that the [List<A>] for param [listOfA] is not empty.
+typedef A testTypedef(C other, List<A> listOfA, B aBee);
diff --git a/pkg/docgen/test/multi_library_test.dart b/pkg/docgen/test/multi_library_test.dart
index ceee380..3ff966d 100644
--- a/pkg/docgen/test/multi_library_test.dart
+++ b/pkg/docgen/test/multi_library_test.dart
@@ -8,8 +8,7 @@
 import 'package:unittest/unittest.dart';
 
 import '../lib/docgen.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
-    as dart2js_util;
+import '../lib/src/exports/mirrors_util.dart' as dart2js_util;
 
 import 'util.dart';
 
diff --git a/pkg/docgen/test/single_library_test.dart b/pkg/docgen/test/single_library_test.dart
index 295cede..31f3a5e 100644
--- a/pkg/docgen/test/single_library_test.dart
+++ b/pkg/docgen/test/single_library_test.dart
@@ -9,9 +9,8 @@
 import 'package:path/path.dart' as path;
 import 'package:unittest/unittest.dart';
 
+import '../lib/src/exports/mirrors_util.dart' as dart2js_util;
 import '../lib/docgen.dart';
-import '../../../sdk/lib/_internal/compiler/implementation/mirrors/mirrors_util.dart'
-    as dart2js_util;
 
 const String DART_LIBRARY = '''
   library test;
diff --git a/pkg/docgen/test/typedef_test.dart b/pkg/docgen/test/typedef_test.dart
index 9911f08..ca1dc6a 100644
--- a/pkg/docgen/test/typedef_test.dart
+++ b/pkg/docgen/test/typedef_test.dart
@@ -28,21 +28,20 @@
     });
 
     schedule(() {
-      var path = p.join(d.defaultRoot, 'docs', 'test_lib-bar.json');
-      var dartCoreJson = new File(path).readAsStringSync();
+      var path = p.join(d.defaultRoot, 'docs', 'root_lib.json');
+      var rootLibJson = new File(path).readAsStringSync();
 
-      var testLibBar = JSON.decode(dartCoreJson) as Map<String, dynamic>;
+      var rootLib = JSON.decode(rootLibJson) as Map<String, dynamic>;
 
       //
       // Validate function doc references
       //
-      var generateFoo = testLibBar['functions']['methods']['generateFoo']
+      var testMethod = rootLib['functions']['methods']['testMethod']
           as Map<String, dynamic>;
 
-      expect(generateFoo['comment'], '<p><a>test_lib-bar.generateFoo.input</a> '
-          'is of type <a>test_lib-bar.C</a> returns an <a>test_lib.A</a>.</p>');
+      expect(testMethod['comment'], _TEST_METHOD_COMMENT);
 
-      var classes = testLibBar['classes'] as Map<String, dynamic>;
+      var classes = rootLib['classes'] as Map<String, dynamic>;
 
       expect(classes, hasLength(3));
 
@@ -50,16 +49,42 @@
       expect(classes['error'], isList);
 
       var typeDefs = classes['typedef'] as Map<String, dynamic>;
-      var comparator = typeDefs['AnATransformer'] as Map<String, dynamic>;
+      var comparator = typeDefs['testTypedef'] as Map<String, dynamic>;
 
-      var expectedPreview = '<p>Processes a [C] instance for testing.</p>';
+      expect(comparator['preview'], _TEST_TYPEDEF_PREVIEW);
 
-      expect(comparator['preview'], expectedPreview);
+      expect(comparator['comment'], _TEST_TYPEDEF_COMMENT);
+    });
 
-      var expectedComment = expectedPreview + '\n'
-          '<p>To eliminate import warnings for [A] and to test typedefs.</p>';
+    schedule(() {
+      var path = p.join(d.defaultRoot, 'docs', 'root_lib.RootClass.json');
+      var rootClassJson = new File(path).readAsStringSync();
 
-      expect(comparator['comment'], expectedComment);
+      var rootClass = JSON.decode(rootClassJson) as Map<String, dynamic>;
+
+      var defaultCtor = rootClass['methods']['constructors'][''] as Map;
+
+      expect(defaultCtor['qualifiedName'], 'root_lib.RootClass.RootClass-');
     });
   });
 }
+
+// TOOD: [List<A>] is not formatted correctly - issue 16771
+const _TEST_METHOD_COMMENT = '<p>Processes an '
+    '<a>root_lib.testMethod.input</a> of type <a>root_lib.C</a> '
+    'instance for testing.</p>\n<p>To eliminate import warnings for '
+    '<a>root_lib.A</a> and to test typedefs.</p>\n<p>It\'s important that the'
+    ' <a>dart-core</a>&lt;A> for param <a>root_lib.testMethod.listOfA</a> '
+    'is not empty.</p>';
+
+// TODO: [input] is not turned into a param refenece
+const _TEST_TYPEDEF_PREVIEW = '<p>Processes an input of type '
+    '<a>root_lib.C</a> instance for testing.</p>';
+
+// TOOD: [List<A>] is not formatted correctly - issue 16771
+// TODO: [listOfA] is not turned into a param reference
+final _TEST_TYPEDEF_COMMENT = _TEST_TYPEDEF_PREVIEW + '\n<p>To eliminate import'
+    ' warnings for <a>root_lib.A</a> and to test typedefs.</p>\n<p>It\'s '
+    'important that the <a>dart-core</a>&lt;A> for param listOfA is not '
+    'empty.</p>';
+
diff --git a/pkg/intl/lib/date_symbol_data_file.dart b/pkg/intl/lib/date_symbol_data_file.dart
index a4a67fe..1e641df 100644
--- a/pkg/intl/lib/date_symbol_data_file.dart
+++ b/pkg/intl/lib/date_symbol_data_file.dart
@@ -7,16 +7,19 @@
  * locale data from files in the file system.
  */
 
-library date_symbol_data_json;
+library date_symbol_data_file;
 
 import 'dart:async';
-import "date_symbols.dart";
-import "src/lazy_locale_data.dart";
-import 'src/date_format_internal.dart';
-import 'src/file_data_reader.dart';
+
 import 'package:path/path.dart' as path;
 
-part "src/data/dates/localeList.dart";
+import 'date_symbols.dart';
+import 'src/data/dates/locale_list.dart';
+import 'src/date_format_internal.dart';
+import 'src/file_data_reader.dart';
+import 'src/lazy_locale_data.dart';
+
+export 'src/data/dates/locale_list.dart';
 
 /**
  * This should be called for at least one [locale] before any date formatting
diff --git a/pkg/intl/lib/date_symbol_data_http_request.dart b/pkg/intl/lib/date_symbol_data_http_request.dart
index 3fe56c6..4c54147 100644
--- a/pkg/intl/lib/date_symbol_data_http_request.dart
+++ b/pkg/intl/lib/date_symbol_data_http_request.dart
@@ -6,16 +6,18 @@
  * This file should be imported, along with date_format.dart in order to read
  * locale data via http requests to a web server..
  */
-library date_symbol_data_json;
+library date_symbol_data_http_request;
 
 import 'dart:async';
+
 import 'date_symbols.dart';
-import 'src/lazy_locale_data.dart';
+import 'intl.dart';
+import 'src/data/dates/locale_list.dart';
 import 'src/date_format_internal.dart';
 import 'src/http_request_data_reader.dart';
-import 'intl.dart';
+import 'src/lazy_locale_data.dart';
 
-part "src/data/dates/localeList.dart";
+export 'src/data/dates/locale_list.dart';
 
 /**
  * This should be called for at least one [locale] before any date formatting
diff --git a/pkg/intl/lib/date_symbol_data_local.dart b/pkg/intl/lib/date_symbol_data_local.dart
index a8ce2b8..9082b0a 100644
--- a/pkg/intl/lib/date_symbol_data_local.dart
+++ b/pkg/intl/lib/date_symbol_data_local.dart
@@ -14,13 +14,13 @@
  * removed after those changes land to CLDR.
  */
 
-library date_symbol_data;
+library date_symbol_data_local;
 
 import 'dart:async';
 
 import "date_symbols.dart";
-import "src/date_format_internal.dart";
 import "date_time_patterns.dart";
+import "src/date_format_internal.dart";
 
 /**
  * This should be called for at least one [locale] before any date
diff --git a/pkg/intl/lib/intl.dart b/pkg/intl/lib/intl.dart
index 59357c5..2e0cab52 100644
--- a/pkg/intl/lib/intl.dart
+++ b/pkg/intl/lib/intl.dart
@@ -235,7 +235,7 @@
     // We treat C as a special case, and assume it wants en_ISO for formatting.
     // TODO(alanknight): en_ISO is probably not quite right for the C/Posix
     // locale for formatting. Consider adding C to the formats database.
-    if (aLocale == null) return systemLocale;
+    if (aLocale == null) return getCurrentLocale();
     if (aLocale == "C") return "en_ISO";
     if ((aLocale.length < 5) || (aLocale.length > 6)) return aLocale;
     if (aLocale[2] != '-' && (aLocale[2] != '_')) return aLocale;
diff --git a/pkg/intl/lib/src/data/dates/localeList.dart b/pkg/intl/lib/src/data/dates/locale_list.dart
similarity index 92%
rename from pkg/intl/lib/src/data/dates/localeList.dart
rename to pkg/intl/lib/src/data/dates/locale_list.dart
index 6325924..0e74f64 100644
--- a/pkg/intl/lib/src/data/dates/localeList.dart
+++ b/pkg/intl/lib/src/data/dates/locale_list.dart
@@ -2,10 +2,10 @@
 // for details. All rights reserved. Use of this sourcecode is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-part of date_symbol_data_json;
+library intl.locale_list;
 
 /// Hard-coded list of all available locales for dates.
-final availableLocalesForDateFormatting = const ["en_ISO",
+const availableLocalesForDateFormatting = const ["en_ISO",
     "af",
     "am",
     "ar",
@@ -84,4 +84,4 @@
     "zh_CN",
     "zh_HK",
     "zh_TW",
-    "zu"];
\ No newline at end of file
+    "zu"];
diff --git a/pkg/mock/lib/mock.dart b/pkg/mock/lib/mock.dart
index b95f8f3..768cae2 100644
--- a/pkg/mock/lib/mock.dart
+++ b/pkg/mock/lib/mock.dart
@@ -105,1429 +105,21 @@
 
 library mock;
 
-import 'dart:mirrors';
-import 'dart:collection' show LinkedHashMap;
+export 'src/action.dart';
+export 'src/behavior.dart';
+export 'src/call_matcher.dart';
+export 'src/log_entry.dart';
+export 'src/log_entry_list.dart';
+export 'src/mock.dart';
+export 'src/responder.dart';
+export 'src/result_matcher.dart';
+export 'src/result_set_matcher.dart';
+export 'src/times_matcher.dart';
 
-import 'package:matcher/matcher.dart';
+import 'src/log_entry_list.dart';
 
 /**
- * The error formatter for mocking is a bit different from the default one
- * for unit testing; instead of the third argument being a 'reason'
- * it is instead a [signature] describing the method signature filter
- * that was used to select the logs that were verified.
+ * [sharedLog] is not used in this library and is deprecated.
  */
-String _mockingErrorFormatter(actual, Matcher matcher, String signature,
-                              Map matchState, bool verbose) {
-  var description = new StringDescription();
-  description.add('Expected ${signature} ').addDescriptionOf(matcher).
-      add('\n     but: ');
-  matcher.describeMismatch(actual, description, matchState, verbose).add('.');
-  return description.toString();
-}
-
-/**
- * The failure handler for the [expect()] calls that occur in [verify()]
- * methods in the mock objects. This calls the real failure handler used
- * by the unit test library after formatting the error message with
- * the custom formatter.
- */
-class _MockFailureHandler implements FailureHandler {
-  FailureHandler proxy;
-  _MockFailureHandler(this.proxy);
-  void fail(String reason) {
-    proxy.fail(reason);
-  }
-  void failMatch(actual, Matcher matcher, String reason,
-                 Map matchState, bool verbose) {
-    proxy.fail(_mockingErrorFormatter(actual, matcher, reason,
-        matchState, verbose));
-  }
-}
-
-_MockFailureHandler _mockFailureHandler = null;
-
-/** Sentinel value for representing no argument. */
-class _Sentinel {
-  const _Sentinel();
-}
-const _noArg = const _Sentinel();
-
-/** The ways in which a call to a mock method can be handled. */
-class Action {
-  /** Do nothing (void method) */
-  static const IGNORE = const Action._('IGNORE');
-
-  /** Return a supplied value. */
-  static const RETURN = const Action._('RETURN');
-
-  /** Throw a supplied value. */
-  static const THROW = const Action._('THROW');
-
-  /** Call a supplied function. */
-  static const PROXY = const Action._('PROXY');
-
-  const Action._(this.name);
-
-  final String name;
-
-  String toString() => 'Action: $name';
-}
-
-/**
- * The behavior of a method call in the mock library is specified
- * with [Responder]s. A [Responder] has a [value] to throw
- * or return (depending on the type of [action]),
- * and can either be one-shot, multi-shot, or infinitely repeating,
- * depending on the value of [count (1, greater than 1, or 0 respectively).
- */
-class Responder {
-  final Object value;
-  final Action action;
-  int count;
-  Responder(this.value, [this.count = 1, this.action = Action.RETURN]);
-}
-
-/**
- * A [CallMatcher] is a special matcher used to match method calls (i.e.
- * a method name and set of arguments). It is not a [Matcher] like the
- * unit test [Matcher], but instead represents a method name and a
- * collection of [Matcher]s, one per argument, that will be applied
- * to the parameters to decide if the method call is a match.
- */
-class CallMatcher {
-  Matcher nameFilter;
-  List<Matcher> argMatchers;
-
-  /**
-   * Constructor for [CallMatcher]. [name] can be null to
-   * match anything, or a literal [String], a predicate [Function],
-   * or a [Matcher]. The various arguments can be scalar values or
-   * [Matcher]s.
-   */
-  CallMatcher([name,
-              arg0 = _noArg,
-              arg1 = _noArg,
-              arg2 = _noArg,
-              arg3 = _noArg,
-              arg4 = _noArg,
-              arg5 = _noArg,
-              arg6 = _noArg,
-              arg7 = _noArg,
-              arg8 = _noArg,
-              arg9 = _noArg]) {
-    if (name == null) {
-      nameFilter = anything;
-    } else {
-      nameFilter = wrapMatcher(name);
-    }
-    argMatchers = new List<Matcher>();
-    if (identical(arg0, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg0));
-    if (identical(arg1, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg1));
-    if (identical(arg2, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg2));
-    if (identical(arg3, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg3));
-    if (identical(arg4, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg4));
-    if (identical(arg5, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg5));
-    if (identical(arg6, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg6));
-    if (identical(arg7, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg7));
-    if (identical(arg8, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg8));
-    if (identical(arg9, _noArg)) return;
-    argMatchers.add(wrapMatcher(arg9));
-  }
-
-  /**
-   * We keep our behavior specifications in a Map, which is keyed
-   * by the [CallMatcher]. To make the keys unique and to get a
-   * descriptive value for the [CallMatcher] we have this override
-   * of [toString()].
-   */
-  String toString() {
-    Description d = new StringDescription();
-    d.addDescriptionOf(nameFilter);
-    // If the nameFilter was a simple string - i.e. just a method name -
-    // strip the quotes to make this more natural in appearance.
-    if (d.toString()[0] == "'") {
-      d.replace(d.toString().substring(1, d.toString().length - 1));
-    }
-    d.add('(');
-    for (var i = 0; i < argMatchers.length; i++) {
-      if (i > 0) d.add(', ');
-      d.addDescriptionOf(argMatchers[i]);
-    }
-    d.add(')');
-    return d.toString();
-  }
-
-  /**
-   * Given a [method] name and list of [arguments], return true
-   * if it matches this [CallMatcher.
-   */
-  bool matches(String method, List arguments) {
-    var matchState = {};
-    if (!nameFilter.matches(method, matchState)) {
-      return false;
-    }
-    var numArgs = (arguments == null) ? 0 : arguments.length;
-    if (numArgs < argMatchers.length) {
-      throw new Exception("Less arguments than matchers for $method.");
-    }
-    for (var i = 0; i < argMatchers.length; i++) {
-      if (!argMatchers[i].matches(arguments[i], matchState)) {
-        return false;
-      }
-    }
-    return true;
-  }
-}
-
-/**
- * Returns a [CallMatcher] for the specified signature. [method] can be
- * null to match anything, or a literal [String], a predicate [Function],
- * or a [Matcher]. The various arguments can be scalar values or [Matcher]s.
- * To match getters and setters, use "get " and "set " prefixes on the names.
- * For example, for a property "foo", you could use "get foo" and "set foo"
- * as literal string arguments to callsTo to match the getter and setter
- * of "foo".
- */
-CallMatcher callsTo([method,
-                     arg0 = _noArg,
-                     arg1 = _noArg,
-                     arg2 = _noArg,
-                     arg3 = _noArg,
-                     arg4 = _noArg,
-                     arg5 = _noArg,
-                     arg6 = _noArg,
-                     arg7 = _noArg,
-                     arg8 = _noArg,
-                     arg9 = _noArg]) {
-  return new CallMatcher(method, arg0, arg1, arg2, arg3, arg4,
-      arg5, arg6, arg7, arg8, arg9);
-}
-
-/**
- * A [Behavior] represents how a [Mock] will respond to one particular
- * type of method call.
- */
-class Behavior {
-  CallMatcher matcher; // The method call matcher.
-  List<Responder> actions; // The values to return/throw or proxies to call.
-  bool logging = true;
-
-  Behavior (this.matcher) {
-    actions = new List<Responder>();
-  }
-
-  /**
-   * Adds a [Responder] that returns a [value] for [count] calls
-   * (1 by default).
-   */
-  Behavior thenReturn(value, [count = 1]) {
-    actions.add(new Responder(value, count, Action.RETURN));
-    return this; // For chaining calls.
-  }
-
-  /** Adds a [Responder] that repeatedly returns a [value]. */
-  Behavior alwaysReturn(value) {
-    return thenReturn(value, 0);
-  }
-
-  /**
-   * Adds a [Responder] that throws [value] [count]
-   * times (1 by default).
-   */
-  Behavior thenThrow(value, [count = 1]) {
-    actions.add(new Responder(value, count, Action.THROW));
-    return this; // For chaining calls.
-  }
-
-  /** Adds a [Responder] that throws [value] endlessly. */
-  Behavior alwaysThrow(value) {
-    return thenThrow(value, 0);
-  }
-
-  /**
-   * [thenCall] creates a proxy Responder, that is called [count]
-   * times (1 by default; 0 is used for unlimited calls, and is
-   * exposed as [alwaysCall]). [value] is the function that will
-   * be called with the same arguments that were passed to the
-   * mock. Proxies can be used to wrap real objects or to define
-   * more complex return/throw behavior. You could even (if you
-   * wanted) use proxies to emulate the behavior of thenReturn;
-   * e.g.:
-   *
-   *     m.when(callsTo('foo')).thenReturn(0)
-   *
-   * is equivalent to:
-   *
-   *     m.when(callsTo('foo')).thenCall(() => 0)
-   */
-  Behavior thenCall(value, [count = 1]) {
-    actions.add(new Responder(value, count, Action.PROXY));
-    return this; // For chaining calls.
-  }
-
-  /** Creates a repeating proxy call. */
-  Behavior alwaysCall(value) {
-    return thenCall(value, 0);
-  }
-
-  /** Returns true if a method call matches the [Behavior]. */
-  bool matches(String method, List args) => matcher.matches(method, args);
-
-  /** Returns the [matcher]'s representation. */
-  String toString() => matcher.toString();
-}
-
-/**
- * Every call to a [Mock] object method is logged. The logs are
- * kept in instances of [LogEntry].
- */
-class LogEntry {
-  /** The time of the event. */
-  DateTime time;
-
-  /** The mock object name, if any. */
-  final String mockName;
-
-  /** The method name. */
-  final String methodName;
-
-  /** The parameters. */
-  final List args;
-
-  /** The behavior that resulted. */
-  final Action action;
-
-  /** The value that was returned (if no throw). */
-  final value;
-
-  LogEntry(this.mockName, this.methodName,
-      this.args, this.action, [this.value]) {
-    time = new DateTime.now();
-  }
-
-  String _pad2(int val) => (val >= 10 ? '$val' : '0$val');
-
-  String toString([DateTime baseTime]) {
-    Description d = new StringDescription();
-    if (baseTime == null) {
-      // Show absolute time.
-      d.add('${time.hour}:${_pad2(time.minute)}:'
-          '${_pad2(time.second)}.${time.millisecond}>  ');
-    } else {
-      // Show relative time.
-      int delta = time.millisecondsSinceEpoch - baseTime.millisecondsSinceEpoch;
-      int secs = delta ~/ 1000;
-      int msecs = delta % 1000;
-      d.add('$secs.$msecs>  ');
-    }
-    d.add('${_qualifiedName(mockName, methodName)}(');
-    if (args != null) {
-      for (var i = 0; i < args.length; i++) {
-        if (i != 0) d.add(', ');
-        d.addDescriptionOf(args[i]);
-      }
-    }
-    d.add(') ${action == Action.THROW ? "threw" : "returned"} ');
-    d.addDescriptionOf(value);
-    return d.toString();
-  }
-}
-
-/** Utility function for optionally qualified method names */
-String _qualifiedName(owner, String method) {
-  if (owner == null || identical(owner, anything)) {
-    return method;
-  } else if (owner is Matcher) {
-    Description d = new StringDescription();
-    d.addDescriptionOf(owner);
-    d.add('.');
-    d.add(method);
-    return d.toString();
-  } else {
-    return '$owner.$method';
-  }
-}
-
-/**
-* [StepValidator]s are used by [stepwiseValidate] in [LogEntryList], which
-* iterates through the list and call the [StepValidator] function with the
-* log [List] and position. The [StepValidator] should return the number of
-* positions to advance upon success, or zero upon failure. When zero is
-* returned an error is reported.
-*/
-typedef int StepValidator(List<LogEntry> logs, int pos);
-
-/**
- * We do verification on a list of [LogEntry]s. To allow chaining
- * of calls to verify, we encapsulate such a list in the [LogEntryList]
- * class.
- */
-class LogEntryList {
-  String filter;
-  List<LogEntry> logs;
-  LogEntryList([this.filter]) {
-    logs = new List<LogEntry>();
-  }
-
-  /** Add a [LogEntry] to the log. */
-  add(LogEntry entry) => logs.add(entry);
-
-  /** Get the first entry, or null if no entries. */
-  get first => (logs == null || logs.length == 0) ? null : logs[0];
-
-  /** Get the last entry, or null if no entries. */
-  get last => (logs == null || logs.length == 0) ? null : logs.last;
-
-  /** Creates a LogEntry predicate function from the argument. */
-  Function _makePredicate(arg) {
-    if (arg == null) {
-      return (e) => true;
-    } else if (arg is CallMatcher) {
-      return (e) => arg.matches(e.methodName, e.args);
-    } else if (arg is Function) {
-      return arg;
-    } else {
-      throw new Exception("Invalid argument to _makePredicate.");
-    }
-  }
-
-  /**
-   * Create a new [LogEntryList] consisting of [LogEntry]s from
-   * this list that match the specified [mockNameFilter] and [logFilter].
-   * [mockNameFilter] can be null, a [String], a predicate [Function],
-   * or a [Matcher]. If [mockNameFilter] is null, this is the same as
-   * [anything].
-   * If [logFilter] is null, all entries in the log will be returned.
-   * Otherwise [logFilter] should be a [CallMatcher] or  predicate function
-   * that takes a [LogEntry] and returns a bool.
-   * If [destructive] is true, the log entries are removed from the
-   * original list.
-   */
-  LogEntryList getMatches([mockNameFilter,
-                          logFilter,
-                          Matcher actionMatcher,
-                          bool destructive = false]) {
-    if (mockNameFilter == null) {
-      mockNameFilter = anything;
-    } else {
-      mockNameFilter = wrapMatcher(mockNameFilter);
-    }
-    Function entryFilter = _makePredicate(logFilter);
-    String filterName = _qualifiedName(mockNameFilter, logFilter.toString());
-    LogEntryList rtn = new LogEntryList(filterName);
-    var matchState = {};
-    for (var i = 0; i < logs.length; i++) {
-      LogEntry entry = logs[i];
-      if (mockNameFilter.matches(entry.mockName, matchState) &&
-          entryFilter(entry)) {
-        if (actionMatcher == null ||
-            actionMatcher.matches(entry, matchState)) {
-          rtn.add(entry);
-          if (destructive) {
-            int startIndex = i--;
-            logs.removeRange(startIndex, startIndex + 1);
-          }
-        }
-      }
-    }
-    return rtn;
-  }
-
-  /** Apply a unit test [Matcher] to the [LogEntryList]. */
-  LogEntryList verify(Matcher matcher) {
-    if (_mockFailureHandler == null) {
-      _mockFailureHandler =
-          new _MockFailureHandler(getOrCreateExpectFailureHandler());
-    }
-    expect(logs, matcher, reason:filter, failureHandler: _mockFailureHandler);
-    return this;
-  }
-
-  /**
-   * Iterate through the list and call the [validator] function with the
-   * log [List] and position. The [validator] should return the number of
-   * positions to advance upon success, or zero upon failure. When zero is
-   * returned an error is reported. [reason] can be used to provide a
-   * more descriptive failure message. If a failure occurred false will be
-   * returned (unless the failure handler itself threw an exception);
-   * otherwise true is returned.
-   * The use case here is to perform more complex validations; for example
-   * we may want to assert that the return value from some function is
-   * later used as a parameter to a following function. If we filter the logs
-   * to include just these two functions we can write a simple validator to
-   * do this check.
-   */
-  bool stepwiseValidate(StepValidator validator, [String reason = '']) {
-    if (_mockFailureHandler == null) {
-      _mockFailureHandler =
-          new _MockFailureHandler(getOrCreateExpectFailureHandler());
-    }
-    var i = 0;
-    while (i < logs.length) {
-      var n = validator(logs, i);
-      if (n == 0) {
-        if (reason.length > 0) {
-          reason = ': $reason';
-        }
-        _mockFailureHandler.fail("Stepwise validation failed at $filter "
-                                 "position $i$reason");
-        return false;
-      } else {
-        i += n;
-      }
-    }
-    return true;
-  }
-
-  /**
-   * Turn the logs into human-readable text. If [baseTime] is specified
-   * then each entry is prefixed with the offset from that time in
-   * milliseconds; otherwise the time of day is used.
-   */
-  String toString([DateTime baseTime]) {
-    String s = '';
-    for (var e in logs) {
-      s = '$s${e.toString(baseTime)}\n';
-    }
-    return s;
-  }
-
-  /**
-   *  Find the first log entry that satisfies [logFilter] and
-   *  return its position. A search [start] position can be provided
-   *  to allow for repeated searches. [logFilter] can be a [CallMatcher],
-   *  or a predicate function that takes a [LogEntry] argument and returns
-   *  a bool. If [logFilter] is null, it will match any [LogEntry].
-   *  If no entry is found, then [failureReturnValue] is returned.
-   *  After each check the position is updated by [skip], so using
-   *  [skip] of -1 allows backward searches, using a [skip] of 2 can
-   *  be used to check pairs of adjacent entries, and so on.
-   */
-  int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1,
-      skip = 1]) {
-    logFilter = _makePredicate(logFilter);
-    int pos = start;
-    while (pos >= 0 && pos < logs.length) {
-      if (logFilter(logs[pos])) {
-        return pos;
-      }
-      pos += skip;
-    }
-    return failureReturnValue;
-  }
-
-  /**
-   * Returns log events that happened up to the first one that
-   * satisfies [logFilter]. If [inPlace] is true, then returns
-   * this LogEntryList after removing the from the first satisfier;
-   * onwards otherwise a new list is created. [description]
-   * is used to create a new name for the resulting list.
-   * [defaultPosition] is used as the index of the matching item in
-   * the case that no match is found.
-   */
-  LogEntryList _head(logFilter, bool inPlace,
-                     String description, int defaultPosition) {
-    if (filter != null) {
-      description = '$filter $description';
-    }
-    int pos = findLogEntry(logFilter, 0, defaultPosition);
-    if (inPlace) {
-      if (pos < logs.length) {
-        logs.removeRange(pos, logs.length);
-      }
-      filter = description;
-      return this;
-    } else {
-      LogEntryList newList = new LogEntryList(description);
-      for (var i = 0; i < pos; i++) {
-        newList.logs.add(logs[i]);
-      }
-      return newList;
-    }
-  }
-
-  /**
-   * Returns log events that happened from the first one that
-   * satisfies [logFilter]. If [inPlace] is true, then returns
-   * this LogEntryList after removing the entries up to the first
-   * satisfier; otherwise a new list is created. [description]
-   * is used to create a new name for the resulting list.
-   * [defaultPosition] is used as the index of the matching item in
-   * the case that no match is found.
-   */
-  LogEntryList _tail(logFilter, bool inPlace,
-                     String description, int defaultPosition) {
-    if (filter != null) {
-      description = '$filter $description';
-    }
-    int pos = findLogEntry(logFilter, 0, defaultPosition);
-    if (inPlace) {
-      if (pos > 0) {
-        logs.removeRange(0, pos);
-      }
-      filter = description;
-      return this;
-    } else {
-      LogEntryList newList = new LogEntryList(description);
-      while (pos < logs.length) {
-        newList.logs.add(logs[pos++]);
-      }
-      return newList;
-    }
-  }
-
-  /**
-   * Returns log events that happened after [when]. If [inPlace]
-   * is true, then it returns this LogEntryList after removing
-   * the entries that happened up to [when]; otherwise a new
-   * list is created.
-   */
-  LogEntryList after(DateTime when, [bool inPlace = false]) =>
-      _tail((e) => e.time.isAfter(when), inPlace, 'after $when', logs.length);
-
-  /**
-   * Returns log events that happened from [when] onwards. If
-   * [inPlace] is true, then it returns this LogEntryList after
-   * removing the entries that happened before [when]; otherwise
-   * a new list is created.
-   */
-  LogEntryList from(DateTime when, [bool inPlace = false]) =>
-      _tail((e) => !e.time.isBefore(when), inPlace, 'from $when', logs.length);
-
-  /**
-   * Returns log events that happened until [when]. If [inPlace]
-   * is true, then it returns this LogEntryList after removing
-   * the entries that happened after [when]; otherwise a new
-   * list is created.
-   */
-  LogEntryList until(DateTime when, [bool inPlace = false]) =>
-      _head((e) => e.time.isAfter(when), inPlace, 'until $when', logs.length);
-
-  /**
-   * Returns log events that happened before [when]. If [inPlace]
-   * is true, then it returns this LogEntryList after removing
-   * the entries that happened from [when] onwards; otherwise a new
-   * list is created.
-   */
-  LogEntryList before(DateTime when, [bool inPlace = false]) =>
-      _head((e) => !e.time.isBefore(when),
-            inPlace,
-            'before $when',
-            logs.length);
-
-  /**
-   * Returns log events that happened after [logEntry]'s time.
-   * If [inPlace] is true, then it returns this LogEntryList after
-   * removing the entries that happened up to [when]; otherwise a new
-   * list is created. If [logEntry] is null the current time is used.
-   */
-  LogEntryList afterEntry(LogEntry logEntry, [bool inPlace = false]) =>
-      after(logEntry == null ? new DateTime.now() : logEntry.time);
-
-  /**
-   * Returns log events that happened from [logEntry]'s time onwards.
-   * If [inPlace] is true, then it returns this LogEntryList after
-   * removing the entries that happened before [when]; otherwise
-   * a new list is created. If [logEntry] is null the current time is used.
-   */
-  LogEntryList fromEntry(LogEntry logEntry, [bool inPlace = false]) =>
-      from(logEntry == null ? new DateTime.now() : logEntry.time);
-
-  /**
-   * Returns log events that happened until [logEntry]'s time. If
-   * [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened after [when]; otherwise a new
-   * list is created. If [logEntry] is null the epoch time is used.
-   */
-  LogEntryList untilEntry(LogEntry logEntry, [bool inPlace = false]) =>
-      until(logEntry == null ?
-          new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
-
-  /**
-   * Returns log events that happened before [logEntry]'s time. If
-   * [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened from [when] onwards; otherwise a new
-   * list is created. If [logEntry] is null the epoch time is used.
-   */
-  LogEntryList beforeEntry(LogEntry logEntry, [bool inPlace = false]) =>
-      before(logEntry == null ?
-          new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
-
-  /**
-   * Returns log events that happened after the first event in [segment].
-   * If [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened earlier; otherwise a new list is created.
-   */
-  LogEntryList afterFirst(LogEntryList segment, [bool inPlace = false]) =>
-      afterEntry(segment.first, inPlace);
-
-  /**
-   * Returns log events that happened after the last event in [segment].
-   * If [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened earlier; otherwise a new list is created.
-   */
-  LogEntryList afterLast(LogEntryList segment, [bool inPlace = false]) =>
-      afterEntry(segment.last, inPlace);
-
-  /**
-   * Returns log events that happened from the time of the first event in
-   * [segment] onwards. If [inPlace] is true, then it returns this
-   * LogEntryList after removing the earlier entries; otherwise a new list
-   * is created.
-   */
-  LogEntryList fromFirst(LogEntryList segment, [bool inPlace = false]) =>
-      fromEntry(segment.first, inPlace);
-
-  /**
-   * Returns log events that happened from the time of the last event in
-   * [segment] onwards. If [inPlace] is true, then it returns this
-   * LogEntryList after removing the earlier entries; otherwise a new list
-   * is created.
-   */
-  LogEntryList fromLast(LogEntryList segment, [bool inPlace = false]) =>
-      fromEntry(segment.last, inPlace);
-
-  /**
-   * Returns log events that happened until the first event in [segment].
-   * If [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened later; otherwise a new list is created.
-   */
-  LogEntryList untilFirst(LogEntryList segment, [bool inPlace = false]) =>
-      untilEntry(segment.first, inPlace);
-
-  /**
-   * Returns log events that happened until the last event in [segment].
-   * If [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened later; otherwise a new list is created.
-   */
-  LogEntryList untilLast(LogEntryList segment, [bool inPlace = false]) =>
-      untilEntry(segment.last, inPlace);
-
-  /**
-   * Returns log events that happened before the first event in [segment].
-   * If [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened later; otherwise a new list is created.
-   */
-  LogEntryList beforeFirst(LogEntryList segment, [bool inPlace = false]) =>
-      beforeEntry(segment.first, inPlace);
-
-  /**
-   * Returns log events that happened before the last event in [segment].
-   * If [inPlace] is true, then it returns this LogEntryList after removing
-   * the entries that happened later; otherwise a new list is created.
-   */
-  LogEntryList beforeLast(LogEntryList segment, [bool inPlace = false]) =>
-      beforeEntry(segment.last, inPlace);
-
-  /**
-   * Iterate through the LogEntryList looking for matches to the entries
-   * in [keys]; for each match found the closest [distance] neighboring log
-   * entries that match [mockNameFilter] and [logFilter] will be included in
-   * the result. If [isPreceding] is true we use the neighbors that precede
-   * the matched entry; else we use the neighbors that followed.
-   * If [includeKeys] is true then the entries in [keys] that resulted in
-   * entries in the output list are themselves included in the output list. If
-   * [distance] is zero then all matches are included.
-   */
-  LogEntryList _neighboring(bool isPreceding,
-                            LogEntryList keys,
-                            mockNameFilter,
-                            logFilter,
-                            int distance,
-                            bool includeKeys) {
-    String filterName = 'Calls to '
-        '${_qualifiedName(mockNameFilter, logFilter.toString())} '
-        '${isPreceding?"preceding":"following"} ${keys.filter}';
-
-    LogEntryList rtn = new LogEntryList(filterName);
-
-    // Deal with the trivial case.
-    if (logs.length == 0 || keys.logs.length == 0) {
-      return rtn;
-    }
-
-    // Normalize the mockNameFilter and logFilter values.
-    if (mockNameFilter == null) {
-      mockNameFilter = anything;
-    } else {
-      mockNameFilter = wrapMatcher(mockNameFilter);
-    }
-    logFilter = _makePredicate(logFilter);
-
-    // The scratch list is used to hold matching entries when we
-    // are doing preceding neighbors. The remainingCount is used to
-    // keep track of how many matching entries we can still add in the
-    // current segment (0 if we are doing doing following neighbors, until
-    // we get our first key match).
-    List scratch = null;
-    int remainingCount = 0;
-    if (isPreceding) {
-      scratch = new List();
-      remainingCount = logs.length;
-    }
-
-    var keyIterator = keys.logs.iterator;
-    keyIterator.moveNext();
-    LogEntry keyEntry = keyIterator.current;
-    Map matchState = {};
-
-    for (LogEntry logEntry in logs) {
-      // If we have a log entry match, copy the saved matches from the
-      // scratch buffer into the return list, as well as the matching entry,
-      // if appropriate, and reset the scratch buffer. Continue processing
-      // from the next key entry.
-      if (keyEntry == logEntry) {
-        if (scratch != null) {
-          int numToCopy = scratch.length;
-          if (distance > 0 && distance < numToCopy) {
-            numToCopy = distance;
-          }
-          for (var i = scratch.length - numToCopy; i < scratch.length; i++) {
-            rtn.logs.add(scratch[i]);
-          }
-          scratch.clear();
-        } else {
-          remainingCount = distance > 0 ? distance : logs.length;
-        }
-        if (includeKeys) {
-          rtn.logs.add(keyEntry);
-        }
-        if (keyIterator.moveNext()) {
-          keyEntry = keyIterator.current;
-        } else if (isPreceding) { // We're done.
-          break;
-        }
-      } else if (remainingCount > 0 &&
-                 mockNameFilter.matches(logEntry.mockName, matchState) &&
-                 logFilter(logEntry)) {
-        if (scratch != null) {
-          scratch.add(logEntry);
-        } else {
-          rtn.logs.add(logEntry);
-          --remainingCount;
-        }
-      }
-    }
-    return rtn;
-  }
-
-  /**
-   * Iterate through the LogEntryList looking for matches to the entries
-   * in [keys]; for each match found the closest [distance] prior log entries
-   * that match [mocknameFilter] and [logFilter] will be included in the result.
-   * If [includeKeys] is true then the entries in [keys] that resulted in
-   * entries in the output list are themselves included in the output list. If
-   * [distance] is zero then all matches are included.
-   *
-   * The idea here is that you could find log entries that are related to
-   * other logs entries in some temporal sense. For example, say we have a
-   * method commit() that returns -1 on failure. Before commit() gets called
-   * the value being committed is created by process(). We may want to find
-   * the calls to process() that preceded calls to commit() that failed.
-   * We could do this with:
-   *
-   *      print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
-   *          logFilter: callsTo('process')).toString());
-   *
-   * We might want to include the details of the failing calls to commit()
-   * to see what parameters were passed in, in which case we would set
-   * [includeKeys].
-   *
-   * As another simple example, say we wanted to know the three method
-   * calls that immediately preceded each failing call to commit():
-   *
-   *     print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
-   *         distance: 3).toString());
-   */
-  LogEntryList preceding(LogEntryList keys,
-                         {mockNameFilter: null,
-                         logFilter: null,
-                         int distance: 1,
-                         bool includeKeys: false}) =>
-      _neighboring(true, keys, mockNameFilter, logFilter,
-          distance, includeKeys);
-
-  /**
-   * Iterate through the LogEntryList looking for matches to the entries
-   * in [keys]; for each match found the closest [distance] subsequent log
-   * entries that match [mocknameFilter] and [logFilter] will be included in
-   * the result. If [includeKeys] is true then the entries in [keys] that
-   * resulted in entries in the output list are themselves included in the
-   * output list. If [distance] is zero then all matches are included.
-   * See [preceding] for a usage example.
-   */
-  LogEntryList following(LogEntryList keys,
-                         {mockNameFilter: null,
-                         logFilter: null,
-                         int distance: 1,
-                         bool includeKeys: false}) =>
-      _neighboring(false, keys, mockNameFilter, logFilter,
-          distance, includeKeys);
-}
-
-/**
- * [_TimesMatcher]s are used to make assertions about the number of
- * times a method was called.
- */
-class _TimesMatcher extends Matcher {
-  final int min, max;
-
-  const _TimesMatcher(this.min, [this.max = -1]);
-
-  bool matches(logList, Map matchState) => logList.length >= min &&
-      (max < 0 || logList.length <= max);
-
-  Description describe(Description description) {
-    description.add('to be called ');
-    if (max < 0) {
-      description.add('at least $min');
-    } else if (max == min) {
-      description.add('$max');
-    } else if (min == 0) {
-      description.add('at most $max');
-    } else {
-      description.add('between $min and $max');
-    }
-    return description.add(' times');
-  }
-
-  Description describeMismatch(logList, Description mismatchDescription,
-                               Map matchState, bool verbose) =>
-      mismatchDescription.add('was called ${logList.length} times');
-}
-
-/** [happenedExactly] matches an exact number of calls. */
-Matcher happenedExactly(count) {
-  return new _TimesMatcher(count, count);
-}
-
-/** [happenedAtLeast] matches a minimum number of calls. */
-Matcher happenedAtLeast(count) {
-  return new _TimesMatcher(count);
-}
-
-/** [happenedAtMost] matches a maximum number of calls. */
-Matcher happenedAtMost(count) {
-  return new _TimesMatcher(0, count);
-}
-
-/** [neverHappened] matches zero calls. */
-const Matcher neverHappened = const _TimesMatcher(0, 0);
-
-/** [happenedOnce] matches exactly one call. */
-const Matcher happenedOnce = const _TimesMatcher(1, 1);
-
-/** [happenedAtLeastOnce] matches one or more calls. */
-const Matcher happenedAtLeastOnce = const _TimesMatcher(1);
-
-/** [happenedAtMostOnce] matches zero or one call. */
-const Matcher happenedAtMostOnce = const _TimesMatcher(0, 1);
-
-/**
- * [_ResultMatcher]s are used to make assertions about the results
- * of method calls. These can be used as optional parameters to [getLogs].
- */
-class _ResultMatcher extends Matcher {
-  final Action action;
-  final Matcher value;
-
-  const _ResultMatcher(this.action, this.value);
-
-  bool matches(item, Map matchState) {
-    if (item is! LogEntry) {
-     return false;
-    }
-    // normalize the action; _PROXY is like _RETURN.
-    Action eaction = item.action;
-    if (eaction == Action.PROXY) {
-      eaction = Action.RETURN;
-    }
-    return (eaction == action && value.matches(item.value, matchState));
-  }
-
-  Description describe(Description description) {
-    description.add(' to ');
-    if (action == Action.RETURN || action == Action.PROXY)
-      description.add('return ');
-    else
-      description.add('throw ');
-    return description.addDescriptionOf(value);
-  }
-
-  Description describeMismatch(item, Description mismatchDescription,
-                               Map matchState, bool verbose) {
-    if (item.action == Action.RETURN || item.action == Action.PROXY) {
-      mismatchDescription.add('returned ');
-    } else {
-      mismatchDescription.add('threw ');
-    }
-    mismatchDescription.add(item.value);
-    return mismatchDescription;
-  }
-}
-
-/**
- *[returning] matches log entries where the call to a method returned
- * a value that matched [value].
- */
-Matcher returning(value) =>
-    new _ResultMatcher(Action.RETURN, wrapMatcher(value));
-
-/**
- *[throwing] matches log entrues where the call to a method threw
- * a value that matched [value].
- */
-Matcher throwing(value) =>
-    new _ResultMatcher(Action.THROW, wrapMatcher(value));
-
-/** Special values for use with [_ResultSetMatcher] [frequency]. */
-class _Frequency {
-  /** Every call/throw must match */
-  static const ALL = const _Frequency._('ALL');
-
-  /** At least one call/throw must match. */
-  static const SOME = const _Frequency._('SOME');
-
-  /** No calls/throws should match. */
-  static const NONE = const _Frequency._('NONE');
-
-  const _Frequency._(this.name);
-
-  final String name;
-}
-
-/**
- * [_ResultSetMatcher]s are used to make assertions about the results
- * of method calls. When filtering an execution log by calling
- * [getLogs], a [LogEntrySet] of matching call logs is returned;
- * [_ResultSetMatcher]s can then assert various things about this
- * (sub)set of logs.
- *
- * We could make this class use _ResultMatcher but it doesn't buy that
- * match and adds some perf hit, so there is some duplication here.
- */
-class _ResultSetMatcher extends Matcher {
-  final Action action;
-  final Matcher value;
-  final _Frequency frequency; // ALL, SOME, or NONE.
-
-  const _ResultSetMatcher(this.action, this.value, this.frequency);
-
-  bool matches(logList, Map matchState) {
-    for (LogEntry entry in logList) {
-      // normalize the action; PROXY is like RETURN.
-      Action eaction = entry.action;
-      if (eaction == Action.PROXY) {
-        eaction = Action.RETURN;
-      }
-      if (eaction == action && value.matches(entry.value, matchState)) {
-        if (frequency == _Frequency.NONE) {
-          addStateInfo(matchState, {'entry': entry});
-          return false;
-        } else if (frequency == _Frequency.SOME) {
-          return true;
-        }
-      } else {
-        // Mismatch.
-        if (frequency == _Frequency.ALL) { // We need just one mismatch to fail.
-          addStateInfo(matchState, {'entry': entry});
-          return false;
-        }
-      }
-    }
-    // If we get here, then if count is _ALL we got all matches and
-    // this is success; otherwise we got all mismatched which is
-    // success for count == _NONE and failure for count == _SOME.
-    return (frequency != _Frequency.SOME);
-  }
-
-  Description describe(Description description) {
-    description.add(' to ');
-    description.add(frequency == _Frequency.ALL ? 'alway ' :
-        (frequency == _Frequency.NONE ? 'never ' : 'sometimes '));
-    if (action == Action.RETURN || action == Action.PROXY)
-      description.add('return ');
-    else
-      description.add('throw ');
-    return description.addDescriptionOf(value);
-  }
-
-  Description describeMismatch(logList, Description mismatchDescription,
-                               Map matchState, bool verbose) {
-    if (frequency != _Frequency.SOME) {
-      LogEntry entry = matchState['entry'];
-      if (entry.action == Action.RETURN || entry.action == Action.PROXY) {
-        mismatchDescription.add('returned');
-      } else {
-        mismatchDescription.add('threw');
-      }
-      mismatchDescription.add(' value that ');
-      value.describeMismatch(entry.value, mismatchDescription,
-        matchState['state'], verbose);
-      mismatchDescription.add(' at least once');
-    } else {
-      mismatchDescription.add('never did');
-    }
-    return mismatchDescription;
-  }
-}
-
-/**
- *[alwaysReturned] asserts that all matching calls to a method returned
- * a value that matched [value].
- */
-Matcher alwaysReturned(value) =>
-    new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.ALL);
-
-/**
- *[sometimeReturned] asserts that at least one matching call to a method
- * returned a value that matched [value].
- */
-Matcher sometimeReturned(value) =>
-    new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.SOME);
-
-/**
- *[neverReturned] asserts that no matching calls to a method returned
- * a value that matched [value].
- */
-Matcher neverReturned(value) =>
-    new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.NONE);
-
-/**
- *[alwaysThrew] asserts that all matching calls to a method threw
- * a value that matched [value].
- */
-Matcher alwaysThrew(value) =>
-    new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.ALL);
-
-/**
- *[sometimeThrew] asserts that at least one matching call to a method threw
- * a value that matched [value].
- */
-Matcher sometimeThrew(value) =>
-  new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.SOME);
-
-/**
- *[neverThrew] asserts that no matching call to a method threw
- * a value that matched [value].
- */
-Matcher neverThrew(value) =>
-  new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE);
-
-/** The shared log used for named mocks. */
+@deprecated
 LogEntryList sharedLog = null;
-
-/** The base class for all mocked objects. */
-@proxy
-class Mock {
-  /** The mock name. Needed if the log is shared; optional otherwise. */
-  final String name;
-
-  /** The set of [Behavior]s supported. */
-  final LinkedHashMap<String,Behavior> _behaviors;
-
-  /** How to handle unknown method calls - swallow or throw. */
-  final bool _throwIfNoBehavior;
-
-  /** For spys, the real object that we are spying on. */
-  final Object _realObject;
-
-  /** The [log] of calls made. Only used if [name] is null. */
-  LogEntryList log;
-
-  /** Whether to create an audit log or not. */
-  bool _logging;
-
-  bool get logging => _logging;
-  set logging(bool value) {
-    if (value && log == null) {
-      log = new LogEntryList();
-    }
-    _logging = value;
-  }
-
-  /**
-   * Default constructor. Unknown method calls are allowed and logged,
-   * the mock has no name, and has its own log.
-   */
-  Mock() :
-    _throwIfNoBehavior = false, log = null, name = null, _realObject = null,
-    _behaviors = new LinkedHashMap<String,Behavior>() {
-    logging = true;
-  }
-
-  /**
-   * This constructor makes a mock that has a [name] and possibly uses
-   * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods
-   * that have no defined behaviors will throw an exception; otherwise they
-   * will be allowed and logged (but will not do anything).
-   * If [enableLogging] is false, no logging will be done initially (whether
-   * or not a [log] is supplied), but [logging] can be set to true later.
-   */
-  Mock.custom({this.name,
-               this.log,
-               throwIfNoBehavior: false,
-               enableLogging: true})
-      : _throwIfNoBehavior = throwIfNoBehavior, _realObject = null,
-        _behaviors = new LinkedHashMap<String,Behavior>() {
-    if (log != null && name == null) {
-      throw new Exception("Mocks with shared logs must have a name.");
-    }
-    logging = enableLogging;
-  }
-
-  /**
-   * This constructor creates a spy with no user-defined behavior.
-   * This is simply a proxy for a real object that passes calls
-   * through to that real object but captures an audit trail of
-   * calls made to the object that can be queried and validated
-   * later.
-   */
-  Mock.spy(this._realObject, {this.name, this.log})
-    : _behaviors = null,
-     _throwIfNoBehavior = true {
-    logging = true;
-  }
-
-  /**
-   * [when] is used to create a new or extend an existing [Behavior].
-   * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for
-   * that signature are returned (being created first if needed).
-   *
-   * Typical use case:
-   *
-   *     mock.when(callsTo(...)).alwaysReturn(...);
-   */
-  Behavior when(CallMatcher logFilter) {
-    String key = logFilter.toString();
-    if (!_behaviors.containsKey(key)) {
-      Behavior b = new Behavior(logFilter);
-      _behaviors[key] = b;
-      return b;
-    } else {
-      return _behaviors[key];
-    }
-  }
-
-  /**
-   * This is the handler for method calls. We loop through the list
-   * of [Behavior]s, and find the first match that still has return
-   * values available, and then do the action specified by that
-   * return value. If we find no [Behavior] to apply an exception is
-   * thrown.
-   */
-  noSuchMethod(Invocation invocation) {
-    var method = MirrorSystem.getName(invocation.memberName);
-    var args = invocation.positionalArguments;
-    if (invocation.isGetter) {
-      method = 'get $method';
-    } else if (invocation.isSetter) {
-      method = 'set $method';
-      // Remove the trailing '='.
-      if (method[method.length-1] == '=') {
-        method = method.substring(0, method.length - 1);
-      }
-    }
-    if (_behaviors == null) { // Spy.
-      var mirror = reflect(_realObject);
-      try {
-        var result = mirror.delegate(invocation);
-        log.add(new LogEntry(name, method, args, Action.PROXY, result));
-        return result;
-      } catch (e) {
-        log.add(new LogEntry(name, method, args, Action.THROW, e));
-        throw e;
-      }
-    }
-    bool matchedMethodName = false;
-    Map matchState = {};
-    for (String k in _behaviors.keys) {
-      Behavior b = _behaviors[k];
-      if (b.matcher.nameFilter.matches(method, matchState)) {
-        matchedMethodName = true;
-      }
-      if (b.matches(method, args)) {
-        List actions = b.actions;
-        if (actions == null || actions.length == 0) {
-          continue; // No return values left in this Behavior.
-        }
-        // Get the first response.
-        Responder response = actions[0];
-        // If it is exhausted, remove it from the list.
-        // Note that for endlessly repeating values, we started the count at
-        // 0, so we get a potentially useful value here, which is the
-        // (negation of) the number of times we returned the value.
-        if (--response.count == 0) {
-          actions.removeRange(0, 1);
-        }
-        // Do the response.
-        Action action = response.action;
-        var value = response.value;
-        if (action == Action.RETURN) {
-          if (_logging && b.logging) {
-            log.add(new LogEntry(name, method, args, action, value));
-          }
-          return value;
-        } else if (action == Action.THROW) {
-          if (_logging && b.logging) {
-            log.add(new LogEntry(name, method, args, action, value));
-          }
-          throw value;
-        } else if (action == Action.PROXY) {
-          // TODO(gram): Replace all this with:
-          //     var rtn = reflect(value).apply(invocation.positionalArguments,
-          //         invocation.namedArguments);
-          // once that is supported.
-          var rtn;
-          switch (args.length) {
-            case 0:
-              rtn = value();
-              break;
-            case 1:
-              rtn = value(args[0]);
-              break;
-            case 2:
-              rtn = value(args[0], args[1]);
-              break;
-            case 3:
-              rtn = value(args[0], args[1], args[2]);
-              break;
-            case 4:
-              rtn = value(args[0], args[1], args[2], args[3]);
-              break;
-            case 5:
-              rtn = value(args[0], args[1], args[2], args[3], args[4]);
-              break;
-            case 6:
-              rtn = value(args[0], args[1], args[2], args[3],
-                  args[4], args[5]);
-              break;
-            case 7:
-              rtn = value(args[0], args[1], args[2], args[3],
-                  args[4], args[5], args[6]);
-              break;
-            case 8:
-              rtn = value(args[0], args[1], args[2], args[3],
-                  args[4], args[5], args[6], args[7]);
-              break;
-            case 9:
-              rtn = value(args[0], args[1], args[2], args[3],
-                  args[4], args[5], args[6], args[7], args[8]);
-              break;
-            case 9:
-              rtn = value(args[0], args[1], args[2], args[3],
-                  args[4], args[5], args[6], args[7], args[8], args[9]);
-              break;
-            default:
-              throw new Exception(
-                  "Cannot proxy calls with more than 10 parameters.");
-          }
-          if (_logging && b.logging) {
-            log.add(new LogEntry(name, method, args, action, rtn));
-          }
-          return rtn;
-        }
-      }
-    }
-    if (matchedMethodName) {
-      // User did specify behavior for this method, but all the
-      // actions are exhausted. This is considered an error.
-      throw new Exception('No more actions for method '
-          '${_qualifiedName(name, method)}.');
-    } else if (_throwIfNoBehavior) {
-      throw new Exception('No behavior specified for method '
-          '${_qualifiedName(name, method)}.');
-    }
-    // Otherwise user hasn't specified behavior for this method; we don't throw
-    // so we can underspecify.
-    if (_logging) {
-      log.add(new LogEntry(name, method, args, Action.IGNORE));
-    }
-  }
-
-  /** [verifyZeroInteractions] returns true if no calls were made */
-  bool verifyZeroInteractions() {
-    if (log == null) {
-      // This means we created the mock with logging off and have never turned
-      // it on, so it doesn't make sense to verify behavior on such a mock.
-      throw new
-          Exception("Can't verify behavior when logging was never enabled.");
-    }
-    return log.logs.length == 0;
-  }
-
-  /**
-   * [getLogs] extracts all calls from the call log that match the
-   * [logFilter], and returns the matching list of [LogEntry]s. If
-   * [destructive] is false (the default) the matching calls are left
-   * in the log, else they are removed. Removal allows us to verify a
-   * set of interactions and then verify that there are no other
-   * interactions left. [actionMatcher] can be used to further
-   * restrict the returned logs based on the action the mock performed.
-   * [logFilter] can be a [CallMatcher] or a predicate function that
-   * takes a [LogEntry] and returns a bool.
-   *
-   * Typical usage:
-   *
-   *     getLogs(callsTo(...)).verify(...);
-   */
-  LogEntryList getLogs([CallMatcher logFilter,
-                        Matcher actionMatcher,
-                        bool destructive = false]) {
-    if (log == null) {
-      // This means we created the mock with logging off and have never turned
-      // it on, so it doesn't make sense to get logs from such a mock.
-      throw new
-          Exception("Can't retrieve logs when logging was never enabled.");
-    } else {
-      return log.getMatches(name, logFilter, actionMatcher, destructive);
-    }
-  }
-
-  /**
-   * Useful shorthand method that creates a [CallMatcher] from its arguments
-   * and then calls [getLogs].
-   */
-  LogEntryList calls(method,
-                      [arg0 = _noArg,
-                       arg1 = _noArg,
-                       arg2 = _noArg,
-                       arg3 = _noArg,
-                       arg4 = _noArg,
-                       arg5 = _noArg,
-                       arg6 = _noArg,
-                       arg7 = _noArg,
-                       arg8 = _noArg,
-                       arg9 = _noArg]) =>
-      getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4,
-          arg5, arg6, arg7, arg8, arg9));
-
-  /** Clear the behaviors for the Mock. */
-  void resetBehavior() => _behaviors.clear();
-
-  /** Clear the logs for the Mock. */
-  void clearLogs() {
-    if (log != null) {
-      if (name == null) { // This log is not shared.
-        log.logs.clear();
-      } else { // This log may be shared.
-        log.logs = log.logs.where((e) => e.mockName != name).toList();
-      }
-    }
-  }
-
-  /** Clear both logs and behavior. */
-  void reset() {
-    resetBehavior();
-    clearLogs();
-  }
-}
diff --git a/pkg/mock/lib/src/action.dart b/pkg/mock/lib/src/action.dart
new file mode 100644
index 0000000..1712b2c
--- /dev/null
+++ b/pkg/mock/lib/src/action.dart
@@ -0,0 +1,26 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mock.action;
+
+/** The ways in which a call to a mock method can be handled. */
+class Action {
+  /** Do nothing (void method) */
+  static const IGNORE = const Action._('IGNORE');
+
+  /** Return a supplied value. */
+  static const RETURN = const Action._('RETURN');
+
+  /** Throw a supplied value. */
+  static const THROW = const Action._('THROW');
+
+  /** Call a supplied function. */
+  static const PROXY = const Action._('PROXY');
+
+  const Action._(this.name);
+
+  final String name;
+
+  String toString() => 'Action: $name';
+}
diff --git a/pkg/mock/lib/src/behavior.dart b/pkg/mock/lib/src/behavior.dart
new file mode 100644
index 0000000..b43f699
--- /dev/null
+++ b/pkg/mock/lib/src/behavior.dart
@@ -0,0 +1,83 @@
+// 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 mock.behavior;
+
+import 'action.dart';
+import 'call_matcher.dart';
+import 'responder.dart';
+
+/**
+ * A [Behavior] represents how a [Mock] will respond to one particular
+ * type of method call.
+ */
+class Behavior {
+  CallMatcher matcher; // The method call matcher.
+  List<Responder> actions; // The values to return/throw or proxies to call.
+  bool logging = true;
+
+  Behavior (this.matcher) {
+    actions = new List<Responder>();
+  }
+
+  /**
+   * Adds a [Responder] that returns a [value] for [count] calls
+   * (1 by default).
+   */
+  Behavior thenReturn(value, [count = 1]) {
+    actions.add(new Responder(value, count, Action.RETURN));
+    return this; // For chaining calls.
+  }
+
+  /** Adds a [Responder] that repeatedly returns a [value]. */
+  Behavior alwaysReturn(value) {
+    return thenReturn(value, 0);
+  }
+
+  /**
+   * Adds a [Responder] that throws [value] [count]
+   * times (1 by default).
+   */
+  Behavior thenThrow(value, [count = 1]) {
+    actions.add(new Responder(value, count, Action.THROW));
+    return this; // For chaining calls.
+  }
+
+  /** Adds a [Responder] that throws [value] endlessly. */
+  Behavior alwaysThrow(value) {
+    return thenThrow(value, 0);
+  }
+
+  /**
+   * [thenCall] creates a proxy Responder, that is called [count]
+   * times (1 by default; 0 is used for unlimited calls, and is
+   * exposed as [alwaysCall]). [value] is the function that will
+   * be called with the same arguments that were passed to the
+   * mock. Proxies can be used to wrap real objects or to define
+   * more complex return/throw behavior. You could even (if you
+   * wanted) use proxies to emulate the behavior of thenReturn;
+   * e.g.:
+   *
+   *     m.when(callsTo('foo')).thenReturn(0)
+   *
+   * is equivalent to:
+   *
+   *     m.when(callsTo('foo')).thenCall(() => 0)
+   */
+  Behavior thenCall(value, [count = 1]) {
+    actions.add(new Responder(value, count, Action.PROXY));
+    return this; // For chaining calls.
+  }
+
+  /** Creates a repeating proxy call. */
+  Behavior alwaysCall(value) {
+    return thenCall(value, 0);
+  }
+
+  /** Returns true if a method call matches the [Behavior]. */
+  bool matches(String method, List args) => matcher.matches(method, args);
+
+  /** Returns the [matcher]'s representation. */
+  String toString() => matcher.toString();
+}
diff --git a/pkg/mock/lib/src/call_matcher.dart b/pkg/mock/lib/src/call_matcher.dart
new file mode 100644
index 0000000..ff6e5e5
--- /dev/null
+++ b/pkg/mock/lib/src/call_matcher.dart
@@ -0,0 +1,134 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mock.call_matcher;
+
+import 'package:matcher/matcher.dart';
+
+import 'util.dart';
+
+/**
+ * A [CallMatcher] is a special matcher used to match method calls (i.e.
+ * a method name and set of arguments). It is not a [Matcher] like the
+ * unit test [Matcher], but instead represents a method name and a
+ * collection of [Matcher]s, one per argument, that will be applied
+ * to the parameters to decide if the method call is a match.
+ */
+class CallMatcher {
+  Matcher nameFilter;
+  List<Matcher> argMatchers;
+
+  /**
+   * Constructor for [CallMatcher]. [name] can be null to
+   * match anything, or a literal [String], a predicate [Function],
+   * or a [Matcher]. The various arguments can be scalar values or
+   * [Matcher]s.
+   */
+  CallMatcher([name,
+              arg0 = NO_ARG,
+              arg1 = NO_ARG,
+              arg2 = NO_ARG,
+              arg3 = NO_ARG,
+              arg4 = NO_ARG,
+              arg5 = NO_ARG,
+              arg6 = NO_ARG,
+              arg7 = NO_ARG,
+              arg8 = NO_ARG,
+              arg9 = NO_ARG]) {
+    if (name == null) {
+      nameFilter = anything;
+    } else {
+      nameFilter = wrapMatcher(name);
+    }
+    argMatchers = new List<Matcher>();
+    if (identical(arg0, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg0));
+    if (identical(arg1, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg1));
+    if (identical(arg2, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg2));
+    if (identical(arg3, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg3));
+    if (identical(arg4, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg4));
+    if (identical(arg5, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg5));
+    if (identical(arg6, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg6));
+    if (identical(arg7, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg7));
+    if (identical(arg8, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg8));
+    if (identical(arg9, NO_ARG)) return;
+    argMatchers.add(wrapMatcher(arg9));
+  }
+
+  /**
+   * We keep our behavior specifications in a Map, which is keyed
+   * by the [CallMatcher]. To make the keys unique and to get a
+   * descriptive value for the [CallMatcher] we have this override
+   * of [toString()].
+   */
+  String toString() {
+    Description d = new StringDescription();
+    d.addDescriptionOf(nameFilter);
+    // If the nameFilter was a simple string - i.e. just a method name -
+    // strip the quotes to make this more natural in appearance.
+    if (d.toString()[0] == "'") {
+      d.replace(d.toString().substring(1, d.toString().length - 1));
+    }
+    d.add('(');
+    for (var i = 0; i < argMatchers.length; i++) {
+      if (i > 0) d.add(', ');
+      d.addDescriptionOf(argMatchers[i]);
+    }
+    d.add(')');
+    return d.toString();
+  }
+
+  /**
+   * Given a [method] name and list of [arguments], return true
+   * if it matches this [CallMatcher.
+   */
+  bool matches(String method, List arguments) {
+    var matchState = {};
+    if (!nameFilter.matches(method, matchState)) {
+      return false;
+    }
+    var numArgs = (arguments == null) ? 0 : arguments.length;
+    if (numArgs < argMatchers.length) {
+      throw new Exception("Less arguments than matchers for $method.");
+    }
+    for (var i = 0; i < argMatchers.length; i++) {
+      if (!argMatchers[i].matches(arguments[i], matchState)) {
+        return false;
+      }
+    }
+    return true;
+  }
+}
+
+/**
+ * Returns a [CallMatcher] for the specified signature. [method] can be
+ * null to match anything, or a literal [String], a predicate [Function],
+ * or a [Matcher]. The various arguments can be scalar values or [Matcher]s.
+ * To match getters and setters, use "get " and "set " prefixes on the names.
+ * For example, for a property "foo", you could use "get foo" and "set foo"
+ * as literal string arguments to callsTo to match the getter and setter
+ * of "foo".
+ */
+CallMatcher callsTo([method,
+                     arg0 = NO_ARG,
+                     arg1 = NO_ARG,
+                     arg2 = NO_ARG,
+                     arg3 = NO_ARG,
+                     arg4 = NO_ARG,
+                     arg5 = NO_ARG,
+                     arg6 = NO_ARG,
+                     arg7 = NO_ARG,
+                     arg8 = NO_ARG,
+                     arg9 = NO_ARG]) {
+  return new CallMatcher(method, arg0, arg1, arg2, arg3, arg4,
+      arg5, arg6, arg7, arg8, arg9);
+}
diff --git a/pkg/mock/lib/src/log_entry.dart b/pkg/mock/lib/src/log_entry.dart
new file mode 100644
index 0000000..85f9061
--- /dev/null
+++ b/pkg/mock/lib/src/log_entry.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mock.log_entry;
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'util.dart';
+
+/**
+ * Every call to a [Mock] object method is logged. The logs are
+ * kept in instances of [LogEntry].
+ */
+class LogEntry {
+  /** The time of the event. */
+  DateTime time;
+
+  /** The mock object name, if any. */
+  final String mockName;
+
+  /** The method name. */
+  final String methodName;
+
+  /** The parameters. */
+  final List args;
+
+  /** The behavior that resulted. */
+  final Action action;
+
+  /** The value that was returned (if no throw). */
+  final value;
+
+  LogEntry(this.mockName, this.methodName,
+      this.args, this.action, [this.value]) {
+    time = new DateTime.now();
+  }
+
+  String _pad2(int val) => (val >= 10 ? '$val' : '0$val');
+
+  String toString([DateTime baseTime]) {
+    Description d = new StringDescription();
+    if (baseTime == null) {
+      // Show absolute time.
+      d.add('${time.hour}:${_pad2(time.minute)}:'
+          '${_pad2(time.second)}.${time.millisecond}>  ');
+    } else {
+      // Show relative time.
+      int delta = time.millisecondsSinceEpoch - baseTime.millisecondsSinceEpoch;
+      int secs = delta ~/ 1000;
+      int msecs = delta % 1000;
+      d.add('$secs.$msecs>  ');
+    }
+    d.add('${qualifiedName(mockName, methodName)}(');
+    if (args != null) {
+      for (var i = 0; i < args.length; i++) {
+        if (i != 0) d.add(', ');
+        d.addDescriptionOf(args[i]);
+      }
+    }
+    d.add(') ${action == Action.THROW ? "threw" : "returned"} ');
+    d.addDescriptionOf(value);
+    return d.toString();
+  }
+}
diff --git a/pkg/mock/lib/src/log_entry_list.dart b/pkg/mock/lib/src/log_entry_list.dart
new file mode 100644
index 0000000..6c612ba
--- /dev/null
+++ b/pkg/mock/lib/src/log_entry_list.dart
@@ -0,0 +1,564 @@
+// 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 mock.log_entry_list;
+
+import 'package:matcher/matcher.dart';
+
+import 'call_matcher.dart';
+import 'log_entry.dart';
+import 'util.dart';
+
+/**
+* [StepValidator]s are used by [stepwiseValidate] in [LogEntryList], which
+* iterates through the list and call the [StepValidator] function with the
+* log [List] and position. The [StepValidator] should return the number of
+* positions to advance upon success, or zero upon failure. When zero is
+* returned an error is reported.
+*/
+typedef int StepValidator(List<LogEntry> logs, int pos);
+
+/**
+ * We do verification on a list of [LogEntry]s. To allow chaining
+ * of calls to verify, we encapsulate such a list in the [LogEntryList]
+ * class.
+ */
+class LogEntryList {
+  String filter;
+  List<LogEntry> logs;
+  LogEntryList([this.filter]) {
+    logs = new List<LogEntry>();
+  }
+
+  /** Add a [LogEntry] to the log. */
+  add(LogEntry entry) => logs.add(entry);
+
+  /** Get the first entry, or null if no entries. */
+  get first => (logs == null || logs.length == 0) ? null : logs[0];
+
+  /** Get the last entry, or null if no entries. */
+  get last => (logs == null || logs.length == 0) ? null : logs.last;
+
+  /** Creates a LogEntry predicate function from the argument. */
+  Function _makePredicate(arg) {
+    if (arg == null) {
+      return (e) => true;
+    } else if (arg is CallMatcher) {
+      return (e) => arg.matches(e.methodName, e.args);
+    } else if (arg is Function) {
+      return arg;
+    } else {
+      throw new Exception("Invalid argument to _makePredicate.");
+    }
+  }
+
+  /**
+   * Create a new [LogEntryList] consisting of [LogEntry]s from
+   * this list that match the specified [mockNameFilter] and [logFilter].
+   * [mockNameFilter] can be null, a [String], a predicate [Function],
+   * or a [Matcher]. If [mockNameFilter] is null, this is the same as
+   * [anything].
+   * If [logFilter] is null, all entries in the log will be returned.
+   * Otherwise [logFilter] should be a [CallMatcher] or  predicate function
+   * that takes a [LogEntry] and returns a bool.
+   * If [destructive] is true, the log entries are removed from the
+   * original list.
+   */
+  LogEntryList getMatches([mockNameFilter,
+                          logFilter,
+                          Matcher actionMatcher,
+                          bool destructive = false]) {
+    if (mockNameFilter == null) {
+      mockNameFilter = anything;
+    } else {
+      mockNameFilter = wrapMatcher(mockNameFilter);
+    }
+    Function entryFilter = _makePredicate(logFilter);
+    String filterName = qualifiedName(mockNameFilter, logFilter.toString());
+    LogEntryList rtn = new LogEntryList(filterName);
+    var matchState = {};
+    for (var i = 0; i < logs.length; i++) {
+      LogEntry entry = logs[i];
+      if (mockNameFilter.matches(entry.mockName, matchState) &&
+          entryFilter(entry)) {
+        if (actionMatcher == null ||
+            actionMatcher.matches(entry, matchState)) {
+          rtn.add(entry);
+          if (destructive) {
+            int startIndex = i--;
+            logs.removeRange(startIndex, startIndex + 1);
+          }
+        }
+      }
+    }
+    return rtn;
+  }
+
+  /** Apply a unit test [Matcher] to the [LogEntryList]. */
+  LogEntryList verify(Matcher matcher) {
+    if (_mockFailureHandler == null) {
+      _mockFailureHandler =
+          new _MockFailureHandler(getOrCreateExpectFailureHandler());
+    }
+    expect(logs, matcher, reason:filter, failureHandler: _mockFailureHandler);
+    return this;
+  }
+
+  /**
+   * Iterate through the list and call the [validator] function with the
+   * log [List] and position. The [validator] should return the number of
+   * positions to advance upon success, or zero upon failure. When zero is
+   * returned an error is reported. [reason] can be used to provide a
+   * more descriptive failure message. If a failure occurred false will be
+   * returned (unless the failure handler itself threw an exception);
+   * otherwise true is returned.
+   * The use case here is to perform more complex validations; for example
+   * we may want to assert that the return value from some function is
+   * later used as a parameter to a following function. If we filter the logs
+   * to include just these two functions we can write a simple validator to
+   * do this check.
+   */
+  bool stepwiseValidate(StepValidator validator, [String reason = '']) {
+    if (_mockFailureHandler == null) {
+      _mockFailureHandler =
+          new _MockFailureHandler(getOrCreateExpectFailureHandler());
+    }
+    var i = 0;
+    while (i < logs.length) {
+      var n = validator(logs, i);
+      if (n == 0) {
+        if (reason.length > 0) {
+          reason = ': $reason';
+        }
+        _mockFailureHandler.fail("Stepwise validation failed at $filter "
+                                 "position $i$reason");
+        return false;
+      } else {
+        i += n;
+      }
+    }
+    return true;
+  }
+
+  /**
+   * Turn the logs into human-readable text. If [baseTime] is specified
+   * then each entry is prefixed with the offset from that time in
+   * milliseconds; otherwise the time of day is used.
+   */
+  String toString([DateTime baseTime]) {
+    String s = '';
+    for (var e in logs) {
+      s = '$s${e.toString(baseTime)}\n';
+    }
+    return s;
+  }
+
+  /**
+   *  Find the first log entry that satisfies [logFilter] and
+   *  return its position. A search [start] position can be provided
+   *  to allow for repeated searches. [logFilter] can be a [CallMatcher],
+   *  or a predicate function that takes a [LogEntry] argument and returns
+   *  a bool. If [logFilter] is null, it will match any [LogEntry].
+   *  If no entry is found, then [failureReturnValue] is returned.
+   *  After each check the position is updated by [skip], so using
+   *  [skip] of -1 allows backward searches, using a [skip] of 2 can
+   *  be used to check pairs of adjacent entries, and so on.
+   */
+  int findLogEntry(logFilter, [int start = 0, int failureReturnValue = -1,
+      skip = 1]) {
+    logFilter = _makePredicate(logFilter);
+    int pos = start;
+    while (pos >= 0 && pos < logs.length) {
+      if (logFilter(logs[pos])) {
+        return pos;
+      }
+      pos += skip;
+    }
+    return failureReturnValue;
+  }
+
+  /**
+   * Returns log events that happened up to the first one that
+   * satisfies [logFilter]. If [inPlace] is true, then returns
+   * this LogEntryList after removing the from the first satisfier;
+   * onwards otherwise a new list is created. [description]
+   * is used to create a new name for the resulting list.
+   * [defaultPosition] is used as the index of the matching item in
+   * the case that no match is found.
+   */
+  LogEntryList _head(logFilter, bool inPlace,
+                     String description, int defaultPosition) {
+    if (filter != null) {
+      description = '$filter $description';
+    }
+    int pos = findLogEntry(logFilter, 0, defaultPosition);
+    if (inPlace) {
+      if (pos < logs.length) {
+        logs.removeRange(pos, logs.length);
+      }
+      filter = description;
+      return this;
+    } else {
+      LogEntryList newList = new LogEntryList(description);
+      for (var i = 0; i < pos; i++) {
+        newList.logs.add(logs[i]);
+      }
+      return newList;
+    }
+  }
+
+  /**
+   * Returns log events that happened from the first one that
+   * satisfies [logFilter]. If [inPlace] is true, then returns
+   * this LogEntryList after removing the entries up to the first
+   * satisfier; otherwise a new list is created. [description]
+   * is used to create a new name for the resulting list.
+   * [defaultPosition] is used as the index of the matching item in
+   * the case that no match is found.
+   */
+  LogEntryList _tail(logFilter, bool inPlace,
+                     String description, int defaultPosition) {
+    if (filter != null) {
+      description = '$filter $description';
+    }
+    int pos = findLogEntry(logFilter, 0, defaultPosition);
+    if (inPlace) {
+      if (pos > 0) {
+        logs.removeRange(0, pos);
+      }
+      filter = description;
+      return this;
+    } else {
+      LogEntryList newList = new LogEntryList(description);
+      while (pos < logs.length) {
+        newList.logs.add(logs[pos++]);
+      }
+      return newList;
+    }
+  }
+
+  /**
+   * Returns log events that happened after [when]. If [inPlace]
+   * is true, then it returns this LogEntryList after removing
+   * the entries that happened up to [when]; otherwise a new
+   * list is created.
+   */
+  LogEntryList after(DateTime when, [bool inPlace = false]) =>
+      _tail((e) => e.time.isAfter(when), inPlace, 'after $when', logs.length);
+
+  /**
+   * Returns log events that happened from [when] onwards. If
+   * [inPlace] is true, then it returns this LogEntryList after
+   * removing the entries that happened before [when]; otherwise
+   * a new list is created.
+   */
+  LogEntryList from(DateTime when, [bool inPlace = false]) =>
+      _tail((e) => !e.time.isBefore(when), inPlace, 'from $when', logs.length);
+
+  /**
+   * Returns log events that happened until [when]. If [inPlace]
+   * is true, then it returns this LogEntryList after removing
+   * the entries that happened after [when]; otherwise a new
+   * list is created.
+   */
+  LogEntryList until(DateTime when, [bool inPlace = false]) =>
+      _head((e) => e.time.isAfter(when), inPlace, 'until $when', logs.length);
+
+  /**
+   * Returns log events that happened before [when]. If [inPlace]
+   * is true, then it returns this LogEntryList after removing
+   * the entries that happened from [when] onwards; otherwise a new
+   * list is created.
+   */
+  LogEntryList before(DateTime when, [bool inPlace = false]) =>
+      _head((e) => !e.time.isBefore(when),
+            inPlace,
+            'before $when',
+            logs.length);
+
+  /**
+   * Returns log events that happened after [logEntry]'s time.
+   * If [inPlace] is true, then it returns this LogEntryList after
+   * removing the entries that happened up to [when]; otherwise a new
+   * list is created. If [logEntry] is null the current time is used.
+   */
+  LogEntryList afterEntry(LogEntry logEntry, [bool inPlace = false]) =>
+      after(logEntry == null ? new DateTime.now() : logEntry.time);
+
+  /**
+   * Returns log events that happened from [logEntry]'s time onwards.
+   * If [inPlace] is true, then it returns this LogEntryList after
+   * removing the entries that happened before [when]; otherwise
+   * a new list is created. If [logEntry] is null the current time is used.
+   */
+  LogEntryList fromEntry(LogEntry logEntry, [bool inPlace = false]) =>
+      from(logEntry == null ? new DateTime.now() : logEntry.time);
+
+  /**
+   * Returns log events that happened until [logEntry]'s time. If
+   * [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened after [when]; otherwise a new
+   * list is created. If [logEntry] is null the epoch time is used.
+   */
+  LogEntryList untilEntry(LogEntry logEntry, [bool inPlace = false]) =>
+      until(logEntry == null ?
+          new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
+
+  /**
+   * Returns log events that happened before [logEntry]'s time. If
+   * [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened from [when] onwards; otherwise a new
+   * list is created. If [logEntry] is null the epoch time is used.
+   */
+  LogEntryList beforeEntry(LogEntry logEntry, [bool inPlace = false]) =>
+      before(logEntry == null ?
+          new DateTime.fromMillisecondsSinceEpoch(0) : logEntry.time);
+
+  /**
+   * Returns log events that happened after the first event in [segment].
+   * If [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened earlier; otherwise a new list is created.
+   */
+  LogEntryList afterFirst(LogEntryList segment, [bool inPlace = false]) =>
+      afterEntry(segment.first, inPlace);
+
+  /**
+   * Returns log events that happened after the last event in [segment].
+   * If [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened earlier; otherwise a new list is created.
+   */
+  LogEntryList afterLast(LogEntryList segment, [bool inPlace = false]) =>
+      afterEntry(segment.last, inPlace);
+
+  /**
+   * Returns log events that happened from the time of the first event in
+   * [segment] onwards. If [inPlace] is true, then it returns this
+   * LogEntryList after removing the earlier entries; otherwise a new list
+   * is created.
+   */
+  LogEntryList fromFirst(LogEntryList segment, [bool inPlace = false]) =>
+      fromEntry(segment.first, inPlace);
+
+  /**
+   * Returns log events that happened from the time of the last event in
+   * [segment] onwards. If [inPlace] is true, then it returns this
+   * LogEntryList after removing the earlier entries; otherwise a new list
+   * is created.
+   */
+  LogEntryList fromLast(LogEntryList segment, [bool inPlace = false]) =>
+      fromEntry(segment.last, inPlace);
+
+  /**
+   * Returns log events that happened until the first event in [segment].
+   * If [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened later; otherwise a new list is created.
+   */
+  LogEntryList untilFirst(LogEntryList segment, [bool inPlace = false]) =>
+      untilEntry(segment.first, inPlace);
+
+  /**
+   * Returns log events that happened until the last event in [segment].
+   * If [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened later; otherwise a new list is created.
+   */
+  LogEntryList untilLast(LogEntryList segment, [bool inPlace = false]) =>
+      untilEntry(segment.last, inPlace);
+
+  /**
+   * Returns log events that happened before the first event in [segment].
+   * If [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened later; otherwise a new list is created.
+   */
+  LogEntryList beforeFirst(LogEntryList segment, [bool inPlace = false]) =>
+      beforeEntry(segment.first, inPlace);
+
+  /**
+   * Returns log events that happened before the last event in [segment].
+   * If [inPlace] is true, then it returns this LogEntryList after removing
+   * the entries that happened later; otherwise a new list is created.
+   */
+  LogEntryList beforeLast(LogEntryList segment, [bool inPlace = false]) =>
+      beforeEntry(segment.last, inPlace);
+
+  /**
+   * Iterate through the LogEntryList looking for matches to the entries
+   * in [keys]; for each match found the closest [distance] neighboring log
+   * entries that match [mockNameFilter] and [logFilter] will be included in
+   * the result. If [isPreceding] is true we use the neighbors that precede
+   * the matched entry; else we use the neighbors that followed.
+   * If [includeKeys] is true then the entries in [keys] that resulted in
+   * entries in the output list are themselves included in the output list. If
+   * [distance] is zero then all matches are included.
+   */
+  LogEntryList _neighboring(bool isPreceding,
+                            LogEntryList keys,
+                            mockNameFilter,
+                            logFilter,
+                            int distance,
+                            bool includeKeys) {
+    String filterName = 'Calls to '
+        '${qualifiedName(mockNameFilter, logFilter.toString())} '
+        '${isPreceding?"preceding":"following"} ${keys.filter}';
+
+    LogEntryList rtn = new LogEntryList(filterName);
+
+    // Deal with the trivial case.
+    if (logs.length == 0 || keys.logs.length == 0) {
+      return rtn;
+    }
+
+    // Normalize the mockNameFilter and logFilter values.
+    if (mockNameFilter == null) {
+      mockNameFilter = anything;
+    } else {
+      mockNameFilter = wrapMatcher(mockNameFilter);
+    }
+    logFilter = _makePredicate(logFilter);
+
+    // The scratch list is used to hold matching entries when we
+    // are doing preceding neighbors. The remainingCount is used to
+    // keep track of how many matching entries we can still add in the
+    // current segment (0 if we are doing doing following neighbors, until
+    // we get our first key match).
+    List scratch = null;
+    int remainingCount = 0;
+    if (isPreceding) {
+      scratch = new List();
+      remainingCount = logs.length;
+    }
+
+    var keyIterator = keys.logs.iterator;
+    keyIterator.moveNext();
+    LogEntry keyEntry = keyIterator.current;
+    Map matchState = {};
+
+    for (LogEntry logEntry in logs) {
+      // If we have a log entry match, copy the saved matches from the
+      // scratch buffer into the return list, as well as the matching entry,
+      // if appropriate, and reset the scratch buffer. Continue processing
+      // from the next key entry.
+      if (keyEntry == logEntry) {
+        if (scratch != null) {
+          int numToCopy = scratch.length;
+          if (distance > 0 && distance < numToCopy) {
+            numToCopy = distance;
+          }
+          for (var i = scratch.length - numToCopy; i < scratch.length; i++) {
+            rtn.logs.add(scratch[i]);
+          }
+          scratch.clear();
+        } else {
+          remainingCount = distance > 0 ? distance : logs.length;
+        }
+        if (includeKeys) {
+          rtn.logs.add(keyEntry);
+        }
+        if (keyIterator.moveNext()) {
+          keyEntry = keyIterator.current;
+        } else if (isPreceding) { // We're done.
+          break;
+        }
+      } else if (remainingCount > 0 &&
+                 mockNameFilter.matches(logEntry.mockName, matchState) &&
+                 logFilter(logEntry)) {
+        if (scratch != null) {
+          scratch.add(logEntry);
+        } else {
+          rtn.logs.add(logEntry);
+          --remainingCount;
+        }
+      }
+    }
+    return rtn;
+  }
+
+  /**
+   * Iterate through the LogEntryList looking for matches to the entries
+   * in [keys]; for each match found the closest [distance] prior log entries
+   * that match [mocknameFilter] and [logFilter] will be included in the result.
+   * If [includeKeys] is true then the entries in [keys] that resulted in
+   * entries in the output list are themselves included in the output list. If
+   * [distance] is zero then all matches are included.
+   *
+   * The idea here is that you could find log entries that are related to
+   * other logs entries in some temporal sense. For example, say we have a
+   * method commit() that returns -1 on failure. Before commit() gets called
+   * the value being committed is created by process(). We may want to find
+   * the calls to process() that preceded calls to commit() that failed.
+   * We could do this with:
+   *
+   *      print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
+   *          logFilter: callsTo('process')).toString());
+   *
+   * We might want to include the details of the failing calls to commit()
+   * to see what parameters were passed in, in which case we would set
+   * [includeKeys].
+   *
+   * As another simple example, say we wanted to know the three method
+   * calls that immediately preceded each failing call to commit():
+   *
+   *     print(log.preceding(log.getLogs(callsTo('commit'), returning(-1)),
+   *         distance: 3).toString());
+   */
+  LogEntryList preceding(LogEntryList keys,
+                         {mockNameFilter: null,
+                         logFilter: null,
+                         int distance: 1,
+                         bool includeKeys: false}) =>
+      _neighboring(true, keys, mockNameFilter, logFilter,
+          distance, includeKeys);
+
+  /**
+   * Iterate through the LogEntryList looking for matches to the entries
+   * in [keys]; for each match found the closest [distance] subsequent log
+   * entries that match [mocknameFilter] and [logFilter] will be included in
+   * the result. If [includeKeys] is true then the entries in [keys] that
+   * resulted in entries in the output list are themselves included in the
+   * output list. If [distance] is zero then all matches are included.
+   * See [preceding] for a usage example.
+   */
+  LogEntryList following(LogEntryList keys,
+                         {mockNameFilter: null,
+                         logFilter: null,
+                         int distance: 1,
+                         bool includeKeys: false}) =>
+      _neighboring(false, keys, mockNameFilter, logFilter,
+          distance, includeKeys);
+}
+
+_MockFailureHandler _mockFailureHandler = null;
+
+/**
+ * The failure handler for the [expect()] calls that occur in [verify()]
+ * methods in the mock objects. This calls the real failure handler used
+ * by the unit test library after formatting the error message with
+ * the custom formatter.
+ */
+class _MockFailureHandler implements FailureHandler {
+  FailureHandler proxy;
+  _MockFailureHandler(this.proxy);
+  void fail(String reason) {
+    proxy.fail(reason);
+  }
+  void failMatch(actual, Matcher matcher, String reason,
+                 Map matchState, bool verbose) {
+    proxy.fail(_mockingErrorFormatter(actual, matcher, reason,
+        matchState, verbose));
+  }
+}
+
+/**
+ * The error formatter for mocking is a bit different from the default one
+ * for unit testing; instead of the third argument being a 'reason'
+ * it is instead a [signature] describing the method signature filter
+ * that was used to select the logs that were verified.
+ */
+String _mockingErrorFormatter(actual, Matcher matcher, String signature,
+                              Map matchState, bool verbose) {
+  var description = new StringDescription();
+  description.add('Expected ${signature} ').addDescriptionOf(matcher).
+      add('\n     but: ');
+  matcher.describeMismatch(actual, description, matchState, verbose).add('.');
+  return description.toString();
+}
diff --git a/pkg/mock/lib/src/mock.dart b/pkg/mock/lib/src/mock.dart
new file mode 100644
index 0000000..29598b8
--- /dev/null
+++ b/pkg/mock/lib/src/mock.dart
@@ -0,0 +1,325 @@
+// 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 mock.mock;
+
+// TOOD(kevmoo): just use `Map`
+import 'dart:collection' show LinkedHashMap;
+import 'dart:mirrors';
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'behavior.dart';
+import 'call_matcher.dart';
+import 'log_entry.dart';
+import 'log_entry_list.dart';
+import 'responder.dart';
+import 'util.dart';
+
+/** The base class for all mocked objects. */
+@proxy
+class Mock {
+  /** The mock name. Needed if the log is shared; optional otherwise. */
+  final String name;
+
+  /** The set of [Behavior]s supported. */
+  final LinkedHashMap<String,Behavior> _behaviors;
+
+  /** How to handle unknown method calls - swallow or throw. */
+  final bool _throwIfNoBehavior;
+
+  /** For spys, the real object that we are spying on. */
+  final Object _realObject;
+
+  /** The [log] of calls made. Only used if [name] is null. */
+  LogEntryList log;
+
+  /** Whether to create an audit log or not. */
+  bool _logging;
+
+  bool get logging => _logging;
+  set logging(bool value) {
+    if (value && log == null) {
+      log = new LogEntryList();
+    }
+    _logging = value;
+  }
+
+  /**
+   * Default constructor. Unknown method calls are allowed and logged,
+   * the mock has no name, and has its own log.
+   */
+  Mock() :
+    _throwIfNoBehavior = false, log = null, name = null, _realObject = null,
+    _behaviors = new LinkedHashMap<String,Behavior>() {
+    logging = true;
+  }
+
+  /**
+   * This constructor makes a mock that has a [name] and possibly uses
+   * a shared [log]. If [throwIfNoBehavior] is true, any calls to methods
+   * that have no defined behaviors will throw an exception; otherwise they
+   * will be allowed and logged (but will not do anything).
+   * If [enableLogging] is false, no logging will be done initially (whether
+   * or not a [log] is supplied), but [logging] can be set to true later.
+   */
+  Mock.custom({this.name,
+               this.log,
+               throwIfNoBehavior: false,
+               enableLogging: true})
+      : _throwIfNoBehavior = throwIfNoBehavior, _realObject = null,
+        _behaviors = new LinkedHashMap<String,Behavior>() {
+    if (log != null && name == null) {
+      throw new Exception("Mocks with shared logs must have a name.");
+    }
+    logging = enableLogging;
+  }
+
+  /**
+   * This constructor creates a spy with no user-defined behavior.
+   * This is simply a proxy for a real object that passes calls
+   * through to that real object but captures an audit trail of
+   * calls made to the object that can be queried and validated
+   * later.
+   */
+  Mock.spy(this._realObject, {this.name, this.log})
+    : _behaviors = null,
+     _throwIfNoBehavior = true {
+    logging = true;
+  }
+
+  /**
+   * [when] is used to create a new or extend an existing [Behavior].
+   * A [CallMatcher] [filter] must be supplied, and the [Behavior]s for
+   * that signature are returned (being created first if needed).
+   *
+   * Typical use case:
+   *
+   *     mock.when(callsTo(...)).alwaysReturn(...);
+   */
+  Behavior when(CallMatcher logFilter) {
+    String key = logFilter.toString();
+    if (!_behaviors.containsKey(key)) {
+      Behavior b = new Behavior(logFilter);
+      _behaviors[key] = b;
+      return b;
+    } else {
+      return _behaviors[key];
+    }
+  }
+
+  /**
+   * This is the handler for method calls. We loop through the list
+   * of [Behavior]s, and find the first match that still has return
+   * values available, and then do the action specified by that
+   * return value. If we find no [Behavior] to apply an exception is
+   * thrown.
+   */
+  noSuchMethod(Invocation invocation) {
+    var method = MirrorSystem.getName(invocation.memberName);
+    var args = invocation.positionalArguments;
+    if (invocation.isGetter) {
+      method = 'get $method';
+    } else if (invocation.isSetter) {
+      method = 'set $method';
+      // Remove the trailing '='.
+      if (method[method.length-1] == '=') {
+        method = method.substring(0, method.length - 1);
+      }
+    }
+    if (_behaviors == null) { // Spy.
+      var mirror = reflect(_realObject);
+      try {
+        var result = mirror.delegate(invocation);
+        log.add(new LogEntry(name, method, args, Action.PROXY, result));
+        return result;
+      } catch (e) {
+        log.add(new LogEntry(name, method, args, Action.THROW, e));
+        throw e;
+      }
+    }
+    bool matchedMethodName = false;
+    Map matchState = {};
+    for (String k in _behaviors.keys) {
+      Behavior b = _behaviors[k];
+      if (b.matcher.nameFilter.matches(method, matchState)) {
+        matchedMethodName = true;
+      }
+      if (b.matches(method, args)) {
+        List actions = b.actions;
+        if (actions == null || actions.length == 0) {
+          continue; // No return values left in this Behavior.
+        }
+        // Get the first response.
+        Responder response = actions[0];
+        // If it is exhausted, remove it from the list.
+        // Note that for endlessly repeating values, we started the count at
+        // 0, so we get a potentially useful value here, which is the
+        // (negation of) the number of times we returned the value.
+        if (--response.count == 0) {
+          actions.removeRange(0, 1);
+        }
+        // Do the response.
+        Action action = response.action;
+        var value = response.value;
+        if (action == Action.RETURN) {
+          if (_logging && b.logging) {
+            log.add(new LogEntry(name, method, args, action, value));
+          }
+          return value;
+        } else if (action == Action.THROW) {
+          if (_logging && b.logging) {
+            log.add(new LogEntry(name, method, args, action, value));
+          }
+          throw value;
+        } else if (action == Action.PROXY) {
+          // TODO(gram): Replace all this with:
+          //     var rtn = reflect(value).apply(invocation.positionalArguments,
+          //         invocation.namedArguments);
+          // once that is supported.
+          var rtn;
+          switch (args.length) {
+            case 0:
+              rtn = value();
+              break;
+            case 1:
+              rtn = value(args[0]);
+              break;
+            case 2:
+              rtn = value(args[0], args[1]);
+              break;
+            case 3:
+              rtn = value(args[0], args[1], args[2]);
+              break;
+            case 4:
+              rtn = value(args[0], args[1], args[2], args[3]);
+              break;
+            case 5:
+              rtn = value(args[0], args[1], args[2], args[3], args[4]);
+              break;
+            case 6:
+              rtn = value(args[0], args[1], args[2], args[3],
+                  args[4], args[5]);
+              break;
+            case 7:
+              rtn = value(args[0], args[1], args[2], args[3],
+                  args[4], args[5], args[6]);
+              break;
+            case 8:
+              rtn = value(args[0], args[1], args[2], args[3],
+                  args[4], args[5], args[6], args[7]);
+              break;
+            case 9:
+              rtn = value(args[0], args[1], args[2], args[3],
+                  args[4], args[5], args[6], args[7], args[8]);
+              break;
+            case 9:
+              rtn = value(args[0], args[1], args[2], args[3],
+                  args[4], args[5], args[6], args[7], args[8], args[9]);
+              break;
+            default:
+              throw new Exception(
+                  "Cannot proxy calls with more than 10 parameters.");
+          }
+          if (_logging && b.logging) {
+            log.add(new LogEntry(name, method, args, action, rtn));
+          }
+          return rtn;
+        }
+      }
+    }
+    if (matchedMethodName) {
+      // User did specify behavior for this method, but all the
+      // actions are exhausted. This is considered an error.
+      throw new Exception('No more actions for method '
+          '${qualifiedName(name, method)}.');
+    } else if (_throwIfNoBehavior) {
+      throw new Exception('No behavior specified for method '
+          '${qualifiedName(name, method)}.');
+    }
+    // Otherwise user hasn't specified behavior for this method; we don't throw
+    // so we can underspecify.
+    if (_logging) {
+      log.add(new LogEntry(name, method, args, Action.IGNORE));
+    }
+  }
+
+  /** [verifyZeroInteractions] returns true if no calls were made */
+  bool verifyZeroInteractions() {
+    if (log == null) {
+      // This means we created the mock with logging off and have never turned
+      // it on, so it doesn't make sense to verify behavior on such a mock.
+      throw new
+          Exception("Can't verify behavior when logging was never enabled.");
+    }
+    return log.logs.length == 0;
+  }
+
+  /**
+   * [getLogs] extracts all calls from the call log that match the
+   * [logFilter], and returns the matching list of [LogEntry]s. If
+   * [destructive] is false (the default) the matching calls are left
+   * in the log, else they are removed. Removal allows us to verify a
+   * set of interactions and then verify that there are no other
+   * interactions left. [actionMatcher] can be used to further
+   * restrict the returned logs based on the action the mock performed.
+   * [logFilter] can be a [CallMatcher] or a predicate function that
+   * takes a [LogEntry] and returns a bool.
+   *
+   * Typical usage:
+   *
+   *     getLogs(callsTo(...)).verify(...);
+   */
+  LogEntryList getLogs([CallMatcher logFilter,
+                        Matcher actionMatcher,
+                        bool destructive = false]) {
+    if (log == null) {
+      // This means we created the mock with logging off and have never turned
+      // it on, so it doesn't make sense to get logs from such a mock.
+      throw new
+          Exception("Can't retrieve logs when logging was never enabled.");
+    } else {
+      return log.getMatches(name, logFilter, actionMatcher, destructive);
+    }
+  }
+
+  /**
+   * Useful shorthand method that creates a [CallMatcher] from its arguments
+   * and then calls [getLogs].
+   */
+  LogEntryList calls(method,
+                      [arg0 = NO_ARG,
+                       arg1 = NO_ARG,
+                       arg2 = NO_ARG,
+                       arg3 = NO_ARG,
+                       arg4 = NO_ARG,
+                       arg5 = NO_ARG,
+                       arg6 = NO_ARG,
+                       arg7 = NO_ARG,
+                       arg8 = NO_ARG,
+                       arg9 = NO_ARG]) =>
+      getLogs(callsTo(method, arg0, arg1, arg2, arg3, arg4,
+          arg5, arg6, arg7, arg8, arg9));
+
+  /** Clear the behaviors for the Mock. */
+  void resetBehavior() => _behaviors.clear();
+
+  /** Clear the logs for the Mock. */
+  void clearLogs() {
+    if (log != null) {
+      if (name == null) { // This log is not shared.
+        log.logs.clear();
+      } else { // This log may be shared.
+        log.logs = log.logs.where((e) => e.mockName != name).toList();
+      }
+    }
+  }
+
+  /** Clear both logs and behavior. */
+  void reset() {
+    resetBehavior();
+    clearLogs();
+  }
+}
diff --git a/pkg/mock/lib/src/responder.dart b/pkg/mock/lib/src/responder.dart
new file mode 100644
index 0000000..ef0dd71
--- /dev/null
+++ b/pkg/mock/lib/src/responder.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mock.responder;
+
+import 'action.dart';
+
+/**
+ * The behavior of a method call in the mock library is specified
+ * with [Responder]s. A [Responder] has a [value] to throw
+ * or return (depending on the type of [action]),
+ * and can either be one-shot, multi-shot, or infinitely repeating,
+ * depending on the value of [count (1, greater than 1, or 0 respectively).
+ */
+class Responder {
+  final Object value;
+  final Action action;
+  int count;
+  Responder(this.value, [this.count = 1, this.action = Action.RETURN]);
+}
diff --git a/pkg/mock/lib/src/result_matcher.dart b/pkg/mock/lib/src/result_matcher.dart
new file mode 100644
index 0000000..08f89d6
--- /dev/null
+++ b/pkg/mock/lib/src/result_matcher.dart
@@ -0,0 +1,67 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mock.result_matcher;
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'log_entry.dart';
+
+/**
+ * [_ResultMatcher]s are used to make assertions about the results
+ * of method calls. These can be used as optional parameters to [getLogs].
+ */
+class _ResultMatcher extends Matcher {
+  final Action action;
+  final Matcher value;
+
+  const _ResultMatcher(this.action, this.value);
+
+  bool matches(item, Map matchState) {
+    if (item is! LogEntry) {
+     return false;
+    }
+    // normalize the action; _PROXY is like _RETURN.
+    Action eaction = item.action;
+    if (eaction == Action.PROXY) {
+      eaction = Action.RETURN;
+    }
+    return (eaction == action && value.matches(item.value, matchState));
+  }
+
+  Description describe(Description description) {
+    description.add(' to ');
+    if (action == Action.RETURN || action == Action.PROXY)
+      description.add('return ');
+    else
+      description.add('throw ');
+    return description.addDescriptionOf(value);
+  }
+
+  Description describeMismatch(item, Description mismatchDescription,
+                               Map matchState, bool verbose) {
+    if (item.action == Action.RETURN || item.action == Action.PROXY) {
+      mismatchDescription.add('returned ');
+    } else {
+      mismatchDescription.add('threw ');
+    }
+    mismatchDescription.add(item.value);
+    return mismatchDescription;
+  }
+}
+
+/**
+ *[returning] matches log entries where the call to a method returned
+ * a value that matched [value].
+ */
+Matcher returning(value) =>
+    new _ResultMatcher(Action.RETURN, wrapMatcher(value));
+
+/**
+ *[throwing] matches log entrues where the call to a method threw
+ * a value that matched [value].
+ */
+Matcher throwing(value) =>
+    new _ResultMatcher(Action.THROW, wrapMatcher(value));
diff --git a/pkg/mock/lib/src/result_set_matcher.dart b/pkg/mock/lib/src/result_set_matcher.dart
new file mode 100644
index 0000000..103ce78
--- /dev/null
+++ b/pkg/mock/lib/src/result_set_matcher.dart
@@ -0,0 +1,144 @@
+// 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 mock.result_set_matcher;
+
+import 'package:matcher/matcher.dart';
+
+import 'action.dart';
+import 'log_entry.dart';
+
+/** Special values for use with [_ResultSetMatcher] [frequency]. */
+class _Frequency {
+  /** Every call/throw must match */
+  static const ALL = const _Frequency._('ALL');
+
+  /** At least one call/throw must match. */
+  static const SOME = const _Frequency._('SOME');
+
+  /** No calls/throws should match. */
+  static const NONE = const _Frequency._('NONE');
+
+  const _Frequency._(this.name);
+
+  final String name;
+}
+
+/**
+ * [_ResultSetMatcher]s are used to make assertions about the results
+ * of method calls. When filtering an execution log by calling
+ * [getLogs], a [LogEntrySet] of matching call logs is returned;
+ * [_ResultSetMatcher]s can then assert various things about this
+ * (sub)set of logs.
+ *
+ * We could make this class use _ResultMatcher but it doesn't buy that
+ * match and adds some perf hit, so there is some duplication here.
+ */
+class _ResultSetMatcher extends Matcher {
+  final Action action;
+  final Matcher value;
+  final _Frequency frequency; // ALL, SOME, or NONE.
+
+  const _ResultSetMatcher(this.action, this.value, this.frequency);
+
+  bool matches(logList, Map matchState) {
+    for (LogEntry entry in logList) {
+      // normalize the action; PROXY is like RETURN.
+      Action eaction = entry.action;
+      if (eaction == Action.PROXY) {
+        eaction = Action.RETURN;
+      }
+      if (eaction == action && value.matches(entry.value, matchState)) {
+        if (frequency == _Frequency.NONE) {
+          addStateInfo(matchState, {'entry': entry});
+          return false;
+        } else if (frequency == _Frequency.SOME) {
+          return true;
+        }
+      } else {
+        // Mismatch.
+        if (frequency == _Frequency.ALL) { // We need just one mismatch to fail.
+          addStateInfo(matchState, {'entry': entry});
+          return false;
+        }
+      }
+    }
+    // If we get here, then if count is _ALL we got all matches and
+    // this is success; otherwise we got all mismatched which is
+    // success for count == _NONE and failure for count == _SOME.
+    return (frequency != _Frequency.SOME);
+  }
+
+  Description describe(Description description) {
+    description.add(' to ');
+    description.add(frequency == _Frequency.ALL ? 'alway ' :
+        (frequency == _Frequency.NONE ? 'never ' : 'sometimes '));
+    if (action == Action.RETURN || action == Action.PROXY)
+      description.add('return ');
+    else
+      description.add('throw ');
+    return description.addDescriptionOf(value);
+  }
+
+  Description describeMismatch(logList, Description mismatchDescription,
+                               Map matchState, bool verbose) {
+    if (frequency != _Frequency.SOME) {
+      LogEntry entry = matchState['entry'];
+      if (entry.action == Action.RETURN || entry.action == Action.PROXY) {
+        mismatchDescription.add('returned');
+      } else {
+        mismatchDescription.add('threw');
+      }
+      mismatchDescription.add(' value that ');
+      value.describeMismatch(entry.value, mismatchDescription,
+        matchState['state'], verbose);
+      mismatchDescription.add(' at least once');
+    } else {
+      mismatchDescription.add('never did');
+    }
+    return mismatchDescription;
+  }
+}
+
+/**
+ *[alwaysReturned] asserts that all matching calls to a method returned
+ * a value that matched [value].
+ */
+Matcher alwaysReturned(value) =>
+    new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.ALL);
+
+/**
+ *[sometimeReturned] asserts that at least one matching call to a method
+ * returned a value that matched [value].
+ */
+Matcher sometimeReturned(value) =>
+    new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.SOME);
+
+/**
+ *[neverReturned] asserts that no matching calls to a method returned
+ * a value that matched [value].
+ */
+Matcher neverReturned(value) =>
+    new _ResultSetMatcher(Action.RETURN, wrapMatcher(value), _Frequency.NONE);
+
+/**
+ *[alwaysThrew] asserts that all matching calls to a method threw
+ * a value that matched [value].
+ */
+Matcher alwaysThrew(value) =>
+    new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.ALL);
+
+/**
+ *[sometimeThrew] asserts that at least one matching call to a method threw
+ * a value that matched [value].
+ */
+Matcher sometimeThrew(value) =>
+  new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.SOME);
+
+/**
+ *[neverThrew] asserts that no matching call to a method threw
+ * a value that matched [value].
+ */
+Matcher neverThrew(value) =>
+  new _ResultSetMatcher(Action.THROW, wrapMatcher(value), _Frequency.NONE);
diff --git a/pkg/mock/lib/src/times_matcher.dart b/pkg/mock/lib/src/times_matcher.dart
new file mode 100644
index 0000000..225fc94
--- /dev/null
+++ b/pkg/mock/lib/src/times_matcher.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mock.times_matcher;
+
+import 'package:matcher/matcher.dart';
+
+/**
+ * [_TimesMatcher]s are used to make assertions about the number of
+ * times a method was called.
+ */
+class _TimesMatcher extends Matcher {
+  final int min, max;
+
+  const _TimesMatcher(this.min, [this.max = -1]);
+
+  bool matches(logList, Map matchState) => logList.length >= min &&
+      (max < 0 || logList.length <= max);
+
+  Description describe(Description description) {
+    description.add('to be called ');
+    if (max < 0) {
+      description.add('at least $min');
+    } else if (max == min) {
+      description.add('$max');
+    } else if (min == 0) {
+      description.add('at most $max');
+    } else {
+      description.add('between $min and $max');
+    }
+    return description.add(' times');
+  }
+
+  Description describeMismatch(logList, Description mismatchDescription,
+                               Map matchState, bool verbose) =>
+      mismatchDescription.add('was called ${logList.length} times');
+}
+
+/** [happenedExactly] matches an exact number of calls. */
+Matcher happenedExactly(count) {
+  return new _TimesMatcher(count, count);
+}
+
+/** [happenedAtLeast] matches a minimum number of calls. */
+Matcher happenedAtLeast(count) {
+  return new _TimesMatcher(count);
+}
+
+/** [happenedAtMost] matches a maximum number of calls. */
+Matcher happenedAtMost(count) {
+  return new _TimesMatcher(0, count);
+}
+
+/** [neverHappened] matches zero calls. */
+const Matcher neverHappened = const _TimesMatcher(0, 0);
+
+/** [happenedOnce] matches exactly one call. */
+const Matcher happenedOnce = const _TimesMatcher(1, 1);
+
+/** [happenedAtLeastOnce] matches one or more calls. */
+const Matcher happenedAtLeastOnce = const _TimesMatcher(1);
+
+/** [happenedAtMostOnce] matches zero or one call. */
+const Matcher happenedAtMostOnce = const _TimesMatcher(0, 1);
diff --git a/pkg/mock/lib/src/util.dart b/pkg/mock/lib/src/util.dart
new file mode 100644
index 0000000..5341b88
--- /dev/null
+++ b/pkg/mock/lib/src/util.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library mock.util;
+
+import 'package:matcher/matcher.dart';
+
+/** Utility function for optionally qualified method names */
+String qualifiedName(owner, String method) {
+  if (owner == null || identical(owner, anything)) {
+    return method;
+  } else if (owner is Matcher) {
+    Description d = new StringDescription();
+    d.addDescriptionOf(owner);
+    d.add('.');
+    d.add(method);
+    return d.toString();
+  } else {
+    return '$owner.$method';
+  }
+}
+
+/** Sentinel value for representing no argument. */
+class _Sentinel {
+  const _Sentinel();
+}
+const NO_ARG = const _Sentinel();
diff --git a/pkg/mock/pubspec.yaml b/pkg/mock/pubspec.yaml
index 33acbdb..2220370 100644
--- a/pkg/mock/pubspec.yaml
+++ b/pkg/mock/pubspec.yaml
@@ -1,5 +1,5 @@
 name: mock
-version: 0.10.0+1
+version: 0.10.1-dev
 author: Dart Team <misc@dartlang.org>
 description: A library for mocking classes
 homepage: http://www.dartlang.org
diff --git a/pkg/observe/pubspec.yaml b/pkg/observe/pubspec.yaml
index bf2bd93..83b924c 100644
--- a/pkg/observe/pubspec.yaml
+++ b/pkg/observe/pubspec.yaml
@@ -1,5 +1,5 @@
 name: observe
-version: 0.10.0-pre.4.dev
+version: 0.10.0-pre.4
 author: Polymer.dart Authors <web-ui-dev@dartlang.org>
 description: >
   Observable properties and objects for use in template_binding.
diff --git a/pkg/pkg.gyp b/pkg/pkg.gyp
index 9e37d4d..df21e10 100644
--- a/pkg/pkg.gyp
+++ b/pkg/pkg.gyp
@@ -18,7 +18,7 @@
             '<!@(["python", "../tools/list_pkg_directories.py", '
                 '"../third_party/pkg"])',
             '<!@(["python", "../tools/list_pkg_directories.py", '
-                '"../pkg/polymer/example/"])',
+                '"polymer/example/"])',
           ],
           'outputs': [
             '<(SHARED_INTERMEDIATE_DIR)/packages.stamp',
@@ -31,6 +31,6 @@
           ],
         },
       ],
-    }
+    },
   ],
 }
diff --git a/pkg/pkg.status b/pkg/pkg.status
index b0bd22e..13de742 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -17,39 +17,32 @@
 scheduled_test/test/scheduled_server_test: Pass, Fail # 13524
 scheduled_test/test/scheduled_process_test: Pass, Slow # Issue 9231
 
-[ $compiler == dart2js && $mode == debug ]
-docgen/test/generate_json_test: Skip # Way too slow
-docgen/test/multi_library_test: Skip # Way too slow
-docgen/test/single_library_test: Skip # Way too slow
-docgen/test/typedef_test: Skip # Way too slow
-
 [ $runtime == vm && $mode == debug]
-docgen/test/only_lib_content_in_pkg_test: Skip # Slow
-polymer/test/build/all_phases_test: Skip # Slow
 analysis_server/test/analysis_server_test: Pass, Timeout
 analysis_server/test/domain_context_test: Pass, Timeout
 analysis_server/test/domain_server_test: Pass, Timeout
 analyzer/test/generated/element_test: Pass, Timeout
 analyzer/test/generated/parser_test: Pass, Timeout
 code_transformers/test/resolver_test: Pass, Timeout
-docgen/test/only_lib_content_in_pkg_test: Pass, Timeout
-polymer_expressions/test/globals_test: Pass, Timeout
-polymer/test/build/all_phases_test: Pass, Timeout
+docgen/test/*: Skip # Slow
+polymer/test/build/all_phases_test: Skip # Slow
 polymer/test/build/script_compactor_test: Pass, Timeout
+polymer_expressions/test/globals_test: Pass, Timeout
 smoke/test/codegen/end_to_end_test: Pass, Timeout
 smoke/test/codegen/recorder_test: Pass, Timeout
 template_binding/test/template_binding_test: Pass, Timeout
 third_party/html5lib/test/tokenizer_test: Pass, Timeout
 
 [ $runtime == vm && ( $arch == simarm || $arch == simmips ) ]
-docgen/test/only_lib_content_in_pkg_test: Skip # slow
-docgen/test/typedef_test: Skip # slow
 barback/test/too_many_open_files_test: Skip # 14220
-third_party/html5lib/test/tokenizer_test: Pass, Slow
-docgen/test/generate_json_test: Skip # Issue 17003
-polymer/test/build/script_compactor_test: Skip # Slow
-polymer/test/build/all_phases_test: Skip # Slow
 code_transformers/test/resolver_test: Skip # Issue 17908
+docgen/test/*: Skip # Issue 17003
+polymer/test/build/all_phases_test: Skip # Slow
+polymer/test/build/script_compactor_test: Skip # Slow
+third_party/html5lib/test/tokenizer_test: Pass, Slow
+
+[ $runtime == vm ]
+docgen/test/typedef_test: RuntimeError # Issue 18352
 
 [ $compiler == dart2js ]
 collection/test/equality_test/01: Fail # Issue 1533
@@ -58,7 +51,7 @@
 collection/test/equality_test/04: Fail # Issue 1533
 collection/test/equality_test/05: Fail # Issue 1533
 collection/test/equality_test/none: Pass, Fail # Issue 14348
-docgen/test/multi_library_test: Slow, Pass # issue 17060
+docgen/test/*: Skip # Far too slow
 third_party/angular_tests/browser_test: Pass, Slow # Large dart2js compile time
 typed_data/test/typed_buffers_test/01: Fail # Not supporting Int64List, Uint64List.
 
@@ -72,14 +65,15 @@
 
 [ $runtime == jsshell ]
 analyzer/test/generated/element_test: Pass, Slow # Issue 16473
-docgen/test/typedef_test: Slow, Pass # issue 17060
 
 [ $runtime == d8 || $runtime == jsshell ]
-async/test/stream_zip_test: RuntimeError, OK # Timers are not supported.
-scheduled_test/test/unittest_compatibility_test: RuntimeError # Issue 7728
 stack_trace/test/chain_test: Fail # Issues 15171 and 15105
 stack_trace/test/vm_test: RuntimeError, OK # VM-specific traces
 unittest/test/missing_tick_test: Fail # Timer interface not supported: dartbug.com/7728.
+
+[ $runtime == jsshell ]
+async/test/stream_zip_test: RuntimeError, OK # Timers are not supported.
+scheduled_test/test/unittest_compatibility_test: RuntimeError # Issue 7728
 unittest/test/unittest_nested_groups_setup_teardown_test: RuntimeError # http://dartbug.com/10109
 
 [ $runtime == vm || $runtime == d8 || $runtime == jsshell ]
@@ -167,7 +161,6 @@
 polymer/example/canonicalization3: Skip
 
 third_party/angular_tests/browser_test: StaticWarning # Issue 15890
-docgen/test/*: StaticWarning # Issue 17896
 
 [ $compiler == dart2js && $runtime == none]
 polymer/example/canonicalization: Skip
@@ -191,14 +184,13 @@
 
 [ $compiler == dart2js && $browser ]
 stack_trace/test/vm_test: Fail, OK # VM-specific traces
-stack_trace/test/chain_test: Fail, Timeout # Issues 15171, 15105, and 18142
+stack_trace/test/chain_test: Fail # Issues 15171 and 15105
 crypto/test/sha256_test: Slow, Pass
 crypto/test/sha1_test: Slow, Pass
 polymer/example/component: Fail # Issue 13198
 polymer/test/mirror_loader_test: Skip # tests development only behavior
 
 [ $compiler == dart2js && $runtime == chromeOnAndroid ]
-docgen/test/single_library_test: Fail # TODO(kasperl): Please triage.
 intl/test/date_time_format_http_request_test: Fail # TODO(kasperl): Please triage.
 
 [ $browser ]
diff --git a/pkg/pkg_files.gyp b/pkg/pkg_files.gyp
new file mode 100644
index 0000000..df3a986
--- /dev/null
+++ b/pkg/pkg_files.gyp
@@ -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.
+
+{
+  'targets': [
+    # Other targets depend on pkg files, but have to many inputs, which causes
+    # issues on some platforms.
+    # This target lists all the files in pkg and third_party/pkg,
+    # and creates a single pkg_files.stamp
+    {
+      'target_name': 'pkg_files_stamp',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'make_pkg_files_stamp',
+          'inputs': [
+            '../tools/create_timestamp_file.py',
+            '<!@(["python", "../tools/list_files.py", "\\.dart$", "."])',
+            '<!@(["python", "../tools/list_files.py", "\\.dart$",'
+                '"../third_party/pkg"])',
+          ],
+          'outputs': [
+            '<(SHARED_INTERMEDIATE_DIR)/pkg_files.stamp',
+          ],
+          'action': [
+            'python', '../tools/create_timestamp_file.py',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    }
+  ],
+}
diff --git a/pkg/pkgbuild.status b/pkg/pkgbuild.status
index ea8f0de..7e856fd 100644
--- a/pkg/pkgbuild.status
+++ b/pkg/pkgbuild.status
@@ -2,8 +2,6 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-third_party/pkg/route_hierarchical: Fail
-
 samples/third_party/angular_todo: Pass, Slow
 samples/third_party/dromaeo: Pass, Slow
 samples/third_party/todomvc_performance: Pass, Slow
@@ -28,15 +26,3 @@
 
 [ $system == windows ]
 samples/third_party/todomvc_performance: Fail # Issue 18086
-
-[ $use_public_packages ]
-pkg/observe: PubGetError # Issue 18115
-pkg/polymer: PubGetError # Issue 18115
-pkg/polymer_expressions: PubGetError # Issue 18115
-pkg/template_binding: PubGetError # Issue 18115
-samples/polymer_intl: PubGetError # Issue 18115
-samples/searchable_list: PubGetError # Issue 18115
-samples/third_party/angular_todo: PubGetError # Issue 18115
-samples/third_party/todomvc: PubGetError # Issue 18115
-samples/third_party/todomvc_performance: PubGetError # Issue 18115
-samples/tracker: PubGetError # Issue 18115
diff --git a/pkg/polymer/lib/src/build/import_inliner.dart b/pkg/polymer/lib/src/build/import_inliner.dart
index ee2aee0..e785315 100644
--- a/pkg/polymer/lib/src/build/import_inliner.dart
+++ b/pkg/polymer/lib/src/build/import_inliner.dart
@@ -28,6 +28,7 @@
   final AssetId docId;
   final seen = new Set<AssetId>();
   final scriptIds = <AssetId>[];
+  final extractedFiles = new Set<AssetId>();
 
   /// The number of extracted inline Dart scripts. Used as a counter to give
   /// unique-ish filenames.
@@ -51,8 +52,10 @@
       changed = _extractScripts(document, docId);
       return _visitImports(document);
     }).then((importsFound) {
-      bool scriptsRemoved = _removeScripts(document);
-      changed = changed || importsFound || scriptsRemoved;
+      changed = changed || importsFound;
+      return _removeScripts(document);
+    }).then((scriptsRemoved) {
+      changed = changed || scriptsRemoved;
 
       var output = transform.primaryInput;
       if (changed) output = new Asset.fromString(docId, document.outerHtml);
@@ -160,17 +163,32 @@
   ///
   /// Dartium only allows a single script tag per page, so we can't inline
   /// the script tags. Instead we remove them entirely.
-  bool _removeScripts(Document doc) {
+  Future<bool> _removeScripts(Document doc) {
     bool changed = false;
-    for (var script in doc.querySelectorAll('script')) {
+    return Future.forEach(doc.querySelectorAll('script'), (script) {
       if (script.attributes['type'] == TYPE_DART_COMPONENT) {
         changed = true;
         script.remove();
         var src = script.attributes['src'];
-        scriptIds.add(uriToAssetId(docId, src, logger, script.sourceSpan));
+        var srcId = uriToAssetId(docId, src, logger, script.sourceSpan);
+
+        // We check for extractedFiles because 'hasInput' below is only true for
+        // assets that existed before this transformer runs (hasInput is false
+        // for files created by [_extractScripts]).
+        if (extractedFiles.contains(srcId)) {
+          scriptIds.add(srcId);
+          return true;
+        }
+        return transform.hasInput(srcId).then((exists) {
+          if (!exists) {
+            logger.warning('Script file at "$src" not found.',
+              span: script.sourceSpan);
+          } else {
+            scriptIds.add(srcId);
+          }
+        });
       }
-    }
-    return changed;
+    }).then((_) => changed);
   }
 
   /// Split inline scripts into their own files. We need to do this for dart2js
@@ -219,6 +237,7 @@
 
         code = "library $libName;\n$code";
       }
+      extractedFiles.add(newId);
       transform.addOutput(new Asset.fromString(newId, code));
     }
     return changed;
diff --git a/pkg/polymer/lib/src/build/script_compactor.dart b/pkg/polymer/lib/src/build/script_compactor.dart
index 4690889..b90dfe1 100644
--- a/pkg/polymer/lib/src/build/script_compactor.dart
+++ b/pkg/polymer/lib/src/build/script_compactor.dart
@@ -139,26 +139,32 @@
   /// Emits the main HTML and Dart bootstrap code for the application. If there
   /// were not Dart entry point files, then this simply emits the original HTML.
   Future _emitNewEntrypoint(_) {
-    if (entryLibraries.isEmpty) {
-      // We didn't find code, nothing to do.
-      transform.addOutput(transform.primaryInput);
-      return null;
-    }
-
+    // If we don't find code, there is nothing to do.
+    if (entryLibraries.isEmpty) return null;
     return _initResolver()
         .then(_extractUsesOfMirrors)
         .then(_emitFiles)
-        .whenComplete(() => resolver.release());
+        .whenComplete(() {
+          if (resolver != null) resolver.release();
+        });
   }
 
   /// Load a resolver that computes information for every library in
   /// [entryLibraries], then use it to initialize the [recorder] (for import
   /// resolution) and to resolve specific elements (for analyzing the user's
   /// code).
-  Future _initResolver() => resolvers.get(transform, entryLibraries).then((r) {
-    resolver = r;
-    types = new _ResolvedTypes(resolver);
-  });
+  Future _initResolver() {
+    // We include 'polymer.dart' to simplify how we do resolution below. This
+    // way we can assume polymer is there, even if the user didn't include an
+    // import to it. If not, the polymer build will fail with an error when
+    // trying to create _ResolvedTypes below.
+    var libsToLoad = [new AssetId('polymer', 'lib/polymer.dart')]
+        ..addAll(entryLibraries);
+    return resolvers.get(transform, libsToLoad).then((r) {
+      resolver = r;
+      types = new _ResolvedTypes(resolver);
+    });
+  }
 
   /// Inspects the entire program to find out anything that polymer accesses
   /// using mirrors and produces static information that can be used to replace
@@ -343,15 +349,21 @@
     generator.writeTopLevelDeclarations(code);
     code.writeln('\nvoid main() {');
     generator.writeInitCall(code);
-    code.writeln('  startPolymer([');
+    code.write('  startPolymer([');
 
     // Include initializers to switch from mirrors_loader to static_loader.
-    for (var init in initializers) {
-      var initCode = init.asCode(prefixes[init.assetId]);
-      code.write("      $initCode,\n");
+    if (!initializers.isEmpty) {
+      code.writeln();
+      for (var init in initializers) {
+        var initCode = init.asCode(prefixes[init.assetId]);
+        code.write("      $initCode,\n");
+      }
+      code.writeln('    ]);');
+    } else {
+      logger.warning(NO_INITIALIZERS_ERROR);
+      code.writeln(']);');
     }
-    code..writeln('    ]);')
-        ..writeln('}');
+    code.writeln('}');
     transform.addOutput(new Asset.fromString(bootstrapId, code.toString()));
 
 
@@ -402,6 +414,13 @@
 import 'package:polymer/polymer.dart';
 """;
 
+const NO_INITIALIZERS_ERROR =
+    'No polymer initializers were found. Make sure to either '
+    'annotate your polymer elements with @CustomTag or include a '
+    'top level method annotated with @initMethod that registers your '
+    'elements. Both annotations are defined in the polymer library ('
+    'package:polymer/polymer.dart).';
+
 /// An html visitor that:
 ///   * finds all polymer expressions and records the getters and setters that
 ///     will be needed to evaluate them at runtime.
@@ -538,7 +557,7 @@
 
   visitInvoke(pe.Invoke e) {
     _includeSetter = false; // Invoke is only valid as an r-value.
-    _add(e.method);
+    if (e.method != null) _add(e.method);
     super.visitInvoke(e);
   }
 }
diff --git a/pkg/polymer/pubspec.yaml b/pkg/polymer/pubspec.yaml
index aca5df5..05badf4 100644
--- a/pkg/polymer/pubspec.yaml
+++ b/pkg/polymer/pubspec.yaml
@@ -1,5 +1,5 @@
 name: polymer
-version: 0.10.0-pre.9.dev
+version: 0.10.0-pre.9
 author: Polymer.dart Authors <web-ui-dev@dartlang.org>
 description: >
   Polymer.dart is a new type of library for the web, built on top of Web
diff --git a/pkg/polymer/test/bind_mdv_test.html b/pkg/polymer/test/bind_mdv_test.html
index 2539932..1f23b31 100644
--- a/pkg/polymer/test/bind_mdv_test.html
+++ b/pkg/polymer/test/bind_mdv_test.html
@@ -17,8 +17,6 @@
   <h1> Running bind_mdv_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/pkg/polymer/test/build/code_extractor.dart b/pkg/polymer/test/build/code_extractor.dart
index beeafcb..813d13d 100644
--- a/pkg/polymer/test/build/code_extractor.dart
+++ b/pkg/polymer/test/build/code_extractor.dart
@@ -164,6 +164,7 @@
     });
 
   group('fixes import/export/part URIs', dartUriTests);
+  group('validates script-tag URIs', validateUriTests);
 }
 
 dartUriTests() {
@@ -312,3 +313,55 @@
           '</body></html>',
     });
 }
+
+validateUriTests() {
+
+  testPhases('script is inline', phases, {
+      'a|web/test.html':
+          '<!DOCTYPE html><html><body>'
+          '<script type="application/dart;component=1">'
+          'main(){}'
+          '</script>'
+          '</body></html>',
+    }, {
+      'a|web/test.html.scriptUrls': '[["a","web/test.html.0.dart"]]',
+    }, []);
+
+  testPhases('script src is valid', phases, {
+      'a|web/test.html':
+          '<!DOCTYPE html><html><body>'
+          '<script type="application/dart;component=1" src="a.dart"></script>'
+          '</body></html>',
+      'a|web/a.dart': 'main() {}',
+    }, {
+      'a|web/test.html.scriptUrls': '[["a","web/a.dart"]]',
+    }, []);
+
+  testPhases('script src is invalid', phases, {
+      'a|web/test.html':
+          '<!DOCTYPE html><html><body>\n'
+          '<script type="application/dart;component=1" src="a.dart"></script>'
+          '</body></html>',
+    }, {
+      'a|web/test.html.scriptUrls': '[]',
+    }, [
+      'warning: Script file at "a.dart" not found. (web/test.html 1 0)',
+    ]);
+
+  testPhases('many script src around, valid and invalid', phases, {
+      'a|web/test.html':
+          '<!DOCTYPE html><html><body>'
+          '<script type="application/dart;component=1" src="a.dart"></script>'
+          '\n<script type="application/dart;component=1" src="b.dart"></script>'
+          '\n<script type="application/dart;component=1" src="c.dart"></script>'
+          '\n<script type="application/dart;component=1" src="d.dart"></script>'
+          '</body></html>',
+      'a|web/a.dart': 'main() {}',
+      'a|web/c.dart': 'main() {}',
+    }, {
+      'a|web/test.html.scriptUrls': '[["a","web/a.dart"],["a","web/c.dart"]]',
+    }, [
+      'warning: Script file at "b.dart" not found. (web/test.html 1 0)',
+      'warning: Script file at "d.dart" not found. (web/test.html 3 0)',
+    ]);
+}
diff --git a/pkg/polymer/test/build/import_inliner_test.dart b/pkg/polymer/test/build/import_inliner_test.dart
index 8741ac1..6684c5b 100644
--- a/pkg/polymer/test/build/import_inliner_test.dart
+++ b/pkg/polymer/test/build/import_inliner_test.dart
@@ -436,6 +436,8 @@
           '<link rel="import" href="test_1.html">'
           '</head><body><polymer-element>2</polymer-element>'
           '<script type="application/dart" src="s2.dart"></script></html>',
+      'a|web/s1.dart': '',
+      'a|web/s2.dart': '',
     }, {
       'a|web/test.html':
           '<!DOCTYPE html><html><head>'
@@ -484,6 +486,8 @@
           '<script type="application/dart;component=1" src="s2.dart"></script>'
           '</polymer-element>'
           '</html>',
+      'a|web/s1.dart': '',
+      'a|web/s2.dart': '',
     }, {
       'a|web/test.html':
           '<!DOCTYPE html><html><head>'
@@ -525,6 +529,7 @@
           '<script type="application/dart;component=1" src="s1.dart"></script>'
           '</polymer-element>'
           'FOO</body></html>',
+      'a|web/s1.dart': '',
     }, {
       'a|web/test.html':
           '<!DOCTYPE html><html><head>'
@@ -813,6 +818,4 @@
       'a|web/test2.css':
           'h1 { font-size: 70px; }',
     });
-
 }
-
diff --git a/pkg/polymer/test/build/script_compactor_test.dart b/pkg/polymer/test/build/script_compactor_test.dart
index 862ceb3..6c3119e 100644
--- a/pkg/polymer/test/build/script_compactor_test.dart
+++ b/pkg/polymer/test/build/script_compactor_test.dart
@@ -108,7 +108,7 @@
           }
           '''.replaceAll('\n          ', '\n'),
     });
-  
+
   testPhases('use const expressions', phases, {
       'a|web/test.html':
           '<!DOCTYPE html><html><head>',
@@ -163,9 +163,59 @@
           'class XFoo extends PolymerElement {\n'
           '}\n'
           'main(){}',
-    }, {}, [
+    }, {
+      'a|web/test.html_bootstrap.dart':
+          '''$MAIN_HEADER
+          import 'a.dart' as i0;
+          ${DEFAULT_IMPORTS.join('\n')}
+          import 'a.dart' as smoke_0;
+          import 'package:polymer/polymer.dart' as smoke_1;
+
+          void main() {
+            useGeneratedCode(new StaticConfiguration(
+                checkedMode: false,
+                parents: {
+                  smoke_0.XFoo: smoke_1.PolymerElement,
+                },
+                declarations: {
+                  smoke_0.XFoo: const {},
+                }));
+            startPolymer([]);
+          }
+          '''.replaceAll('\n          ', '\n'),
+
+    }, [
       'warning: The parameter to @CustomTag seems to be invalid. '
       '(web/a.dart 2 11)',
+      'warning: $NO_INITIALIZERS_ERROR',
+    ]);
+
+  testPhases('no polymer import (no warning, but no crash either)', phases, {
+      'a|web/test.html':
+          '<!DOCTYPE html><html><head>',
+      'a|web/test.html.scriptUrls': '[["a","web/a.dart"]]',
+      'a|web/a.dart':
+          'library a;\n'
+          'import "package:polymer/polymer.broken.import.dart";\n'
+          '@CustomTag("x-foo")\n'
+          'class XFoo extends PolymerElement {\n'
+          '}\n'
+          'main(){}',
+    }, {
+      'a|web/test.html_bootstrap.dart':
+          '''$MAIN_HEADER
+          import 'a.dart' as i0;
+          ${DEFAULT_IMPORTS.join('\n')}
+
+          void main() {
+            useGeneratedCode(new StaticConfiguration(
+                checkedMode: false));
+            startPolymer([]);
+          }
+          '''.replaceAll('\n          ', '\n'),
+
+    }, [
+      'warning: $NO_INITIALIZERS_ERROR',
     ]);
 
   testPhases('several scripts', phases, {
@@ -256,7 +306,7 @@
                   smoke_2.XF1: smoke_1.PolymerElement,
                   smoke_3.XG2: smoke_1.PolymerElement,
                   smoke_4.XH1: smoke_1.PolymerElement,
-                }, 
+                },
                 declarations: {
                   smoke_5.XC1: const {},
                   smoke_5.XC2: const {},
@@ -292,6 +342,8 @@
           '<polymer-element name="foo-bar"><template>'
           '<div>{{a.node}}</div>'
           '<div>{{anotherNode}}</div>'
+          '<div>{{a.call1(a)}}</div>'
+          '<div>{{call2(a)}}</div>'
           '<div class="{{an.attribute}}"></div>'
           '<a href="path/{{within.an.attribute}}/foo/bar"></a>'
           '<div data-attribute="{{anotherAttribute}}"></div>'
@@ -322,6 +374,8 @@
                   #anotherAttribute: (o) => o.anotherAttribute,
                   #anotherNode: (o) => o.anotherNode,
                   #attribute: (o) => o.attribute,
+                  #call1: (o) => o.call1,
+                  #call2: (o) => o.call2,
                   #here: (o) => o.here,
                   #intToStringTransformer: (o) => o.intToStringTransformer,
                   #is: (o) => o.is,
@@ -345,6 +399,8 @@
                   #anotherAttribute: r'anotherAttribute',
                   #anotherNode: r'anotherNode',
                   #attribute: r'attribute',
+                  #call1: r'call1',
+                  #call2: r'call2',
                   #here: r'here',
                   #intToStringTransformer: r'intToStringTransformer',
                   #is: r'is',
diff --git a/pkg/scheduled_test/CHANGELOG.md b/pkg/scheduled_test/CHANGELOG.md
index 86d33dd..7f095a1 100644
--- a/pkg/scheduled_test/CHANGELOG.md
+++ b/pkg/scheduled_test/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.11.0+1
+
+* Support `v0.5.0` of `shelf`
+
 ## 0.11.0
 
 * `ScheduledServer.handle` now takes a `shelf.Handler` rather than a custom
diff --git a/pkg/scheduled_test/pubspec.yaml b/pkg/scheduled_test/pubspec.yaml
index 12316a9..4066bfa 100644
--- a/pkg/scheduled_test/pubspec.yaml
+++ b/pkg/scheduled_test/pubspec.yaml
@@ -1,5 +1,5 @@
 name: scheduled_test
-version: 0.11.0
+version: 0.11.0+1
 author: Dart Team <misc@dartlang.org>
 homepage: http://www.dartlang.org
 description: >
@@ -12,7 +12,7 @@
 dependencies:
   http: '>=0.9.0 <0.11.0'
   path: '>=0.9.0 <2.0.0'
-  shelf: '>=0.4.0 <0.5.0'
+  shelf: '>=0.4.0 <0.6.0'
   stack_trace: '>=0.9.1 <0.10.0'
   unittest: '>=0.9.0 <0.11.0'
 environment:
diff --git a/pkg/scheduled_test/test/scheduled_server_test.dart b/pkg/scheduled_test/test/scheduled_server_test.dart
index 9b92820..53e4ac1 100644
--- a/pkg/scheduled_test/test/scheduled_server_test.dart
+++ b/pkg/scheduled_test/test/scheduled_server_test.dart
@@ -191,7 +191,7 @@
 
 /// Creates a metatest that runs [testBody], captures its schedule errors, and
 /// asserts that it throws an error with the given [errorMessage].
-void expectServerError(String description, void testBody(),
+void expectServerError(String description, testBody(),
     String errorMessage) {
   expectTestFails(description, testBody, (errors) {
     // There can be between one and three errors here. The first is the
diff --git a/pkg/scheduled_test/test/utils.dart b/pkg/scheduled_test/test/utils.dart
index 2d27b2d..11d04fe 100644
--- a/pkg/scheduled_test/test/utils.dart
+++ b/pkg/scheduled_test/test/utils.dart
@@ -62,7 +62,7 @@
 /// Creates a metatest with [body] and asserts that it passes.
 ///
 /// This is like [expectTestsPass], but the [test] is set up automatically.
-void expectTestPasses(String description, Future body()) =>
+void expectTestPasses(String description, body()) =>
   expectTestsPass(description, () => test('test', body));
 
 /// Creates a metatest that runs [testBody], captures its schedule errors, and
diff --git a/pkg/shelf/CHANGELOG.md b/pkg/shelf/CHANGELOG.md
index caefe02..a8ec616 100644
--- a/pkg/shelf/CHANGELOG.md
+++ b/pkg/shelf/CHANGELOG.md
@@ -1,3 +1,16 @@
+## 0.5.1
+
+* Add a `context` map to `Request` and `Response` for passing data among
+  handlers and middleware.
+
+## 0.5.0+1
+
+* Allow `scheduled_test` development dependency up to v0.12.0
+
+## 0.5.0
+
+* Renamed `Stack` to `Pipeline`.
+
 ## 0.4.0
 
 * Access to headers for `Request` and `Response` is now case-insensitive.
diff --git a/pkg/shelf/README.md b/pkg/shelf/README.md
index 9456481..7f527d0 100644
--- a/pkg/shelf/README.md
+++ b/pkg/shelf/README.md
@@ -20,7 +20,7 @@
 import 'package:shelf/shelf_io.dart' as io;
 
 void main() {
-  var handler = const shelf.Stack().addMiddleware(shelf.logRequests())
+  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
       .addHandler(_echoRequest);
 
   io.serve(handler, 'localhost', 8080).then((server) {
diff --git a/pkg/shelf/example/example_server.dart b/pkg/shelf/example/example_server.dart
index 49fda9e..ca0210b 100644
--- a/pkg/shelf/example/example_server.dart
+++ b/pkg/shelf/example/example_server.dart
@@ -6,7 +6,7 @@
 import 'package:shelf/shelf_io.dart' as io;
 
 void main() {
-  var handler = const shelf.Stack().addMiddleware(shelf.logRequests())
+  var handler = const shelf.Pipeline().addMiddleware(shelf.logRequests())
       .addHandler(_echoRequest);
 
   io.serve(handler, 'localhost', 8080).then((server) {
diff --git a/pkg/shelf/lib/shelf.dart b/pkg/shelf/lib/shelf.dart
index eb339c0..09ed47a 100644
--- a/pkg/shelf/lib/shelf.dart
+++ b/pkg/shelf/lib/shelf.dart
@@ -4,9 +4,9 @@
 
 library shelf;
 
+export 'src/handler.dart';
 export 'src/handlers/logger.dart';
 export 'src/middleware.dart';
+export 'src/pipeline.dart';
 export 'src/request.dart';
 export 'src/response.dart';
-export 'src/stack.dart';
-export 'src/handler.dart';
diff --git a/pkg/shelf/lib/src/message.dart b/pkg/shelf/lib/src/message.dart
index ad64d10..1b2e966 100644
--- a/pkg/shelf/lib/src/message.dart
+++ b/pkg/shelf/lib/src/message.dart
@@ -20,6 +20,19 @@
   /// The value is immutable.
   final Map<String, String> headers;
 
+  /// Extra context that can be used by for middleware and handlers.
+  ///
+  /// For requests, this is used to pass data to inner middleware and handlers;
+  /// for responses, it's used to pass data to outer middleware and handlers.
+  ///
+  /// Context properties that are used by a particular package should begin with
+  /// that package's name followed by a period. For example, if [logRequests]
+  /// wanted to take a prefix, its property name would be `"shelf.prefix"`,
+  /// since it's in the `shelf` package.
+  ///
+  /// The value is immutable.
+  final Map<String, Object> context;
+
   /// The streaming body of the message.
   ///
   /// This can be read via [read] or [readAsString].
@@ -28,8 +41,11 @@
   /// Creates a new [Message].
   ///
   /// If [headers] is `null`, it is treated as empty.
-  Message(this._body, {Map<String, String> headers})
-      : this.headers = _getIgnoreCaseMapView(headers);
+  Message(this._body, {Map<String, String> headers,
+        Map<String, Object> context})
+      : this.headers = _getIgnoreCaseMapView(headers),
+        this.context = new pc.UnmodifiableMapView(
+            context == null ? const {} : new Map.from(context));
 
   /// The contents of the content-length field in [headers].
   ///
diff --git a/pkg/shelf/lib/src/stack.dart b/pkg/shelf/lib/src/pipeline.dart
similarity index 66%
rename from pkg/shelf/lib/src/stack.dart
rename to pkg/shelf/lib/src/pipeline.dart
index 3aacf72..016e5fc 100644
--- a/pkg/shelf/lib/src/stack.dart
+++ b/pkg/shelf/lib/src/pipeline.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-library shelf.stack;
+library shelf.pipeline;
 
 import 'handler.dart';
 import 'middleware.dart';
@@ -10,36 +10,36 @@
 /// A helper that makes it easy to compose a set of [Middleware] and a
 /// [Handler].
 ///
-///     var handler = const Stack()
+///     var handler = const Pipeline()
 ///         .addMiddleware(loggingMiddleware)
 ///         .addMiddleware(cachingMiddleware)
 ///         .addHandler(application);
-class Stack {
-  final Stack _parent;
+class Pipeline {
+  final Pipeline _parent;
   final Middleware _middleware;
 
-  const Stack()
+  const Pipeline()
       : _middleware = null,
         _parent = null;
 
-  Stack._(this._middleware, this._parent);
+  Pipeline._(this._middleware, this._parent);
 
-  /// Returns a new [Stack] with [middleware] added to the existing set of
+  /// Returns a new [Pipeline] with [middleware] added to the existing set of
   /// [Middleware].
   ///
   /// [middleware] will be the last [Middleware] to process a request and
   /// the first to process a response.
-  Stack addMiddleware(Middleware middleware) =>
-      new Stack._(middleware, this);
+  Pipeline addMiddleware(Middleware middleware) =>
+      new Pipeline._(middleware, this);
 
   /// Returns a new [Handler] with [handler] as the final processor of a
-  /// [Request] if all of the middleware in the stack have passed the request
+  /// [Request] if all of the middleware in the pipeline have passed the request
   /// through.
   Handler addHandler(Handler handler) {
     if (_middleware == null) return handler;
     return _parent.addHandler(_middleware(handler));
   }
 
-  /// Exposes this stack of [Middleware] as a single middleware instance.
+  /// Exposes this pipeline of [Middleware] as a single middleware instance.
   Middleware get middleware => addHandler;
 }
diff --git a/pkg/shelf/lib/src/request.dart b/pkg/shelf/lib/src/request.dart
index 71c4e98..f7e011d 100644
--- a/pkg/shelf/lib/src/request.dart
+++ b/pkg/shelf/lib/src/request.dart
@@ -71,14 +71,14 @@
   // TODO(kevmoo) finish documenting the rest of the arguments.
   Request(this.method, Uri requestedUri, {String protocolVersion,
     Map<String, String> headers, Uri url, String scriptName,
-    Stream<List<int>> body})
+    Stream<List<int>> body, Map<String, Object> context})
       : this.requestedUri = requestedUri,
         this.protocolVersion = protocolVersion == null ?
             '1.1' : protocolVersion,
         this.url = _computeUrl(requestedUri, url, scriptName),
         this.scriptName = _computeScriptName(requestedUri, url, scriptName),
         super(body == null ? new Stream.fromIterable([]) : body,
-            headers: headers) {
+            headers: headers, context: context) {
     if (method.isEmpty) throw new ArgumentError('method cannot be empty.');
 
     // TODO(kevmoo) use isAbsolute property on Uri once Issue 18053 is fixed
diff --git a/pkg/shelf/lib/src/response.dart b/pkg/shelf/lib/src/response.dart
index bbc268b..e59b0c5 100644
--- a/pkg/shelf/lib/src/response.dart
+++ b/pkg/shelf/lib/src/response.dart
@@ -53,8 +53,10 @@
   /// If [encoding] is passed, the "encoding" field of the Content-Type header
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
-  Response.ok(body, {Map<String, String> headers, Encoding encoding})
-      : this(200, body: body, headers: headers, encoding: encoding);
+  Response.ok(body, {Map<String, String> headers, Encoding encoding, 
+    Map<String, Object> context})
+      : this(200, body: body, headers: headers, encoding: encoding,
+          context: context);
 
   /// Constructs a 301 Moved Permanently response.
   ///
@@ -71,8 +73,9 @@
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
   Response.movedPermanently(location, {body, Map<String, String> headers,
-      Encoding encoding})
-      : this._redirect(301, location, body, headers, encoding);
+      Encoding encoding, Map<String, Object> context})
+      : this._redirect(301, location, body, headers, encoding,
+          context: context);
 
   /// Constructs a 302 Found response.
   ///
@@ -89,8 +92,9 @@
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
   Response.found(location, {body, Map<String, String> headers,
-      Encoding encoding})
-      : this._redirect(302, location, body, headers, encoding);
+      Encoding encoding, Map<String, Object> context})
+      : this._redirect(302, location, body, headers, encoding,
+          context: context);
 
   /// Constructs a 303 See Other response.
   ///
@@ -108,17 +112,20 @@
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
   Response.seeOther(location, {body, Map<String, String> headers,
-      Encoding encoding})
-      : this._redirect(303, location, body, headers, encoding);
+      Encoding encoding, Map<String, Object> context})
+      : this._redirect(303, location, body, headers, encoding,
+          context: context);
 
   /// Constructs a helper constructor for redirect responses.
   Response._redirect(int statusCode, location, body,
-      Map<String, String> headers, Encoding encoding)
+      Map<String, String> headers, Encoding encoding, 
+      { Map<String, Object> context })
       : this(statusCode,
             body: body,
             encoding: encoding,
             headers: _addHeader(
-                headers, 'location', _locationToString(location)));
+                headers, 'location', _locationToString(location)),
+            context: context);
 
   /// Constructs a 304 Not Modified response.
   ///
@@ -126,9 +133,11 @@
   /// information used to determine whether the requested resource has changed
   /// since the last request. It indicates that the resource has not changed and
   /// the old value should be used.
-  Response.notModified({Map<String, String> headers})
+  Response.notModified({Map<String, String> headers, 
+    Map<String, Object> context})
       : this(304, headers: _addHeader(
-            headers, 'date', formatHttpDate(new DateTime.now())));
+            headers, 'date', formatHttpDate(new DateTime.now())),
+            context: context);
 
   /// Constructs a 403 Forbidden response.
   ///
@@ -143,8 +152,9 @@
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
   Response.forbidden(body, {Map<String, String> headers,
-      Encoding encoding})
-      : this(403, body: body, headers: headers);
+      Encoding encoding, Map<String, Object> context})
+      : this(403, body: body, headers: headers,
+          context: context);
 
   /// Constructs a 404 Not Found response.
   ///
@@ -159,8 +169,10 @@
   /// If [encoding] is passed, the "encoding" field of the Content-Type header
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
-  Response.notFound(body, {Map<String, String> headers, Encoding encoding})
-      : this(404, body: body, headers: headers);
+  Response.notFound(body, {Map<String, String> headers, Encoding encoding, 
+    Map<String, Object> context})
+      : this(404, body: body, headers: headers,
+          context: context);
 
   /// Constructs a 500 Internal Server Error response.
   ///
@@ -176,10 +188,11 @@
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
   Response.internalServerError({body, Map<String, String> headers,
-      Encoding encoding})
+      Encoding encoding, Map<String, Object> context})
       : this(500,
             headers: body == null ? _adjust500Headers(headers) : headers,
-            body: body == null ? 'Internal Server Error' : body);
+            body: body == null ? 'Internal Server Error' : body,
+            context: context);
 
   /// Constructs an HTTP response with the given [statusCode].
   ///
@@ -194,9 +207,10 @@
   /// in [headers] will be set appropriately. If there is no existing
   /// Content-Type header, it will be set to "application/octet-stream".
   Response(this.statusCode, {body, Map<String, String> headers,
-      Encoding encoding})
+      Encoding encoding, Map<String, Object> context})
       : super(_bodyToStream(body, encoding),
-          headers: _adjustHeaders(headers, encoding)) {
+          headers: _adjustHeaders(headers, encoding),
+          context: context) {
     if (statusCode < 100) {
       throw new ArgumentError("Invalid status code: $statusCode.");
     }
diff --git a/pkg/shelf/pubspec.yaml b/pkg/shelf/pubspec.yaml
index fcf337b..3c103d9 100644
--- a/pkg/shelf/pubspec.yaml
+++ b/pkg/shelf/pubspec.yaml
@@ -1,5 +1,5 @@
 name: shelf
-version: 0.4.0
+version: 0.5.1
 author: Dart Team <misc@dartlang.org>
 description: Web Server Middleware for Dart
 homepage: http://www.dartlang.org
@@ -13,5 +13,5 @@
   stack_trace: '>=0.9.2 <0.10.0'
 dev_dependencies:
   http: '>=0.9.2 <0.11.0'
-  scheduled_test: '>=0.10.0 <0.11.0'
+  scheduled_test: '>=0.10.0 <0.12.0'
   unittest: '>=0.10.0 <0.11.0'
diff --git a/pkg/shelf/test/create_middleware_test.dart b/pkg/shelf/test/create_middleware_test.dart
index 7ce99eb..5c35f4d 100644
--- a/pkg/shelf/test/create_middleware_test.dart
+++ b/pkg/shelf/test/create_middleware_test.dart
@@ -13,7 +13,7 @@
 
 void main() {
   test('forwards the request and response if both handlers are null', () {
-    var handler = const Stack()
+    var handler = const Pipeline()
         .addMiddleware(createMiddleware())
         .addHandler((request) {
           return syncHandler(request, headers: {'from' : 'innerHandler'});
@@ -26,7 +26,7 @@
 
   group('requestHandler', () {
     test('sync null response forwards to inner handler', () {
-      var handler = const Stack()
+      var handler = const Pipeline()
           .addMiddleware(createMiddleware(requestHandler: (request) => null))
           .addHandler(syncHandler);
 
@@ -36,7 +36,7 @@
     });
 
     test('async null response forwards to inner handler', () {
-      var handler = const Stack()
+      var handler = const Pipeline()
           .addMiddleware(createMiddleware(
               requestHandler: (request) => new Future.value(null)))
           .addHandler(syncHandler);
@@ -47,7 +47,7 @@
     });
 
     test('sync response is returned', () {
-      var handler = const Stack()
+      var handler = const Pipeline()
           .addMiddleware(createMiddleware(
               requestHandler: (request) => _middlewareResponse))
           .addHandler(_failHandler);
@@ -58,7 +58,7 @@
     });
 
     test('async response is returned', () {
-      var handler = const Stack()
+      var handler = const Pipeline()
           .addMiddleware(createMiddleware(requestHandler: (request) =>
               new Future.value(_middlewareResponse)))
           .addHandler(_failHandler);
@@ -74,7 +74,7 @@
             requestHandler: (request) => _middlewareResponse,
             responseHandler: (response) => fail('should not be called'));
 
-        var handler = const Stack()
+        var handler = const Pipeline()
             .addMiddleware(middleware)
             .addHandler(syncHandler);
 
@@ -87,7 +87,7 @@
         var middleware = createMiddleware(
             requestHandler: (request) => new Future.value(_middlewareResponse),
             responseHandler: (response) => fail('should not be called'));
-        var handler = const Stack()
+        var handler = const Pipeline()
             .addMiddleware(middleware)
             .addHandler(syncHandler);
 
@@ -100,7 +100,7 @@
 
   group('responseHandler', () {
     test('innerHandler sync response is seen, replaced value continues', () {
-      var handler = const Stack().addMiddleware(createMiddleware(
+      var handler = const Pipeline().addMiddleware(createMiddleware(
           responseHandler: (response) {
         expect(response.headers['from'], 'handler');
         return _middlewareResponse;
@@ -114,7 +114,7 @@
     });
 
     test('innerHandler async response is seen, async value continues', () {
-      var handler = const Stack().addMiddleware(
+      var handler = const Pipeline().addMiddleware(
           createMiddleware(responseHandler: (response) {
         expect(response.headers['from'], 'handler');
         return new Future.value(_middlewareResponse);
@@ -131,7 +131,7 @@
 
   group('error handling', () {
     test('sync error thrown by requestHandler bubbles down', () {
-      var handler = const Stack()
+      var handler = const Pipeline()
           .addMiddleware(createMiddleware(
               requestHandler: (request) => throw 'middleware error'))
           .addHandler(_failHandler);
@@ -140,7 +140,7 @@
     });
 
     test('async error thrown by requestHandler bubbles down', () {
-      var handler = const Stack()
+      var handler = const Pipeline()
           .addMiddleware(createMiddleware(requestHandler: (request) =>
               new Future.error('middleware error')))
           .addHandler(_failHandler);
@@ -153,7 +153,7 @@
         throw 'middleware error';
       }, errorHandler: (e, s) => fail('should never get here'));
 
-      var handler = const Stack().addMiddleware(middleware)
+      var handler = const Pipeline().addMiddleware(middleware)
           .addHandler(syncHandler);
 
       expect(makeSimpleRequest(handler), throwsA('middleware error'));
@@ -166,7 +166,7 @@
           },
           errorHandler: (e, s) => fail('should never get here'));
 
-      var handler = const Stack().addMiddleware(middleware)
+      var handler = const Pipeline().addMiddleware(middleware)
           .addHandler(syncHandler);
 
       expect(makeSimpleRequest(handler), throwsA('middleware error'));
@@ -179,7 +179,7 @@
             return _middlewareResponse;
           });
 
-      var handler = const Stack().addMiddleware(middleware)
+      var handler = const Pipeline().addMiddleware(middleware)
           .addHandler((request) {
         throw 'bad handler';
       });
@@ -195,7 +195,7 @@
         throw error;
       });
 
-      var handler = const Stack().addMiddleware(middleware)
+      var handler = const Pipeline().addMiddleware(middleware)
           .addHandler((request) {
         throw 'bad handler';
       });
diff --git a/pkg/shelf/test/log_middleware_test.dart b/pkg/shelf/test/log_middleware_test.dart
index d8da8fa..b9a8304 100644
--- a/pkg/shelf/test/log_middleware_test.dart
+++ b/pkg/shelf/test/log_middleware_test.dart
@@ -25,7 +25,7 @@
   };
 
   test('logs a request with a synchronous response', () {
-    var handler = const Stack()
+    var handler = const Pipeline()
         .addMiddleware(logRequests(logger: logger))
         .addHandler(syncHandler);
 
@@ -35,7 +35,7 @@
   });
 
   test('logs a request with an asynchronous response', () {
-    var handler = const Stack()
+    var handler = const Pipeline()
         .addMiddleware(logRequests(logger: logger))
         .addHandler(asyncHandler);
 
@@ -45,7 +45,7 @@
   });
 
   test('logs a request with an asynchronous response', () {
-    var handler = const Stack()
+    var handler = const Pipeline()
         .addMiddleware(logRequests(logger: (msg, isError) {
       expect(gotLog, isFalse);
       gotLog = true;
diff --git a/pkg/shelf/test/message_test.dart b/pkg/shelf/test/message_test.dart
index 57f32c4..1db286ba 100644
--- a/pkg/shelf/test/message_test.dart
+++ b/pkg/shelf/test/message_test.dart
@@ -11,13 +11,15 @@
 import 'package:unittest/unittest.dart';
 
 class _TestMessage extends Message {
-  _TestMessage(Map<String, String> headers, Stream<List<int>> body)
-      : super(body, headers: headers);
+  _TestMessage(Map<String, String> headers, Map<String, Object> context,
+        Stream<List<int>> body)
+      : super(body, headers: headers, context: context);
 }
 
-Message _createMessage({Map<String, String> headers, Stream<List<int>> body}) {
+Message _createMessage({Map<String, String> headers,
+    Map<String, Object> context, Stream<List<int>> body}) {
   if (body == null) body = new Stream.fromIterable([]);
-  return new _TestMessage(headers, body);
+  return new _TestMessage(headers, context, body);
 }
 
 void main() {
@@ -43,6 +45,26 @@
       expect(() => message.headers['h2'] = 'value2', throwsUnsupportedError);
     });
   });
+
+  group('context', () {
+    test('is accessible', () {
+      var message = _createMessage(context: {'foo': 'bar'});
+      expect(message.context, containsPair('foo', 'bar'));
+    });
+
+    test('null context value becomes empty and immutable', () {
+      var message = _createMessage();
+      expect(message.context, isEmpty);
+      expect(() => message.context['key'] = 'value', throwsUnsupportedError);
+    });
+
+    test('is immutable', () {
+      var message = _createMessage(context: {'key': 'value'});
+      expect(() => message.context['key'] = 'value', throwsUnsupportedError);
+      expect(() => message.context['key2'] = 'value', throwsUnsupportedError);
+    });
+  });
+
   group("readAsString", () {
     test("supports a null body", () {
       var request = _createMessage();
diff --git a/pkg/shelf/test/stack_test.dart b/pkg/shelf/test/pipeline_test.dart
similarity index 88%
rename from pkg/shelf/test/stack_test.dart
rename to pkg/shelf/test/pipeline_test.dart
index 8ff29d6..eda4a12 100644
--- a/pkg/shelf/test/stack_test.dart
+++ b/pkg/shelf/test/pipeline_test.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-library shelf.shelf_stack_test;
+library shelf.pipeline_test;
 
 import 'package:shelf/shelf.dart';
 import 'package:unittest/unittest.dart';
@@ -10,7 +10,7 @@
 import 'test_util.dart';
 
 void main() {
-  test('compose middleware with Stack', () {
+  test('compose middleware with Pipeline', () {
     int accessLocation = 0;
 
     var middlewareA = createMiddleware(requestHandler: (request) {
@@ -33,7 +33,7 @@
       return response;
     });
 
-    var handler = const Stack()
+    var handler = const Pipeline()
         .addMiddleware(middlewareA)
         .addMiddleware(middlewareB)
         .addHandler((request) {
@@ -48,7 +48,7 @@
     });
   });
 
-  test('Stack can be used as middleware', () {
+  test('Pipeline can be used as middleware', () {
     int accessLocation = 0;
 
     var middlewareA = createMiddleware(requestHandler: (request) {
@@ -71,12 +71,12 @@
       return response;
     });
 
-    var innerStack = const Stack()
+    var innerPipeline = const Pipeline()
         .addMiddleware(middlewareA)
         .addMiddleware(middlewareB);
 
-    var handler = const Stack()
-        .addMiddleware(innerStack.middleware)
+    var handler = const Pipeline()
+        .addMiddleware(innerPipeline.middleware)
         .addHandler((request) {
       expect(accessLocation, 2);
       accessLocation = 3;
diff --git a/pkg/smoke/pubspec.yaml b/pkg/smoke/pubspec.yaml
index 96c398d..435eee5 100644
--- a/pkg/smoke/pubspec.yaml
+++ b/pkg/smoke/pubspec.yaml
@@ -1,5 +1,5 @@
 name: smoke
-version: 0.1.0-pre.3.dev
+version: 0.1.0-pre.3
 author: Polymer.dart Authors <web-ui-dev@dartlang.org>
 homepage: "https://api.dartlang.org/apidocs/channels/be/#smoke"
 description: >
diff --git a/pkg/stack_trace/test/chain_test.dart b/pkg/stack_trace/test/chain_test.dart
index 683544f..3347deb 100644
--- a/pkg/stack_trace/test/chain_test.dart
+++ b/pkg/stack_trace/test/chain_test.dart
@@ -92,16 +92,20 @@
         inMicrotask(() => throw 'first error');
         inPeriodicTimer(() => throw 'second error');
       }, onError: (error, chain) {
-        if (first) {
-          expect(error, equals('first error'));
-          expect(chain.traces[1].frames,
-              contains(frameMember(startsWith('inMicrotask'))));
-          first = false;
-        } else {
-          expect(error, equals('second error'));
-          expect(chain.traces[1].frames,
-              contains(frameMember(startsWith('inPeriodicTimer'))));
-          completer.complete();
+        try {
+          if (first) {
+            expect(error, equals('first error'));
+            expect(chain.traces[1].frames,
+                contains(frameMember(startsWith('inMicrotask'))));
+            first = false;
+          } else {
+            expect(error, equals('second error'));
+            expect(chain.traces[1].frames,
+                contains(frameMember(startsWith('inPeriodicTimer'))));
+            completer.complete();
+          }
+        } catch (error, stackTrace) {
+          completer.completeError(error, stackTrace);
         }
       });
 
@@ -121,11 +125,15 @@
           throw error;
         });
       }, onError: (error, chain) {
-        expect(error, equals('error'));
-        expect(chain, new isInstanceOf<Chain>());
-        expect(chain.traces[1].frames,
-            contains(frameMember(startsWith('inMicrotask'))));
-        completer.complete();
+        try {
+          expect(error, equals('error'));
+          expect(chain, new isInstanceOf<Chain>());
+          expect(chain.traces[1].frames,
+              contains(frameMember(startsWith('inMicrotask'))));
+          completer.complete();
+        } catch (error, stackTrace) {
+          completer.completeError(error, stackTrace);
+        }
       });
 
       return completer.future;
@@ -138,11 +146,15 @@
     runZoned(() {
       Chain.capture(() => inMicrotask(() => throw 'error'));
     }, onError: (error, chain) {
-      expect(error, equals('error'));
-      expect(chain, new isInstanceOf<Chain>());
-      expect(chain.traces[1].frames,
-          contains(frameMember(startsWith('inMicrotask'))));
-      completer.complete();
+      try {
+        expect(error, equals('error'));
+        expect(chain, new isInstanceOf<Chain>());
+        expect(chain.traces[1].frames,
+            contains(frameMember(startsWith('inMicrotask'))));
+        completer.complete();
+      } catch (error, stackTrace) {
+        completer.completeError(error, stackTrace);
+      }
     });
 
     return completer.future;
diff --git a/pkg/template_binding/test/custom_element_bindings_test.html b/pkg/template_binding/test/custom_element_bindings_test.html
index 45bdb99..cbaa772 100644
--- a/pkg/template_binding/test/custom_element_bindings_test.html
+++ b/pkg/template_binding/test/custom_element_bindings_test.html
@@ -17,8 +17,6 @@
   <h1> Running custom_element_bindings_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/runtime/bin/builtin.cc b/runtime/bin/builtin.cc
index 2486c5b..9927ee8 100644
--- a/runtime/bin/builtin.cc
+++ b/runtime/bin/builtin.cc
@@ -96,7 +96,8 @@
     library = Dart_LoadLibrary(url, Source(id));
     if (!Dart_IsError(library) && (builtin_libraries_[id].has_natives_)) {
       // Setup the native resolver for built in library functions.
-      DART_CHECK_VALID(Dart_SetNativeResolver(library, NativeLookup));
+      DART_CHECK_VALID(
+          Dart_SetNativeResolver(library, NativeLookup, NativeSymbol));
     }
     if (builtin_libraries_[id].patch_url_ != NULL) {
       ASSERT(builtin_libraries_[id].patch_paths_ != NULL);
diff --git a/runtime/bin/builtin.h b/runtime/bin/builtin.h
index a4d8a13..6e03854 100644
--- a/runtime/bin/builtin.h
+++ b/runtime/bin/builtin.h
@@ -57,6 +57,8 @@
                                           int argument_count,
                                           bool* auto_setup_scope);
 
+  static const uint8_t* NativeSymbol(Dart_NativeFunction nf);
+
   static const char* builtin_source_paths_[];
   static const char* io_source_paths_[];
   static const char* io_patch_paths_[];
diff --git a/runtime/bin/builtin_gen_snapshot.cc b/runtime/bin/builtin_gen_snapshot.cc
index 965a862..3e25004 100644
--- a/runtime/bin/builtin_gen_snapshot.cc
+++ b/runtime/bin/builtin_gen_snapshot.cc
@@ -49,6 +49,18 @@
 }
 
 
+const uint8_t* Builtin::NativeSymbol(Dart_NativeFunction nf) {
+  int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries);
+  for (int i = 0; i < num_entries; i++) {
+    struct NativeEntries* entry = &(BuiltinEntries[i]);
+    if (reinterpret_cast<Dart_NativeFunction>(entry->function_) == nf) {
+      return reinterpret_cast<const uint8_t*>(entry->name_);
+    }
+  }
+  return NULL;
+}
+
+
 // Implementation of native functions which are used for some
 // test/debug functionality in standalone dart mode.
 void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) {
diff --git a/runtime/bin/builtin_natives.cc b/runtime/bin/builtin_natives.cc
index 9201f5c..858aa27 100644
--- a/runtime/bin/builtin_natives.cc
+++ b/runtime/bin/builtin_natives.cc
@@ -99,6 +99,18 @@
 }
 
 
+const uint8_t* Builtin::NativeSymbol(Dart_NativeFunction nf) {
+  int num_entries = sizeof(BuiltinEntries) / sizeof(struct NativeEntries);
+  for (int i = 0; i < num_entries; i++) {
+    struct NativeEntries* entry = &(BuiltinEntries[i]);
+    if (reinterpret_cast<Dart_NativeFunction>(entry->function_) == nf) {
+      return reinterpret_cast<const uint8_t*>(entry->name_);
+    }
+  }
+  return IONativeSymbol(nf);
+}
+
+
 // Implementation of native functions which are used for some
 // test/debug functionality in standalone dart mode.
 void FUNCTION_NAME(Logger_PrintString)(Dart_NativeArguments args) {
diff --git a/runtime/bin/builtin_nolib.cc b/runtime/bin/builtin_nolib.cc
index b19dfd3..e4ad4ce7 100644
--- a/runtime/bin/builtin_nolib.cc
+++ b/runtime/bin/builtin_nolib.cc
@@ -47,7 +47,8 @@
     Dart_Handle library = Dart_LookupLibrary(url);
     ASSERT(!Dart_IsError(library));
     // Setup the native resolver for built in library functions.
-    DART_CHECK_VALID(Dart_SetNativeResolver(library, NativeLookup));
+    DART_CHECK_VALID(
+        Dart_SetNativeResolver(library, NativeLookup, NativeSymbol));
   }
 }
 
diff --git a/runtime/bin/dbg_message.cc b/runtime/bin/dbg_message.cc
index d47474f..34f4271 100644
--- a/runtime/bin/dbg_message.cc
+++ b/runtime/bin/dbg_message.cc
@@ -473,8 +473,9 @@
     res = Dart_GetActivationFrame(trace, i, &frame);
     ASSERT_NOT_ERROR(res);
     Dart_Handle func_name;
+    Dart_Handle func;
     Dart_CodeLocation location;
-    res = Dart_ActivationFrameGetLocation(frame, &func_name, NULL, &location);
+    res = Dart_ActivationFrameGetLocation(frame, &func_name, &func, &location);
     ASSERT_NOT_ERROR(res);
     ASSERT(Dart_IsString(func_name));
     msg->Printf("%s{\"functionName\":", (i > 0) ? "," : "");
@@ -486,6 +487,13 @@
       msg->Printf(",\"libraryId\":%d,", location.library_id);
       msg->Printf("\"tokenOffset\":%d}", location.token_pos);
     }
+    ASSERT_NOT_ERROR(func);
+    Dart_Handle origin = Dart_GetFunctionOrigin(func);
+    ASSERT_NOT_ERROR(origin);
+    if (Dart_IsInteger(origin)) {
+      int64_t class_id = GetIntValue(origin);
+      msg->Printf(",\"classId\":%" Pd64 "", class_id);
+    }
     Dart_Handle locals = Dart_GetLocalVariables(frame);
     ASSERT_NOT_ERROR(locals);
     msg->Printf(",\"locals\":");
diff --git a/runtime/bin/directory_win.cc b/runtime/bin/directory_win.cc
index eefdd58..ecfa7e9 100644
--- a/runtime/bin/directory_win.cc
+++ b/runtime/bin/directory_win.cc
@@ -310,12 +310,15 @@
     return false;
   }
 
-  bool success = DeleteEntry(&find_file_data, path);
-
-  while ((FindNextFileW(find_handle, &find_file_data) != 0) && success) {
+  do {
+    if (!DeleteEntry(&find_file_data, path)) {
+      DWORD last_error = GetLastError();
+      FindClose(find_handle);
+      SetLastError(last_error);
+      return false;
+    }
     path->Reset(path_length);  // DeleteEntry adds to the path.
-    success = success && DeleteEntry(&find_file_data, path);
-  }
+  } while (FindNextFileW(find_handle, &find_file_data) != 0);
 
   path->Reset(path_length - 1);  // Drop the "\" from the end of the path.
   if ((GetLastError() != ERROR_NO_MORE_FILES) ||
@@ -324,7 +327,7 @@
     return false;
   }
 
-  return success;
+  return true;
 }
 
 
diff --git a/runtime/bin/file_android.cc b/runtime/bin/file_android.cc
index fb3a616..0b099ba 100644
--- a/runtime/bin/file_android.cc
+++ b/runtime/bin/file_android.cc
@@ -266,10 +266,10 @@
         }
       }
     }
+    int e = errno;
+    VOID_TEMP_FAILURE_RETRY(close(old_fd));
+    VOID_TEMP_FAILURE_RETRY(close(new_fd));
     if (result < 0) {
-      int e = errno;
-      VOID_TEMP_FAILURE_RETRY(close(old_fd));
-      VOID_TEMP_FAILURE_RETRY(close(new_fd));
       VOID_NO_RETRY_EXPECTED(unlink(new_path));
       errno = e;
       return false;
diff --git a/runtime/bin/file_linux.cc b/runtime/bin/file_linux.cc
index 92c5637..4753a8b 100644
--- a/runtime/bin/file_linux.cc
+++ b/runtime/bin/file_linux.cc
@@ -265,10 +265,10 @@
         }
       }
     }
+    int e = errno;
+    VOID_TEMP_FAILURE_RETRY(close(old_fd));
+    VOID_TEMP_FAILURE_RETRY(close(new_fd));
     if (result < 0) {
-      int e = errno;
-      VOID_TEMP_FAILURE_RETRY(close(old_fd));
-      VOID_TEMP_FAILURE_RETRY(close(new_fd));
       VOID_NO_RETRY_EXPECTED(unlink(new_path));
       errno = e;
       return false;
diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc
index 86d7c2d..bd8d498 100644
--- a/runtime/bin/io_natives.cc
+++ b/runtime/bin/io_natives.cc
@@ -124,5 +124,17 @@
   return NULL;
 }
 
+
+const uint8_t* IONativeSymbol(Dart_NativeFunction nf) {
+  int num_entries = sizeof(IOEntries) / sizeof(struct NativeEntries);
+  for (int i = 0; i < num_entries; i++) {
+    struct NativeEntries* entry = &(IOEntries[i]);
+    if (reinterpret_cast<Dart_NativeFunction>(entry->function_) == nf) {
+      return reinterpret_cast<const uint8_t*>(entry->name_);
+    }
+  }
+  return NULL;
+}
+
 }  // namespace bin
 }  // namespace dart
diff --git a/runtime/bin/io_natives.h b/runtime/bin/io_natives.h
index 7039234..d44f839 100644
--- a/runtime/bin/io_natives.h
+++ b/runtime/bin/io_natives.h
@@ -15,6 +15,8 @@
                                    int argument_count,
                                    bool* auto_setup_scope);
 
+const uint8_t* IONativeSymbol(Dart_NativeFunction nf);
+
 }  // namespace bin
 }  // namespace dart
 
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 62fea29..d1eea46 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -941,6 +941,7 @@
   // Start event handler.
   EventHandler::Start();
 
+  ASSERT(Dart_CurrentIsolate() == NULL);
   // Start the VM service isolate, if necessary.
   if (start_vm_service) {
     ASSERT(vm_service_server_port >= 0);
@@ -952,6 +953,7 @@
     Dart_RegisterIsolateServiceRequestCallback(
         "io", &ServiceRequestHandler, NULL);
   }
+  ASSERT(Dart_CurrentIsolate() == NULL);
 
   // Call CreateIsolateAndSetup which creates an isolate and loads up
   // the specified application script.
diff --git a/runtime/bin/process_patch.dart b/runtime/bin/process_patch.dart
index a68b7e5..0f5f8ac 100644
--- a/runtime/bin/process_patch.dart
+++ b/runtime/bin/process_patch.dart
@@ -181,7 +181,7 @@
         throw new ArgumentError("Non-string argument: $arg");
       }
       _arguments[i] = arguments[i];
-      if (Platform.operatingSystem == 'windows') {
+      if (Platform.isWindows) {
         _arguments[i] = _windowsArgumentEscape(_arguments[i]);
       }
     }
@@ -192,21 +192,27 @@
     }
 
     _environment = [];
-    var environmentEntryHandler = (key, value) {
+    // Ensure that we have a non-null environment.
+    environment = (environment == null) ? (const {}) : environment;
+    if (environment is !Map) {
+      throw new ArgumentError("Environment is not a map: $environment");
+    }
+    environment.forEach((key, value) {
       if (key is !String || value is !String) {
         throw new ArgumentError(
         "Environment key or value is not a string: ($key, $value)");
       }
       _environment.add('$key=$value');
-    };
-    if (environment != null) {
-      if (environment is !Map) {
-        throw new ArgumentError("Environment is not a map: $environment");
-      }
-      environment.forEach(environmentEntryHandler);
-    }
+    });
     if (includeParentEnvironment) {
-      Platform.environment.forEach(environmentEntryHandler);
+      Platform.environment.forEach((key, value) {
+        assert(key is String);
+        assert(value is String);
+        // Do not override keys already set as part of environment.
+        if (!environment.containsKey(key)) {
+          _environment.add('$key=$value');
+        }
+      });
     }
 
     // stdin going to process.
@@ -221,7 +227,7 @@
   }
 
   static String _getShellCommand() {
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       return 'cmd.exe';
     }
     return '/bin/sh';
@@ -230,7 +236,7 @@
   static List<String> _getShellArguments(String executable,
                                          List<String> arguments) {
     List<String> shellArguments = [];
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       shellArguments.add('/c');
       shellArguments.add(executable);
       for (var arg in arguments) {
@@ -320,7 +326,6 @@
                                   _stderr._stream._nativeSocket,
                                   _exitHandler._nativeSocket,
                                   status);
-      _environment = null;  // The environment will not be needed going forward.
       if (!success) {
         completer.completeError(
             new ProcessException(_path,
@@ -329,6 +334,12 @@
                                  status._errorCode));
         return;
       }
+      // Reset values which are no longer needed.
+      _path = null;
+      _arguments = null;
+      _workingDirectory = null;
+      _environment = null;
+
       _started = true;
 
       // Setup an exit handler to handle internal cleanup and possible
@@ -376,13 +387,17 @@
                                 _stderr._stream._nativeSocket,
                                 _exitHandler._nativeSocket,
                                 status);
-    _environment = null;  // The environment will not be needed going forward.
     if (!success) {
       throw new ProcessException(_path,
                                  _arguments,
                                  status._errorMessage,
                                  status._errorCode);
     }
+    // Reset values which are no longer needed.
+    _path = null;
+    _arguments = null;
+    _workingDirectory = null;
+    _environment = null;
 
     var result = _wait(
         _stdin._sink._nativeSocket,
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart
index b46bf5e..f31975e 100644
--- a/runtime/bin/socket_patch.dart
+++ b/runtime/bin/socket_patch.dart
@@ -1068,7 +1068,15 @@
         .then((socket) => new _RawServerSocket(socket));
   }
 
-  _RawServerSocket(this._socket) {
+  _RawServerSocket(this._socket);
+
+  StreamSubscription<RawSocket> listen(void onData(RawSocket event),
+                                       {Function onError,
+                                        void onDone(),
+                                        bool cancelOnError}) {
+    if (_controller != null) {
+      throw new StateError("Stream was already listened to");
+    }
     var zone = Zone.current;
     _controller = new StreamController(sync: true,
         onListen: _onSubscriptionStateChange,
@@ -1087,14 +1095,7 @@
         _controller.addError(e);
         _controller.close();
       }),
-      destroyed: _controller.close
-    );
-  }
-
-  StreamSubscription<RawSocket> listen(void onData(RawSocket event),
-                                       {Function onError,
-                                        void onDone(),
-                                        bool cancelOnError}) {
+      destroyed: _controller.close);
     return _controller.stream.listen(
         onData,
         onError: onError,
diff --git a/runtime/bin/socket_win.cc b/runtime/bin/socket_win.cc
index da82b27..7f1de2d 100644
--- a/runtime/bin/socket_win.cc
+++ b/runtime/bin/socket_win.cc
@@ -233,11 +233,17 @@
   memset(&hints, 0, sizeof(hints));
   hints.ai_family = SocketAddress::FromType(type);
   hints.ai_socktype = SOCK_STREAM;
-  hints.ai_flags = 0;
+  hints.ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG);
   hints.ai_protocol = IPPROTO_TCP;
   struct addrinfo* info = NULL;
   int status = getaddrinfo(host, 0, &hints, &info);
   if (status != 0) {
+    // We failed, try without AI_ADDRCONFIG. This can happen when looking up
+    // e.g. '::1', when there are no global IPv6 addresses.
+    hints.ai_flags = AI_V4MAPPED;
+    status = getaddrinfo(host, 0, &hints, &info);
+  }
+  if (status != 0) {
     ASSERT(*os_error == NULL);
     DWORD error_code = WSAGetLastError();
     SetLastError(error_code);
diff --git a/runtime/bin/test_extension.c b/runtime/bin/test_extension.c
index 82387a2..d0c50c5 100644
--- a/runtime/bin/test_extension.c
+++ b/runtime/bin/test_extension.c
@@ -64,7 +64,7 @@
     return parent_library;
   }
 
-  result_code = Dart_SetNativeResolver(parent_library, ResolveName);
+  result_code = Dart_SetNativeResolver(parent_library, ResolveName, NULL);
   if (Dart_IsError(result_code)) {
     return result_code;
   }
diff --git a/runtime/bin/vmservice/client/lib/src/elements/vm_view.html b/runtime/bin/vmservice/client/lib/src/elements/vm_view.html
index bfd4965..253f434 100644
--- a/runtime/bin/vmservice/client/lib/src/elements/vm_view.html
+++ b/runtime/bin/vmservice/client/lib/src/elements/vm_view.html
@@ -52,6 +52,14 @@
           <div class="memberName">uptime</div>
           <div class="memberValue">{{ vm.uptime | formatTime }}</div>
         </div>
+        <div class="memberItem">
+          <div class="memberName">type checks enabled</div>
+          <div class="memberValue">{{ vm.typeChecksEnabled }}</div>
+        </div>
+        <div class="memberItem">
+          <div class="memberName">asserts enabled</div>
+          <div class="memberValue">{{ vm.assertsEnabled }}</div>
+        </div>
       </div>
     </div>
 
diff --git a/runtime/bin/vmservice/client/lib/src/service/object.dart b/runtime/bin/vmservice/client/lib/src/service/object.dart
index 9024d49..d7d595b 100644
--- a/runtime/bin/vmservice/client/lib/src/service/object.dart
+++ b/runtime/bin/vmservice/client/lib/src/service/object.dart
@@ -177,6 +177,8 @@
   @observable String version = 'unknown';
   @observable String architecture = 'unknown';
   @observable double uptime = 0.0;
+  @observable bool assertsEnabled = false;
+  @observable bool typeChecksEnabled = false;
 
   VM() : super._empty(null) {
     name = 'vm';
@@ -339,6 +341,8 @@
     version = map['version'];
     architecture = map['architecture'];
     uptime = map['uptime'];
+    assertsEnabled = map['assertsEnabled'];
+    typeChecksEnabled = map['typeChecksEnabled'];
     _updateIsolates(map['isolates']);
   }
 
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index 9046ec8..a91a88c 100755
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -527,26 +527,85 @@
 DART_EXPORT bool Dart_IsPrologueWeakPersistentHandle(
     Dart_WeakPersistentHandle object);
 
+typedef struct _Dart_WeakReferenceSetBuilder* Dart_WeakReferenceSetBuilder;
+typedef struct _Dart_WeakReferenceSet* Dart_WeakReferenceSet;
+
+/**
+ * Constructs a weak references set builder.
+ *
+ * \returns a pointer to the weak reference set builder if successful.
+ *   Otherwise, returns NULL.
+ */
+DART_EXPORT Dart_WeakReferenceSetBuilder Dart_NewWeakReferenceSetBuilder();
+
 /**
  * Constructs a set of weak references from the Cartesian product of
  * the objects in the key set and the objects in values set.
  *
- * \param keys A set of object references.  These references will be
+ * \param set_builder The weak references set builder which was created
+ *   using Dart_NewWeakReferenceSetBuilder().
+ * \param key An object reference.  This references will be
  *   considered weak by the garbage collector.
- * \param num_keys the number of objects in the keys set.
- * \param values A set of object references.  These references will be
+ * \param value An object reference.  This reference will be
  *   considered weak by garbage collector unless any object reference
  *   in 'keys' is found to be strong.
- * \param num_values the size of the values set
  *
- * \return Success if the weak reference set could be created.
- *   Otherwise, returns an error handle.
+ * \return a pointer to the weak reference set if successful.
+ *   Otherwise, returns NULL.
  */
-DART_EXPORT Dart_Handle Dart_NewWeakReferenceSet(
-    Dart_WeakPersistentHandle* keys,
-    intptr_t num_keys,
-    Dart_WeakPersistentHandle* values,
-    intptr_t num_values);
+DART_EXPORT Dart_WeakReferenceSet Dart_NewWeakReferenceSet(
+    Dart_WeakReferenceSetBuilder set_builder,
+    Dart_WeakPersistentHandle key,
+    Dart_WeakPersistentHandle value);
+
+/**
+ * Append the pair of key/value object references to the weak references set.
+ *
+ * \param reference_set A weak references set into which the pair of key/value
+ *   needs to be added.
+ * \param key An object reference.  This references will be
+ *   considered weak by the garbage collector.
+ * \param value An object reference.  This reference will be
+ *   considered weak by garbage collector unless any object reference
+ *   in 'keys' is found to be strong.
+ *
+ * \return Success if the prologue weak persistent handle was created.
+ *   Otherwise, returns an error.
+ */
+DART_EXPORT Dart_Handle Dart_AppendToWeakReferenceSet(
+    Dart_WeakReferenceSet reference_set,
+    Dart_WeakPersistentHandle key,
+    Dart_WeakPersistentHandle value);
+
+/**
+ * Append the key object reference to the weak references set.
+ *
+ * \param reference_set A weak references set into which the key
+ *   needs to be added.
+ * \param key An object reference.  This references will be
+ *   considered weak by the garbage collector.
+ *
+ * \return Success if the prologue weak persistent handle was created.
+ *   Otherwise, returns an error.
+ */
+DART_EXPORT Dart_Handle Dart_AppendKeyToWeakReferenceSet(
+    Dart_WeakReferenceSet reference_set,
+    Dart_WeakPersistentHandle key);
+
+/**
+ * Append the value object reference to the weak references set.
+ *
+ * \param reference_set A weak references set into which the key
+ *   needs to be added.
+ * \param value An object reference.  This references will be
+ *   considered weak by the garbage collector.
+ *
+ * \return Success if the prologue weak persistent handle was created.
+ *   Otherwise, returns an error.
+ */
+DART_EXPORT Dart_Handle Dart_AppendValueToWeakReferenceSet(
+    Dart_WeakReferenceSet reference_set,
+    Dart_WeakPersistentHandle value);
 
 
 /*
@@ -592,6 +651,14 @@
     Dart_GcPrologueCallback prologue_callback,
     Dart_GcEpilogueCallback epilogue_callback);
 
+typedef void (*Dart_GcPrologueWeakHandleCallback)(void* isolate_callback_data,
+                                                  Dart_WeakPersistentHandle obj,
+                                                  intptr_t num_native_fields,
+                                                  intptr_t* native_fields);
+
+DART_EXPORT Dart_Handle Dart_VisitPrologueWeakHandles(
+    Dart_GcPrologueWeakHandleCallback callback);
+
 /*
  * ==========================
  * Initialization and Globals
@@ -2212,6 +2279,24 @@
 /* TODO(turnidge): Consider renaming to NativeFunctionResolver or
  * NativeResolver. */
 
+/**
+ * Native entry symbol lookup callback.
+ *
+ * For libraries and scripts which have native functions, the embedder
+ * can provide a callback for mapping a native entry to a symbol. This callback
+ * maps a native function entry PC to the native function name. If no native
+ * entry symbol can be found, the callback should return NULL.
+ *
+ * The parameters to the native reverse resolver function are:
+ * \param nf A Dart_NativeFunction.
+ *
+ * \return A const UTF-8 string containing the symbol name or NULL.
+ *
+ * See Dart_SetNativeResolver.
+ */
+typedef const uint8_t* (*Dart_NativeEntrySymbol)(Dart_NativeFunction nf);
+
+
 /*
  * ===========
  * Environment
@@ -2248,7 +2333,8 @@
  */
 DART_EXPORT Dart_Handle Dart_SetNativeResolver(
     Dart_Handle library,
-    Dart_NativeEntryResolver resolver);
+    Dart_NativeEntryResolver resolver,
+    Dart_NativeEntrySymbol symbol);
 /* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */
 
 
diff --git a/runtime/include/dart_debugger_api.h b/runtime/include/dart_debugger_api.h
index e694174..5e83862 100755
--- a/runtime/include/dart_debugger_api.h
+++ b/runtime/include/dart_debugger_api.h
@@ -479,6 +479,18 @@
 
 
 /**
+ * Returns origin class of a function.
+ *
+ * Requires there to be a current isolate.
+ * 
+ * \return Returns the class id (a handle to an integer) of the class in
+ * which \function is defined. Returns a null handle if \function is defined
+ * at the top level. Returns an error object otherwise.
+ */
+DART_EXPORT Dart_Handle Dart_GetFunctionOrigin(Dart_Handle function);
+
+
+/**
  * Returns an array containing all the global variable names and values of
  * the library with given \library_id.
  *
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index 5c9f7d8..3ed280b 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -294,13 +294,13 @@
     0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39
   ];
 
-  // Powers of 10 above 1000000 are indistinguishable.
+  // Powers of 10 above 1000000 are indistinguishable by eye.
   static const int _POW_10_7  = 10000000;
   static const int _POW_10_8  = 100000000;
   static const int _POW_10_9  = 1000000000;
-  static const int _POW_10_10 = 10000000000;
 
   // Find the number of decimal digits in a positive smi.
+  // Never called with numbers < 100. These are handled before calling.
   static int _positiveBase10Length(var smi) {
     // A positive smi has length <= 19 if 63-bit,  <=10 if 31-bit.
     // Avoid comparing a 31-bit smi to a non-smi.
@@ -311,15 +311,13 @@
       if (smi < 1000000) return 6;
       return 7;
     }
-    if (smi < _POW_10_10) {
-      if (smi < _POW_10_8) return 8;
-      if (smi < _POW_10_9) return 9;
-      return 10;
-    }
-    smi = smi ~/ _POW_10_10;
-    if (smi < 10) return 11;
-    if (smi < 100) return 12;
-    return 10 + _positiveBase10Length(smi);
+    if (smi < _POW_10_8) return 8;
+    if (smi < _POW_10_9) return 9;
+    smi = smi ~/ _POW_10_9;
+    // Handle numbers < 100 before calling recursively.
+    if (smi < 10) return 10;
+    if (smi < 100) return 11;
+    return 9 + _positiveBase10Length(smi);
   }
 
   String toString() {
@@ -363,6 +361,7 @@
   }
 
   // Find the number of decimal digits in a negative smi.
+  // Never called with numbers > -100. These are handled before calling.
   static int _negativeBase10Length(var negSmi) {
     // A negative smi has length <= 19 if 63-bit, <=10 if 31-bit.
     // Avoid comparing a 31-bit smi to a non-smi.
@@ -373,15 +372,13 @@
       if (negSmi > -1000000) return 6;
       return 7;
     }
-    if (negSmi > -_POW_10_10) {
-      if (negSmi > -_POW_10_8) return 8;
-      if (negSmi > -_POW_10_9) return 9;
-      return 10;
-    }
-    negSmi = negSmi ~/ _POW_10_10;
-    if (negSmi > -10) return 11;
-    if (negSmi > -100) return 12;
-    return 10 + _negativeBase10Length(negSmi);
+    if (negSmi > -_POW_10_8) return 8;
+    if (negSmi > -_POW_10_9) return 9;
+    negSmi = negSmi ~/ _POW_10_9;
+    // Handle numbers > -100 before calling recursively.
+    if (negSmi > -10) return 10;
+    if (negSmi > -100) return 11;
+    return 9 + _negativeBase10Length(negSmi);
   }
 
   // Convert a negative smi to a string.
diff --git a/runtime/lib/math_patch.dart b/runtime/lib/math_patch.dart
index 279a549..29e8014 100644
--- a/runtime/lib/math_patch.dart
+++ b/runtime/lib/math_patch.dart
@@ -20,10 +20,17 @@
     return 1.0;  // ECMA-262 15.8.2.13
   }
   if (base == 1.0) return 1.0;
+
   if (base.isNaN || exponent.isNaN) {
     return double.NAN;
   }
-  return _pow(base, exponent);
+  if ((base != -double.INFINITY) && (exponent == 0.5)) {
+    if (base == 0.0) {
+      return 0.0;
+    }
+    return sqrt(base);
+  }
+  return _pow(base.toDouble(), exponent.toDouble());
 }
 
 double _pow(double base, double exponent) native "Math_doublePow";
diff --git a/runtime/lib/profiler.cc b/runtime/lib/profiler.cc
new file mode 100644
index 0000000..75bf2d1
--- /dev/null
+++ b/runtime/lib/profiler.cc
@@ -0,0 +1,63 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "vm/bootstrap_natives.h"
+
+#include "include/dart_api.h"
+
+#include "vm/bigint_operations.h"
+#include "vm/exceptions.h"
+#include "vm/native_entry.h"
+#include "vm/object.h"
+#include "vm/object_store.h"
+
+namespace dart {
+
+DECLARE_FLAG(bool, trace_intrinsified_natives);
+
+// dart:profiler.
+
+DEFINE_NATIVE_ENTRY(UserTag_new, 2) {
+  ASSERT(TypeArguments::CheckedHandle(arguments->NativeArgAt(0)).IsNull());
+  GET_NON_NULL_NATIVE_ARGUMENT(String, tag_label, arguments->NativeArgAt(1));
+  return UserTag::New(tag_label);
+}
+
+
+DEFINE_NATIVE_ENTRY(UserTag_label, 1) {
+  const UserTag& self = UserTag::CheckedHandle(arguments->NativeArgAt(0));
+  return self.label();
+}
+
+
+DEFINE_NATIVE_ENTRY(UserTag_makeCurrent, 1) {
+  const UserTag& self = UserTag::CheckedHandle(arguments->NativeArgAt(0));
+  if (FLAG_trace_intrinsified_natives) {
+    OS::Print("UserTag_makeCurrent: %s\n", self.ToCString());
+  }
+  self.MakeActive();
+  return Object::null();
+}
+
+
+DEFINE_NATIVE_ENTRY(Profiler_getCurrentTag, 0) {
+  if (FLAG_trace_intrinsified_natives) {
+    OS::Print("Profiler_getCurrentTag\n");
+  }
+  return isolate->current_tag();
+}
+
+
+DEFINE_NATIVE_ENTRY(Profiler_clearCurrentTag, 0) {
+  // Use a NoGCScope to avoid creating a handle for the old current.
+  NoGCScope no_gc;
+  RawUserTag* old_current = isolate->current_tag();
+  UserTag::ClearActive();
+  if (FLAG_trace_intrinsified_natives) {
+    OS::Print("Profiler_clearCurrentTag\n");
+  }
+  return old_current;
+}
+
+}  // namespace dart
diff --git a/runtime/lib/profiler.dart b/runtime/lib/profiler.dart
new file mode 100644
index 0000000..5ed8df5
--- /dev/null
+++ b/runtime/lib/profiler.dart
@@ -0,0 +1,24 @@
+// 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:_internal";
+
+patch class UserTag {
+  /* patch */ factory UserTag(String label) {
+    return new _UserTag(label);
+  }
+}
+
+
+class _UserTag implements UserTag {
+  factory _UserTag(String label) native "UserTag_new";
+  String get label native "UserTag_label";
+  void makeCurrent() native "UserTag_makeCurrent";
+}
+
+patch UserTag getCurrentTag() => _getCurrentTag();
+UserTag _getCurrentTag() native "Profiler_getCurrentTag";
+
+patch UserTag clearCurrentTag() => _clearCurrentTag();
+UserTag _clearCurrentTag() native "Profiler_clearCurrentTag";
diff --git a/runtime/lib/profiler_sources.gypi b/runtime/lib/profiler_sources.gypi
new file mode 100644
index 0000000..4d5ee05
--- /dev/null
+++ b/runtime/lib/profiler_sources.gypi
@@ -0,0 +1,13 @@
+# 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.
+
+# Sources visible via dart:profiler library.
+
+{
+  'sources': [
+    'profiler.cc',
+    'profiler.dart',
+  ],
+}
+
diff --git a/runtime/vm/assembler_arm64.cc b/runtime/vm/assembler_arm64.cc
index 2da262e..02adb89 100644
--- a/runtime/vm/assembler_arm64.cc
+++ b/runtime/vm/assembler_arm64.cc
@@ -20,10 +20,38 @@
 
 namespace dart {
 
-DEFINE_FLAG(bool, print_stop_message, true, "Print stop message.");
+DEFINE_FLAG(bool, print_stop_message, false, "Print stop message.");
 DECLARE_FLAG(bool, inline_alloc);
 
 
+Assembler::Assembler(bool use_far_branches)
+    : buffer_(),
+      object_pool_(GrowableObjectArray::Handle()),
+      patchable_pool_entries_(),
+      prologue_offset_(-1),
+      use_far_branches_(use_far_branches),
+      comments_() {
+  if (Isolate::Current() != Dart::vm_isolate()) {
+    object_pool_ = GrowableObjectArray::New(Heap::kOld);
+
+    // These objects and labels need to be accessible through every pool-pointer
+    // at the same index.
+    object_pool_.Add(Object::null_object(), Heap::kOld);
+    patchable_pool_entries_.Add(kNotPatchable);
+    // Not adding Object::null() to the index table. It is at index 0 in the
+    // object pool, but the HashMap uses 0 to indicate not found.
+
+    object_pool_.Add(Bool::True(), Heap::kOld);
+    patchable_pool_entries_.Add(kNotPatchable);
+    object_pool_index_table_.Insert(ObjIndexPair(Bool::True().raw(), 1));
+
+    object_pool_.Add(Bool::False(), Heap::kOld);
+    patchable_pool_entries_.Add(kNotPatchable);
+    object_pool_index_table_.Insert(ObjIndexPair(Bool::False().raw(), 2));
+  }
+}
+
+
 void Assembler::InitializeMemoryWithBreakpoints(uword data, intptr_t length) {
   ASSERT(Utils::IsAligned(data, 4));
   ASSERT(Utils::IsAligned(length, 4));
@@ -35,11 +63,6 @@
 }
 
 
-void Assembler::Stop(const char* message) {
-  UNIMPLEMENTED();
-}
-
-
 void Assembler::Emit(int32_t value) {
   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
   buffer_.Emit<int32_t>(value);
@@ -74,6 +97,36 @@
 }
 
 
+// TODO(zra): Support for far branches. Requires loading large immediates.
+void Assembler::Bind(Label* label) {
+  ASSERT(!label->IsBound());
+  intptr_t bound_pc = buffer_.Size();
+
+  while (label->IsLinked()) {
+    const int64_t position = label->Position();
+    const int64_t dest = bound_pc - position;
+    const int32_t next = buffer_.Load<int32_t>(position);
+    const int32_t encoded = EncodeImm19BranchOffset(dest, next);
+    buffer_.Store<int32_t>(position, encoded);
+    label->position_ = DecodeImm19BranchOffset(next);
+  }
+  label->BindTo(bound_pc);
+}
+
+
+void Assembler::Stop(const char* message) {
+  if (FLAG_print_stop_message) {
+    UNIMPLEMENTED();
+  }
+  Label stop;
+  b(&stop);
+  Emit(Utils::Low32Bits(reinterpret_cast<int64_t>(message)));
+  Emit(Utils::High32Bits(reinterpret_cast<int64_t>(message)));
+  Bind(&stop);
+  hlt(kImmExceptionIsDebug);
+}
+
+
 static int CountLeadingZeros(uint64_t value, int width) {
   ASSERT((width == 32) || (width == 64));
   if (value == 0) {
@@ -109,7 +162,7 @@
 // by the corresponding fields in the logical instruction.
 // If it can't be encoded, the function returns false, and the operand is
 // undefined.
-bool Assembler::IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op) {
+bool Operand::IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op) {
   ASSERT(imm_op != NULL);
   ASSERT((width == kWRegSizeInBits) || (width == kXRegSizeInBits));
   ASSERT((width == kXRegSizeInBits) || (value <= 0xffffffffUL));
@@ -203,6 +256,393 @@
   }
 }
 
+
+void Assembler::LoadPoolPointer(Register pp) {
+  const intptr_t object_pool_pc_dist =
+    Instructions::HeaderSize() - Instructions::object_pool_offset() +
+    CodeSize();
+  // PP <- Read(PC - object_pool_pc_dist).
+  ldr(pp, Address::PC(-object_pool_pc_dist));
+
+  // When in the PP register, the pool pointer is untagged. When we
+  // push it on the stack with PushPP it is tagged again. PopPP then untags
+  // when restoring from the stack. This will make loading from the object
+  // pool only one instruction for the first 4096 entries. Otherwise, because
+  // the offset wouldn't be aligned, it would be only one instruction for the
+  // first 64 entries.
+  sub(pp, pp, Operand(kHeapObjectTag));
+}
+
+void Assembler::LoadWordFromPoolOffset(Register dst, Register pp,
+                                       uint32_t offset) {
+  ASSERT(dst != pp);
+  if (Address::CanHoldOffset(offset)) {
+    ldr(dst, Address(pp, offset));
+  } else {
+    const uint16_t offset_low = Utils::Low16Bits(offset);
+    const uint16_t offset_high = Utils::High16Bits(offset);
+    movz(dst, offset_low, 0);
+    if (offset_high != 0) {
+      movk(dst, offset_high, 1);
+    }
+    ldr(dst, Address(pp, dst));
+  }
+}
+
+
+intptr_t Assembler::FindExternalLabel(const ExternalLabel* label,
+                                      Patchability patchable) {
+  // The object pool cannot be used in the vm isolate.
+  ASSERT(Isolate::Current() != Dart::vm_isolate());
+  ASSERT(!object_pool_.IsNull());
+  const uword address = label->address();
+  ASSERT(Utils::IsAligned(address, 4));
+  // The address is stored in the object array as a RawSmi.
+  const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(address));
+  if (patchable == kNotPatchable) {
+    // If the call site is not patchable, we can try to re-use an existing
+    // entry.
+    return FindObject(smi, kNotPatchable);
+  }
+  // If the call is patchable, do not reuse an existing entry since each
+  // reference may be patched independently.
+  object_pool_.Add(smi, Heap::kOld);
+  patchable_pool_entries_.Add(patchable);
+  return object_pool_.Length() - 1;
+}
+
+
+intptr_t Assembler::FindObject(const Object& obj, Patchability patchable) {
+  // The object pool cannot be used in the vm isolate.
+  ASSERT(Isolate::Current() != Dart::vm_isolate());
+  ASSERT(!object_pool_.IsNull());
+
+  // If the object is not patchable, check if we've already got it in the
+  // object pool.
+  if (patchable == kNotPatchable) {
+    // Special case for Object::null(), which is always at object_pool_ index 0
+    // because Lookup() below returns 0 when the object is not mapped in the
+    // table.
+    if (obj.raw() == Object::null()) {
+      return 0;
+    }
+
+    intptr_t idx = object_pool_index_table_.Lookup(obj.raw());
+    if (idx != 0) {
+      ASSERT(patchable_pool_entries_[idx] == kNotPatchable);
+      return idx;
+    }
+  }
+
+  object_pool_.Add(obj, Heap::kOld);
+  patchable_pool_entries_.Add(patchable);
+  if (patchable == kNotPatchable) {
+    // The object isn't patchable. Record the index for fast lookup.
+    object_pool_index_table_.Insert(
+        ObjIndexPair(obj.raw(), object_pool_.Length() - 1));
+  }
+  return object_pool_.Length() - 1;
+}
+
+
+intptr_t Assembler::FindImmediate(int64_t imm) {
+  ASSERT(Isolate::Current() != Dart::vm_isolate());
+  ASSERT(!object_pool_.IsNull());
+  const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(imm));
+  return FindObject(smi, kNotPatchable);
+}
+
+
+bool Assembler::CanLoadObjectFromPool(const Object& object) {
+  // TODO(zra, kmillikin): Also load other large immediates from the object
+  // pool
+  if (object.IsSmi()) {
+    // If the raw smi does not fit into a 32-bit signed int, then we'll keep
+    // the raw value in the object pool.
+    return !Utils::IsInt(32, reinterpret_cast<int64_t>(object.raw()));
+  }
+  ASSERT(object.IsNotTemporaryScopedHandle());
+  ASSERT(object.IsOld());
+  return (Isolate::Current() != Dart::vm_isolate()) &&
+         // Not in the VMHeap, OR is one of the VMHeap objects we put in every
+         // object pool.
+         // TODO(zra): Evaluate putting all VM heap objects into the pool.
+         (!object.InVMHeap() || (object.raw() == Object::null()) ||
+                                (object.raw() == Bool::True().raw()) ||
+                                (object.raw() == Bool::False().raw()));
+}
+
+
+bool Assembler::CanLoadImmediateFromPool(int64_t imm, Register pp) {
+  return !Utils::IsInt(32, imm) &&
+         (pp != kNoRegister) &&
+         (Isolate::Current() != Dart::vm_isolate());
+}
+
+
+void Assembler::LoadExternalLabel(Register dst,
+                                  const ExternalLabel* label,
+                                  Patchability patchable,
+                                  Register pp) {
+  const int32_t offset =
+      Array::element_offset(FindExternalLabel(label, patchable));
+  LoadWordFromPoolOffset(dst, pp, offset);
+}
+
+
+void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
+  if (CanLoadObjectFromPool(object)) {
+    const int32_t offset =
+        Array::element_offset(FindObject(object, kNotPatchable));
+    LoadWordFromPoolOffset(dst, pp, offset);
+  } else {
+    ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
+           object.IsSmi() ||
+           object.InVMHeap());
+    LoadDecodableImmediate(dst, reinterpret_cast<int64_t>(object.raw()), pp);
+  }
+}
+
+
+void Assembler::LoadDecodableImmediate(Register reg, int64_t imm, Register pp) {
+  if ((pp != kNoRegister) && (Isolate::Current() != Dart::vm_isolate())) {
+    int64_t val_smi_tag = imm & kSmiTagMask;
+    imm &= ~kSmiTagMask;  // Mask off the tag bits.
+    const int32_t offset = Array::element_offset(FindImmediate(imm));
+    LoadWordFromPoolOffset(reg, pp, offset);
+    if (val_smi_tag != 0) {
+      // Add back the tag bits.
+      orri(reg, reg, val_smi_tag);
+    }
+  } else {
+    // TODO(zra): Since this sequence only needs to be decodable, it can be
+    // of variable length.
+    LoadPatchableImmediate(reg, imm);
+  }
+}
+
+
+void Assembler::LoadPatchableImmediate(Register reg, int64_t imm) {
+  const uint32_t w0 = Utils::Low32Bits(imm);
+  const uint32_t w1 = Utils::High32Bits(imm);
+  const uint16_t h0 = Utils::Low16Bits(w0);
+  const uint16_t h1 = Utils::High16Bits(w0);
+  const uint16_t h2 = Utils::Low16Bits(w1);
+  const uint16_t h3 = Utils::High16Bits(w1);
+  movz(reg, h0, 0);
+  movk(reg, h1, 1);
+  movk(reg, h2, 2);
+  movk(reg, h3, 3);
+}
+
+
+void Assembler::LoadImmediate(Register reg, int64_t imm, Register pp) {
+  Comment("LoadImmediate");
+  if (CanLoadImmediateFromPool(imm, pp)) {
+    // It's a 64-bit constant and we're not in the VM isolate, so load from
+    // object pool.
+    // Save the bits that must be masked-off for the SmiTag
+    int64_t val_smi_tag = imm & kSmiTagMask;
+    imm &= ~kSmiTagMask;  // Mask off the tag bits.
+    const int32_t offset = Array::element_offset(FindImmediate(imm));
+    LoadWordFromPoolOffset(reg, pp, offset);
+    if (val_smi_tag != 0) {
+      // Add back the tag bits.
+      orri(reg, reg, val_smi_tag);
+    }
+  } else {
+    // 1. Can we use one orri operation?
+    Operand op;
+    Operand::OperandType ot;
+    ot = Operand::CanHold(imm, kXRegSizeInBits, &op);
+    if (ot == Operand::BitfieldImm) {
+      orri(reg, ZR, imm);
+      return;
+    }
+
+    // 2. Fall back on movz, movk, movn.
+    const uint32_t w0 = Utils::Low32Bits(imm);
+    const uint32_t w1 = Utils::High32Bits(imm);
+    const uint16_t h0 = Utils::Low16Bits(w0);
+    const uint16_t h1 = Utils::High16Bits(w0);
+    const uint16_t h2 = Utils::Low16Bits(w1);
+    const uint16_t h3 = Utils::High16Bits(w1);
+
+    // Special case for w1 == 0xffffffff
+    if (w1 == 0xffffffff) {
+      if (h1 == 0xffff) {
+        movn(reg, ~h0, 0);
+      } else {
+        movn(reg, ~h1, 1);
+        movk(reg, h0, 0);
+      }
+      return;
+    }
+
+    // Special case for h3 == 0xffff
+    if (h3 == 0xffff) {
+      // We know h2 != 0xffff.
+      movn(reg, ~h2, 2);
+      if (h1 != 0xffff) {
+        movk(reg, h1, 1);
+      }
+      if (h0 != 0xffff) {
+        movk(reg, h0, 0);
+      }
+      return;
+    }
+
+    bool initialized = false;
+    if (h0 != 0) {
+      movz(reg, h0, 0);
+      initialized = true;
+    }
+    if (h1 != 0) {
+      if (initialized) {
+        movk(reg, h1, 1);
+      } else {
+        movz(reg, h1, 1);
+        initialized = true;
+      }
+    }
+    if (h2 != 0) {
+      if (initialized) {
+        movk(reg, h2, 2);
+      } else {
+        movz(reg, h2, 2);
+        initialized = true;
+      }
+    }
+    if (h3 != 0) {
+      if (initialized) {
+        movk(reg, h3, 3);
+      } else {
+        movz(reg, h3, 3);
+      }
+    }
+  }
+}
+
+
+void Assembler::AddImmediate(
+    Register dest, Register rn, int64_t imm, Register pp) {
+  ASSERT(rn != TMP2);
+  Operand op;
+  if (Operand::CanHold(imm, kXRegSizeInBits, &op) == Operand::Immediate) {
+    add(dest, rn, op);
+  } else if (Operand::CanHold(-imm, kXRegSizeInBits, &op) ==
+             Operand::Immediate) {
+    sub(dest, rn, op);
+  } else {
+    LoadImmediate(TMP2, imm, pp);
+    add(dest, rn, Operand(TMP2));
+  }
+}
+
+
+void Assembler::CompareImmediate(Register rn, int64_t imm, Register pp) {
+  ASSERT(rn != TMP2);
+  Operand op;
+  if (Operand::CanHold(imm, kXRegSizeInBits, &op) == Operand::Immediate) {
+    cmp(rn, op);
+  } else if (Operand::CanHold(-imm, kXRegSizeInBits, &op) ==
+             Operand::Immediate) {
+    cmn(rn, op);
+  } else {
+    LoadImmediate(TMP2, imm, pp);
+    cmp(rn, Operand(TMP2));
+  }
+}
+
+
+void Assembler::LoadFromOffset(Register dest, Register base, int32_t offset) {
+  ASSERT(base != TMP2);
+  if (Address::CanHoldOffset(offset)) {
+    ldr(dest, Address(base, offset));
+  } else {
+    // Since offset is 32-bits, it won't be loaded from the pool.
+    AddImmediate(TMP2, base, offset, kNoRegister);
+    ldr(dest, Address(TMP2));
+  }
+}
+
+
+void Assembler::StoreToOffset(Register src, Register base, int32_t offset) {
+  ASSERT(src != TMP2);
+  ASSERT(base != TMP2);
+  if (Address::CanHoldOffset(offset)) {
+    str(src, Address(base, offset));
+  } else if (Address::CanHoldOffset(offset, Address::PreIndex)) {
+    mov(TMP2, base);
+    str(src, Address(TMP2, offset, Address::PreIndex));
+  } else {
+    // Since offset is 32-bits, it won't be loaded from the pool.
+    AddImmediate(TMP2, base, offset, kNoRegister);
+    str(src, Address(TMP2));
+  }
+}
+
+
+void Assembler::ReserveAlignedFrameSpace(intptr_t frame_space) {
+  // Reserve space for arguments and align frame before entering
+  // the C++ world.
+  AddImmediate(SP, SP, -frame_space, kNoRegister);
+  if (OS::ActivationFrameAlignment() > 1) {
+    mov(TMP, SP);  // SP can't be register operand of andi.
+    andi(TMP, TMP, ~(OS::ActivationFrameAlignment() - 1));
+    mov(SP, TMP);
+  }
+}
+
+
+void Assembler::EnterFrame(intptr_t frame_size) {
+  Push(LR);
+  Push(FP);
+  mov(FP, SP);
+
+  if (frame_size > 0) {
+    sub(SP, SP, Operand(frame_size));
+  }
+}
+
+
+void Assembler::LeaveFrame() {
+  mov(SP, FP);
+  Pop(FP);
+  Pop(LR);
+}
+
+
+void Assembler::EnterDartFrame(intptr_t frame_size) {
+  // Setup the frame.
+  adr(TMP, 0);  // TMP gets PC of this instruction.
+  EnterFrame(0);
+  Push(TMP);  // Save PC Marker.
+  PushPP();  // Save PP.
+
+  // Load the pool pointer.
+  LoadPoolPointer(PP);
+
+  // Reserve space.
+  if (frame_size > 0) {
+    sub(SP, SP, Operand(frame_size));
+  }
+}
+
+
+void Assembler::LeaveDartFrame() {
+  // Restore and untag PP.
+  LoadFromOffset(PP, FP, kSavedCallerPpSlotFromFp * kWordSize);
+  sub(PP, PP, Operand(kHeapObjectTag));
+  LeaveFrame();
+}
+
+
+void Assembler::CallRuntime(const RuntimeEntry& entry,
+                            intptr_t argument_count) {
+  entry.Call(this, argument_count);
+}
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/assembler_arm64.h b/runtime/vm/assembler_arm64.h
index fd9d7cb..1ead6e5 100644
--- a/runtime/vm/assembler_arm64.h
+++ b/runtime/vm/assembler_arm64.h
@@ -12,6 +12,7 @@
 #include "platform/assert.h"
 #include "platform/utils.h"
 #include "vm/constants_arm64.h"
+#include "vm/hash_map.h"
 #include "vm/object.h"
 #include "vm/simulator.h"
 
@@ -87,6 +88,8 @@
     PreIndex,
     PostIndex,
     Reg,
+    PCOffset,
+    Unknown,
   };
 
   // Offset is in bytes. For the unsigned imm12 case, we unscale based on the
@@ -96,14 +99,21 @@
   Address(Register rn, int32_t offset = 0, AddressType at = Offset,
           OperandSize sz = kDoubleWord) {
     ASSERT((rn != R31) && (rn != ZR));
+    ASSERT(CanHoldOffset(offset, at, sz));
     const Register crn = ConcreteRegister(rn);
     const int32_t scale = Log2OperandSizeBytes(sz);
-    if (Utils::IsUint(12 + scale, offset) && (at == Offset)) {
-      ASSERT(offset == ((offset >> scale) << scale));
+    if ((at == Offset) &&
+         Utils::IsUint(12 + scale, offset) &&
+        (offset == ((offset >> scale) << scale))) {
       encoding_ =
           B24 |
           ((offset >> scale) << kImm12Shift) |
           (static_cast<int32_t>(crn) << kRnShift);
+    } else if ((at == Offset) &&
+               Utils::IsInt(9, offset)) {
+      encoding_ =
+          ((offset & 0x1ff) << kImm9Shift) |
+          (static_cast<int32_t>(crn) << kRnShift);
     } else {
       ASSERT(Utils::IsInt(9, offset));
       ASSERT((at == PreIndex) || (at == PostIndex));
@@ -117,8 +127,33 @@
     base_ = crn;
   }
 
-  // TODO(zra): Write CanHoldOffset(int32_t off, AddressType, OperandSize).
-  // TODO(zra): Write constructor for PC-relative load address.
+  static bool CanHoldOffset(int32_t offset, AddressType at = Offset,
+                            OperandSize sz = kDoubleWord) {
+    if (at == Offset) {
+      // Offset fits in 12 bit unsigned and has right alignment for sz,
+      // or fits in 9 bit signed offset with no alignment restriction.
+      const int32_t scale = Log2OperandSizeBytes(sz);
+      return (Utils::IsUint(12 + scale, offset) &&
+              (offset == ((offset >> scale) << scale))) ||
+             (Utils::IsInt(9, offset));
+    } else if (at == PCOffset) {
+      return Utils::IsInt(21, offset) &&
+             (offset == ((offset >> 2) << 2));
+    } else {
+      ASSERT((at == PreIndex) || (at == PostIndex));
+      return Utils::IsInt(9, offset);
+    }
+  }
+
+  // PC-relative load address.
+  static Address PC(int32_t pc_off) {
+    ASSERT(CanHoldOffset(pc_off, PCOffset));
+    Address addr;
+    addr.encoding_ = (((pc_off >> 2) << kImm19Shift) & kImm19Mask);
+    addr.base_ = kNoRegister;
+    addr.type_ = PCOffset;
+    return addr;
+  }
 
   // Base register rn with offset rm. rm is sign-extended according to ext.
   // If ext is UXTX, rm may be optionally scaled by the
@@ -145,6 +180,8 @@
   AddressType type() const { return type_; }
   Register base() const { return base_; }
 
+  Address() : encoding_(0), type_(Unknown), base_(kNoRegister) {}
+
   uint32_t encoding_;
   AddressType type_;
   Register base_;
@@ -169,6 +206,14 @@
 
 class Operand : public ValueObject {
  public:
+  enum OperandType {
+    Shifted,
+    Extended,
+    Immediate,
+    BitfieldImm,
+    Unknown,
+  };
+
   // Data-processing operand - Uninitialized.
   Operand() : encoding_(-1), type_(Unknown) { }
 
@@ -225,7 +270,7 @@
 
   // Encodes the value of an immediate for a logical operation.
   // Since these values are difficult to craft by hand, instead pass the
-  // logical mask to the function Assembler::IsImmLogical to get n, imm_s, and
+  // logical mask to the function IsImmLogical to get n, imm_s, and
   // imm_r.
   Operand(uint8_t n, int8_t imm_s, int8_t imm_r) {
     ASSERT((n == 1) || (n == 0));
@@ -237,13 +282,37 @@
       (static_cast<int32_t>(imm_r) << kImmRShift);
   }
 
-  enum OperandType {
-    Shifted,
-    Extended,
-    Immediate,
-    BitfieldImm,
-    Unknown,
-  };
+  // Test if a given value can be encoded in the immediate field of a logical
+  // instruction.
+  // If it can be encoded, the function returns true, and values pointed to by
+  // n, imm_s and imm_r are updated with immediates encoded in the format
+  // required by the corresponding fields in the logical instruction.
+  // If it can't be encoded, the function returns false, and the operand is
+  // undefined.
+  static bool IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op);
+
+  // An immediate imm can be an operand to add/sub when the return value is
+  // Immediate, or a logical operation over sz bits when the return value is
+  // BitfieldImm. If the return value is Unknown, then the immediate can't be
+  // used as an operand in either instruction. The encoded operand is written
+  // to op.
+  static OperandType CanHold(int64_t imm, uint8_t sz, Operand* op) {
+    ASSERT(op != NULL);
+    ASSERT((sz == kXRegSizeInBits) || (sz == kWRegSizeInBits));
+    if (Utils::IsUint(12, imm)) {
+      op->encoding_ = imm << kImm12Shift;
+      op->type_ = Immediate;
+    } else if (((imm & 0xfff) == 0) && (Utils::IsUint(12, imm >> 12))) {
+      op->encoding_ = B22 | ((imm >> 12) << kImm12Shift);
+      op->type_ = Immediate;
+    } else if (IsImmLogical(imm, sz, op)) {
+      op->type_ = BitfieldImm;
+    } else {
+      op->encoding_ = 0;
+      op->type_ = Unknown;
+    }
+    return op->type_;
+  }
 
  private:
   uint32_t encoding() const {
@@ -262,25 +331,18 @@
 
 class Assembler : public ValueObject {
  public:
-  explicit Assembler(bool use_far_branches = false)
-      : buffer_(),
-        object_pool_(GrowableObjectArray::Handle()),
-        prologue_offset_(-1),
-        use_far_branches_(use_far_branches),
-        comments_() { }
+  explicit Assembler(bool use_far_branches = false);
   ~Assembler() { }
 
   void PopRegister(Register r) {
-    UNIMPLEMENTED();
+    Pop(r);
   }
 
   void Drop(intptr_t stack_elements) {
-    UNIMPLEMENTED();
+    add(SP, SP, Operand(stack_elements * kWordSize));
   }
 
-  void Bind(Label* label) {
-    UNIMPLEMENTED();
-  }
+  void Bind(Label* label);
 
   // Misc. functionality
   intptr_t CodeSize() const { return buffer_.Size(); }
@@ -325,6 +387,14 @@
 
   static const char* FpuRegisterName(FpuRegister reg);
 
+  void SetPrologueOffset() {
+    if (prologue_offset_ == -1) {
+      prologue_offset_ = CodeSize();
+    }
+  }
+
+  void ReserveAlignedFrameSpace(intptr_t frame_space);
+
   // TODO(zra): Make sure this is right.
   // Instruction pattern from entrypoint is used in Dart frame prologs
   // to set up the frame and save a PC which can be used to figure out the
@@ -356,30 +426,35 @@
     AddSubHelper(kDoubleWord, true, true, rd, rn, o);
   }
 
+  // PC relative immediate add. imm is in bytes.
+  void adr(Register rd, int64_t imm) {
+    EmitPCRelOp(ADR, rd, imm);
+  }
+
   // Logical immediate operations.
   // TODO(zra): Add macros that check IsImmLogical, and fall back on a longer
   // sequence on failure.
   void andi(Register rd, Register rn, uint64_t imm) {
     Operand imm_op;
-    const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+    const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
     ASSERT(immok);
     EmitLogicalImmOp(ANDI, rd, rn, imm_op, kDoubleWord);
   }
   void orri(Register rd, Register rn, uint64_t imm) {
     Operand imm_op;
-    const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+    const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
     ASSERT(immok);
     EmitLogicalImmOp(ORRI, rd, rn, imm_op, kDoubleWord);
   }
   void eori(Register rd, Register rn, uint64_t imm) {
     Operand imm_op;
-    const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+    const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
     ASSERT(immok);
     EmitLogicalImmOp(EORI, rd, rn, imm_op, kDoubleWord);
   }
   void andis(Register rd, Register rn, uint64_t imm) {
     Operand imm_op;
-    const bool immok = IsImmLogical(imm, kXRegSizeInBits, &imm_op);
+    const bool immok = Operand::IsImmLogical(imm, kXRegSizeInBits, &imm_op);
     ASSERT(immok);
     EmitLogicalImmOp(ANDIS, rd, rn, imm_op, kDoubleWord);
   }
@@ -410,6 +485,61 @@
     EmitLogicalShiftOp(BICS, rd, rn, o, kDoubleWord);
   }
 
+  // Misc. arithmetic.
+  void udiv(Register rd, Register rn, Register rm) {
+    EmitMiscDP2Source(UDIV, rd, rn, rm, kDoubleWord);
+  }
+  void sdiv(Register rd, Register rn, Register rm) {
+    EmitMiscDP2Source(SDIV, rd, rn, rm, kDoubleWord);
+  }
+  void lslv(Register rd, Register rn, Register rm) {
+    EmitMiscDP2Source(LSLV, rd, rn, rm, kDoubleWord);
+  }
+  void lsrv(Register rd, Register rn, Register rm) {
+    EmitMiscDP2Source(LSRV, rd, rn, rm, kDoubleWord);
+  }
+  void asrv(Register rd, Register rn, Register rm) {
+    EmitMiscDP2Source(ASRV, rd, rn, rm, kDoubleWord);
+  }
+  void madd(Register rd, Register rn, Register rm, Register ra) {
+    EmitMiscDP3Source(MADD, rd, rn, rm, ra, kDoubleWord);
+  }
+
+  // Move wide immediate.
+  void movk(Register rd, uint16_t imm, int hw_idx) {
+    ASSERT(rd != SP);
+    const Register crd = ConcreteRegister(rd);
+    EmitMoveWideOp(MOVK, crd, imm, hw_idx, kDoubleWord);
+  }
+  void movn(Register rd, uint16_t imm, int hw_idx) {
+    ASSERT(rd != SP);
+    const Register crd = ConcreteRegister(rd);
+    EmitMoveWideOp(MOVN, crd, imm, hw_idx, kDoubleWord);
+  }
+  void movz(Register rd, uint16_t imm, int hw_idx) {
+    ASSERT(rd != SP);
+    const Register crd = ConcreteRegister(rd);
+    EmitMoveWideOp(MOVZ, crd, imm, hw_idx, kDoubleWord);
+  }
+
+  // Loads and Stores.
+  void ldr(Register rt, Address a) {
+    if (a.type() == Address::PCOffset) {
+      EmitLoadRegLiteral(LDRpc, rt, a, kDoubleWord);
+    } else {
+      // If we are doing pre-/post-indexing, and the base and result registers
+      // are the same, then the result of the load will be clobbered by the
+      // writeback, which is unlikely to be useful.
+      ASSERT(((a.type() != Address::PreIndex) &&
+              (a.type() != Address::PostIndex)) ||
+             (rt != a.base()));
+      EmitLoadStoreReg(LDR, rt, a, kDoubleWord);
+    }
+  }
+  void str(Register rt, Address a) {
+    EmitLoadStoreReg(STR, rt, a, kDoubleWord);
+  }
+
   // Comparison.
   // rn cmp o.
   void cmp(Register rn, Operand o) {
@@ -420,45 +550,204 @@
     adds(ZR, rn, o);
   }
 
-  // Move wide immediate.
-  void movk(Register rd, int32_t imm, int32_t hw_idx) {
-    ASSERT(rd != SP);
-    const Register crd = ConcreteRegister(rd);
-    EmitMoveWideOp(MOVK, crd, imm, hw_idx, kDoubleWord);
-  }
-  void movn(Register rd, int32_t imm, int32_t hw_idx) {
-    ASSERT(rd != SP);
-    const Register crd = ConcreteRegister(rd);
-    EmitMoveWideOp(MOVN, crd, imm, hw_idx, kDoubleWord);
-  }
-  void movz(Register rd, int32_t imm, int32_t hw_idx) {
-    ASSERT(rd != SP);
-    const Register crd = ConcreteRegister(rd);
-    EmitMoveWideOp(MOVZ, crd, imm, hw_idx, kDoubleWord);
+  // Conditional branch.
+  void b(Label* label, Condition cond = AL) {
+    EmitBranch(BCOND, cond, label);
   }
 
-  // Loads and Stores.
-  void ldr(Register rt, Address a) {
-    // If we are doing pre-/post-indexing, and the base and result registers
-    // are the same, then the result of the load will be clobbered by the
-    // writeback, which is unlikely to be useful.
-    ASSERT(((a.type() != Address::PreIndex) &&
-            (a.type() != Address::PostIndex)) ||
-           (rt != a.base()));
-    EmitLoadStoreReg(LDR, rt, a, kDoubleWord);
-  }
-  void str(Register rt, Address a) {
-    EmitLoadStoreReg(STR, rt, a, kDoubleWord);
-  }
+  // TODO(zra): branch and link with imm26 offset.
+  // TODO(zra): cbz, cbnz.
 
-  // Function return.
+  // Branch, link, return.
+  void br(Register rn) {
+    EmitUnconditionalBranchRegOp(BR, rn);
+  }
+  void blr(Register rn) {
+    EmitUnconditionalBranchRegOp(BLR, rn);
+  }
   void ret(Register rn = R30) {
     EmitUnconditionalBranchRegOp(RET, rn);
   }
 
+  // Exceptions.
+  void hlt(uint16_t imm) {
+    EmitExceptionGenOp(HLT, imm);
+  }
+
+  // Aliases.
+  void mov(Register rd, Register rn) {
+    if ((rd == SP) || (rn == SP)) {
+      add(rd, rn, Operand(0));
+    } else {
+      orr(rd, ZR, Operand(rn));
+    }
+  }
+  void mvn(Register rd, Register rm) {
+    orr(rd, ZR, Operand(rm));
+  }
+  void neg(Register rd, Register rm) {
+    sub(rd, ZR, Operand(rm));
+  }
+  void negs(Register rd, Register rm) {
+    subs(rd, ZR, Operand(rm));
+  }
+  void mul(Register rd, Register rn, Register rm) {
+    madd(rd, rn, rm, ZR);
+  }
+  void Push(Register reg) {
+    ASSERT(reg != PP);  // Only push PP with PushPP().
+    str(reg, Address(SP, -1 * kWordSize, Address::PreIndex));
+  }
+  void Pop(Register reg) {
+    ASSERT(reg != PP);  // Only pop PP with PopPP().
+    ldr(reg, Address(SP, 1 * kWordSize, Address::PostIndex));
+  }
+  void PushPP() {
+    // Add the heap object tag back to PP before putting it on the stack.
+    add(PP, PP, Operand(kHeapObjectTag));
+    str(PP, Address(SP, -1 * kWordSize, Address::PreIndex));
+  }
+  void PopPP() {
+    ldr(PP, Address(SP, 1 * kWordSize, Address::PostIndex));
+    sub(PP, PP, Operand(kHeapObjectTag));
+  }
+  void tst(Register rn, Operand o) {
+    ands(ZR, rn, o);
+  }
+  void tsti(Register rn, uint64_t imm) {
+    andis(ZR, rn, imm);
+  }
+
+  void SmiUntag(Register reg) {
+    add(reg, ZR, Operand(reg, ASR, kSmiTagSize));
+  }
+
+  // Branching to ExternalLabels.
+  void Branch(const ExternalLabel* label) {
+    LoadExternalLabel(TMP, label, kPatchable, PP);
+    br(TMP);
+  }
+
+  void BranchPatchable(const ExternalLabel* label) {
+    LoadPatchableImmediate(TMP, label->address());
+    br(TMP);
+  }
+
+  void BranchLink(const ExternalLabel* label, Register pp) {
+    if (Isolate::Current() == Dart::vm_isolate()) {
+      LoadImmediate(TMP, label->address(), kNoRegister);
+      blr(TMP);
+    } else {
+      LoadExternalLabel(TMP, label, kNotPatchable, pp);
+      blr(TMP);
+    }
+  }
+
+  void BranchLinkPatchable(const ExternalLabel* label) {
+    LoadExternalLabel(TMP, label, kPatchable, PP);
+    blr(TMP);
+  }
+
+  // Macros accepting a pp Register argument may attempt to load values from
+  // the object pool when possible. Unless you are sure that the untagged object
+  // pool pointer is in another register, or that it is not available at all,
+  // PP should be passed for pp.
+  void AddImmediate(Register dest, Register rn, int64_t imm, Register pp);
+  void CompareImmediate(Register rn, int64_t imm, Register pp);
+  void LoadFromOffset(Register dest, Register base, int32_t offset);
+  void LoadFieldFromOffset(Register dest, Register base, int32_t offset) {
+    LoadFromOffset(dest, base, offset - kHeapObjectTag);
+  }
+  void StoreToOffset(Register dest, Register base, int32_t offset);
+  void StoreFieldToOffset(Register dest, Register base, int32_t offset) {
+    StoreToOffset(dest, base, offset - kHeapObjectTag);
+  }
+
+  // Object pool, loading from pool, etc.
+  void LoadPoolPointer(Register pp);
+
+  enum Patchability {
+    kPatchable,
+    kNotPatchable,
+  };
+
+  void LoadWordFromPoolOffset(Register dst, Register pp, uint32_t offset);
+  intptr_t FindExternalLabel(const ExternalLabel* label,
+                             Patchability patchable);
+  intptr_t FindObject(const Object& obj, Patchability patchable);
+  intptr_t FindImmediate(int64_t imm);
+  bool CanLoadObjectFromPool(const Object& object);
+  bool CanLoadImmediateFromPool(int64_t imm, Register pp);
+  void LoadExternalLabel(Register dst, const ExternalLabel* label,
+                         Patchability patchable, Register pp);
+  void LoadObject(Register dst, const Object& obj, Register pp);
+  void LoadDecodableImmediate(Register reg, int64_t imm, Register pp);
+  void LoadPatchableImmediate(Register reg, int64_t imm);
+  void LoadImmediate(Register reg, int64_t imm, Register pp);
+
+  void PushObject(const Object& object, Register pp) {
+    LoadObject(TMP, object, pp);
+    Push(TMP);
+  }
+
+  void EnterFrame(intptr_t frame_size);
+  void LeaveFrame();
+
+  void EnterDartFrame(intptr_t frame_size);
+  void LeaveDartFrame();
+
+  void CallRuntime(const RuntimeEntry& entry, intptr_t argument_count);
+
  private:
   AssemblerBuffer buffer_;  // Contains position independent code.
-  GrowableObjectArray& object_pool_;  // Objects and patchable jump targets.
+
+  // Objects and patchable jump targets.
+  GrowableObjectArray& object_pool_;
+
+  // Patchability of pool entries.
+  GrowableArray<Patchability> patchable_pool_entries_;
+
+  // Pair type parameter for DirectChainedHashMap.
+  class ObjIndexPair {
+   public:
+    // TODO(zra): A WeakTable should be used here instead, but then it would
+    // also have to be possible to register and de-register WeakTables with the
+    // heap. Also, the Assembler would need to become a StackResource.
+    // Issue 13305. In the meantime...
+    // CAUTION: the RawObject* below is only safe because:
+    // The HashMap that will use this pair type will not contain any RawObject*
+    // keys that are not in the object_pool_ array. Since the keys will be
+    // visited by the GC when it visits the object_pool_, and since all objects
+    // in the object_pool_ are Old (and so will not be moved) the GC does not
+    // also need to visit the keys here in the HashMap.
+
+    // Typedefs needed for the DirectChainedHashMap template.
+    typedef RawObject* Key;
+    typedef intptr_t Value;
+    typedef ObjIndexPair Pair;
+
+    ObjIndexPair(Key key, Value value) : key_(key), value_(value) { }
+
+    static Key KeyOf(Pair kv) { return kv.key_; }
+
+    static Value ValueOf(Pair kv) { return kv.value_; }
+
+    static intptr_t Hashcode(Key key) {
+      return reinterpret_cast<intptr_t>(key) >> kObjectAlignmentLog2;
+    }
+
+    static inline bool IsKeyEqual(Pair kv, Key key) {
+      return kv.key_ == key;
+    }
+
+   private:
+    Key key_;
+    Value value_;
+  };
+
+  // Hashmap for fast lookup in object pool.
+  DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
+
   int32_t prologue_offset_;
 
   bool use_far_branches_;
@@ -480,15 +769,13 @@
 
   GrowableArray<CodeComment*> comments_;
 
-  bool IsImmLogical(uint64_t value, uint8_t width, Operand* imm_op);
-
   void AddSubHelper(OperandSize os, bool set_flags, bool subtract,
                     Register rd, Register rn, Operand o) {
     ASSERT((rd != R31) && (rn != R31));
     const Register crd = ConcreteRegister(rd);
     const Register crn = ConcreteRegister(rn);
     if (o.type() == Operand::Immediate) {
-      ASSERT((rd != ZR) && (rn != ZR));
+      ASSERT(rn != ZR);
       EmitAddSubImmOp(subtract ? SUBI : ADDI, crd, crn, o, os, set_flags);
     } else if (o.type() == Operand::Shifted) {
       ASSERT((rd != SP) && (rn != SP));
@@ -563,36 +850,158 @@
     Emit(encoding);
   }
 
-  void EmitUnconditionalBranchRegOp(UnconditionalBranchRegOp op, Register rn) {
+  int32_t EncodeImm19BranchOffset(int64_t imm, int32_t instr) {
+    const int32_t imm32 = static_cast<int32_t>(imm);
+    const int32_t off = (((imm32 >> 2) << kImm19Shift) & kImm19Mask);
+    return (instr & ~kImm19Mask) | off;
+  }
+
+  int64_t DecodeImm19BranchOffset(int32_t instr) {
+    const int32_t off = (((instr >> kImm19Shift) & kImm19Shift) << 13) >> 13;
+    return static_cast<int64_t>(off);
+  }
+
+  void EmitCompareAndBranch(CompareAndBranchOp op, Register rt, int64_t imm,
+                            OperandSize sz) {
+    ASSERT((sz == kDoubleWord) || (sz == kWord));
+    ASSERT(Utils::IsInt(21, imm) && ((imm & 0x3) == 0));
+    ASSERT((rt != SP) && (rt != R31));
+    const Register crt = ConcreteRegister(rt);
+    const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+    const int32_t encoded_offset = EncodeImm19BranchOffset(imm, 0);
     const int32_t encoding =
-        op | (static_cast<int32_t>(rn) << kRnShift);
+        op | size |
+        (static_cast<int32_t>(crt) << kRtShift) |
+        encoded_offset;
     Emit(encoding);
   }
 
-  void EmitMoveWideOp(MoveWideOp op, Register rd, int32_t imm, int32_t hw_idx,
+  void EmitConditionalBranch(ConditionalBranchOp op, Condition cond,
+                             int64_t imm) {
+    ASSERT(Utils::IsInt(21, imm) && ((imm & 0x3) == 0));
+    const int32_t encoding =
+        op |
+        (static_cast<int32_t>(cond) << kCondShift) |
+        (((imm >> 2) << kImm19Shift) & kImm19Mask);
+    Emit(encoding);
+  }
+
+  bool CanEncodeImm19BranchOffset(int64_t offset) {
+    ASSERT(Utils::IsAligned(offset, 4));
+    return Utils::IsInt(19, offset);
+  }
+
+  // TODO(zra): Implement far branches. Requires loading large immediates.
+  void EmitBranch(ConditionalBranchOp op, Condition cond, Label* label) {
+    if (label->IsBound()) {
+      const int64_t dest = label->Position() - buffer_.Size();
+      ASSERT(CanEncodeImm19BranchOffset(dest));
+      EmitConditionalBranch(op, cond, dest);
+    } else {
+      const int64_t position = buffer_.Size();
+      ASSERT(CanEncodeImm19BranchOffset(position));
+      EmitConditionalBranch(op, cond, label->position_);
+      label->LinkTo(position);
+    }
+  }
+
+  void EmitUnconditionalBranchRegOp(UnconditionalBranchRegOp op, Register rn) {
+    ASSERT((rn != SP) && (rn != R31));
+    const Register crn = ConcreteRegister(rn);
+    const int32_t encoding =
+        op | (static_cast<int32_t>(crn) << kRnShift);
+    Emit(encoding);
+  }
+
+  void EmitExceptionGenOp(ExceptionGenOp op, uint16_t imm) {
+    const int32_t encoding =
+        op | (static_cast<int32_t>(imm) << kImm16Shift);
+    Emit(encoding);
+  }
+
+  void EmitMoveWideOp(MoveWideOp op, Register rd, uint16_t imm, int hw_idx,
                       OperandSize sz) {
-    ASSERT(Utils::IsUint(16, imm));
     ASSERT((hw_idx >= 0) && (hw_idx <= 3));
     ASSERT((sz == kDoubleWord) || (sz == kWord));
     const int32_t size = (sz == kDoubleWord) ? B31 : 0;
     const int32_t encoding =
         op | size |
         (static_cast<int32_t>(rd) << kRdShift) |
-        (hw_idx << kHWShift) |
-        (imm << kImm16Shift);
+        (static_cast<int32_t>(hw_idx) << kHWShift) |
+        (static_cast<int32_t>(imm) << kImm16Shift);
     Emit(encoding);
   }
 
   void EmitLoadStoreReg(LoadStoreRegOp op, Register rt, Address a,
                         OperandSize sz) {
+    const Register crt = ConcreteRegister(rt);
     const int32_t size = Log2OperandSizeBytes(sz);
     const int32_t encoding =
         op | (size << kSzShift) |
-        (static_cast<int32_t>(rt) << kRtShift) |
+        (static_cast<int32_t>(crt) << kRtShift) |
         a.encoding();
     Emit(encoding);
   }
 
+  void EmitLoadRegLiteral(LoadRegLiteralOp op, Register rt, Address a,
+                          OperandSize sz) {
+    ASSERT((sz == kDoubleWord) || (sz == kWord));
+    ASSERT((rt != SP) && (rt != R31));
+    const Register crt = ConcreteRegister(rt);
+    const int32_t size = (sz == kDoubleWord) ? B30 : 0;
+    const int32_t encoding =
+        op | size |
+        (static_cast<int32_t>(crt) << kRtShift) |
+        a.encoding();
+    Emit(encoding);
+  }
+
+  void EmitPCRelOp(PCRelOp op, Register rd, int64_t imm) {
+    ASSERT(Utils::IsInt(21, imm));
+    ASSERT((rd != R31) && (rd != SP));
+    const Register crd = ConcreteRegister(rd);
+    const int32_t loimm = (imm & 0x3) << 29;
+    const int32_t hiimm = ((imm >> 2) << kImm19Shift) & kImm19Mask;
+    const int32_t encoding =
+        op | loimm | hiimm |
+        (static_cast<int32_t>(crd) << kRdShift);
+    Emit(encoding);
+  }
+
+  void EmitMiscDP2Source(MiscDP2SourceOp op,
+                         Register rd, Register rn, Register rm,
+                         OperandSize sz) {
+    ASSERT((rd != SP) && (rn != SP) && (rm != SP));
+    const Register crd = ConcreteRegister(rd);
+    const Register crn = ConcreteRegister(rn);
+    const Register crm = ConcreteRegister(rm);
+    const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+    const int32_t encoding =
+        op | size |
+        (static_cast<int32_t>(crd) << kRdShift) |
+        (static_cast<int32_t>(crn) << kRnShift) |
+        (static_cast<int32_t>(crm) << kRmShift);
+    Emit(encoding);
+  }
+
+  void EmitMiscDP3Source(MiscDP3SourceOp op,
+                         Register rd, Register rn, Register rm, Register ra,
+                         OperandSize sz) {
+    ASSERT((rd != SP) && (rn != SP) && (rm != SP) && (ra != SP));
+    const Register crd = ConcreteRegister(rd);
+    const Register crn = ConcreteRegister(rn);
+    const Register crm = ConcreteRegister(rm);
+    const Register cra = ConcreteRegister(ra);
+    const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+    const int32_t encoding =
+        op | size |
+        (static_cast<int32_t>(crd) << kRdShift) |
+        (static_cast<int32_t>(crn) << kRnShift) |
+        (static_cast<int32_t>(crm) << kRmShift) |
+        (static_cast<int32_t>(cra) << kRaShift);
+    Emit(encoding);
+  }
+
   DISALLOW_ALLOCATION();
   DISALLOW_COPY_AND_ASSIGN(Assembler);
 };
diff --git a/runtime/vm/assembler_arm64_test.cc b/runtime/vm/assembler_arm64_test.cc
index 72feb31..eb1006b 100644
--- a/runtime/vm/assembler_arm64_test.cc
+++ b/runtime/vm/assembler_arm64_test.cc
@@ -304,8 +304,8 @@
   __ movz(R0, 43, 0);
   __ movz(R1, 42, 0);
   __ add(R2, SP, Operand(1));
-  __ str(R1, Address(R2, -1, Address::PreIndex));
-  __ ldr(R0, Address(R2, -1, Address::PostIndex));
+  __ str(R1, Address(R2, -1));
+  __ ldr(R0, Address(R2, -1));
   __ ret();
 }
 
@@ -491,8 +491,6 @@
   EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
 }
 
-// TODO(zra): ands and bics after branches are implemented.
-
 
 // Logical immediate operations.
 ASSEMBLER_TEST_GENERATE(AndImm, assembler) {
@@ -553,7 +551,739 @@
   EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
 }
 
-// TODO(zra): andis after branches are implemented.
+
+// Comparisons, branching.
+ASSEMBLER_TEST_GENERATE(BranchALForward, assembler) {
+  Label l;
+  __ movz(R0, 42, 0);
+  __ b(&l, AL);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BranchALForward, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(BranchALBackwards, assembler) {
+  Label l, leave;
+  __ movz(R0, 42, 0);
+  __ b(&l, AL);
+
+  __ movz(R0, 0, 0);
+  __ Bind(&leave);
+  __ ret();
+  __ movz(R0, 0, 0);
+
+  __ Bind(&l);
+  __ b(&leave, AL);
+  __ movz(R0, 0, 0);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BranchALBackwards, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpEqBranch, assembler) {
+  Label l;
+
+  __ movz(R0, 42, 0);
+  __ movz(R1, 234, 0);
+  __ movz(R2, 234, 0);
+
+  __ cmp(R1, Operand(R2));
+  __ b(&l, EQ);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpEqBranch, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpEqBranchNotTaken, assembler) {
+  Label l;
+
+  __ movz(R0, 0, 0);
+  __ movz(R1, 233, 0);
+  __ movz(R2, 234, 0);
+
+  __ cmp(R1, Operand(R2));
+  __ b(&l, EQ);
+  __ movz(R0, 42, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpEqBranchNotTaken, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpEq1Branch, assembler) {
+  Label l;
+
+  __ movz(R0, 42, 0);
+  __ movz(R1, 1, 0);
+
+  __ cmp(R1, Operand(1));
+  __ b(&l, EQ);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpEq1Branch, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmnEq1Branch, assembler) {
+  Label l;
+
+  __ movz(R0, 42, 0);
+  __ movn(R1, 0, 0);  // R1 <- -1
+
+  __ cmn(R1, Operand(1));
+  __ b(&l, EQ);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmnEq1Branch, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpLtBranch, assembler) {
+  Label l;
+
+  __ movz(R0, 42, 0);
+  __ movz(R1, 233, 0);
+  __ movz(R2, 234, 0);
+
+  __ cmp(R1, Operand(R2));
+  __ b(&l, LT);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpLtBranch, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(CmpLtBranchNotTaken, assembler) {
+  Label l;
+
+  __ movz(R0, 0, 0);
+  __ movz(R1, 235, 0);
+  __ movz(R2, 234, 0);
+
+  __ cmp(R1, Operand(R2));
+  __ b(&l, LT);
+  __ movz(R0, 42, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(CmpLtBranchNotTaken, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndsBranch, assembler) {
+  Label l;
+
+  __ movz(R0, 42, 0);
+  __ movz(R1, 2, 0);
+  __ movz(R2, 1, 0);
+
+  __ ands(R3, R1, Operand(R2));
+  __ b(&l, EQ);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndsBranch, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndsBranchNotTaken, assembler) {
+  Label l;
+
+  __ movz(R0, 0, 0);
+  __ movz(R1, 2, 0);
+  __ movz(R2, 2, 0);
+
+  __ ands(R3, R1, Operand(R2));
+  __ b(&l, EQ);
+  __ movz(R0, 42, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndsBranchNotTaken, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(BicsBranch, assembler) {
+  Label l;
+
+  __ movz(R0, 42, 0);
+  __ movz(R1, 2, 0);
+  __ movz(R2, 2, 0);
+
+  __ bics(R3, R1, Operand(R2));
+  __ b(&l, EQ);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BicsBranch, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(BicsBranchNotTaken, assembler) {
+  Label l;
+
+  __ movz(R0, 0, 0);
+  __ movz(R1, 2, 0);
+  __ movz(R2, 1, 0);
+
+  __ bics(R3, R1, Operand(R2));
+  __ b(&l, EQ);
+  __ movz(R0, 42, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(BicsBranchNotTaken, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndisBranch, assembler) {
+  Label l;
+
+  __ movz(R0, 42, 0);
+  __ movz(R1, 2, 0);
+
+  __ andis(R3, R1, 1);
+  __ b(&l, EQ);
+  __ movz(R0, 0, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndisBranch, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AndisBranchNotTaken, assembler) {
+  Label l;
+
+  __ movz(R0, 0, 0);
+  __ movz(R1, 2, 0);
+
+  __ andis(R3, R1, 2);
+  __ b(&l, EQ);
+  __ movz(R0, 42, 0);
+  __ Bind(&l);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AndisBranchNotTaken, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Address of PC-rel offset, br, blr.
+ASSEMBLER_TEST_GENERATE(AdrBr, assembler) {
+  __ movz(R0, 123, 0);
+  __ adr(R1, 3 * Instr::kInstrSize);  // R1 <- PC + 3*Instr::kInstrSize
+  __ br(R1);
+  __ ret();
+
+  // br goes here.
+  __ movz(R0, 42, 0);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AdrBr, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(AdrBlr, assembler) {
+  __ movz(R0, 123, 0);
+  __ add(R3, ZR, Operand(LR));  // Save LR.
+  __ adr(R1, 4 * Instr::kInstrSize);  // R1 <- PC + 4*Instr::kInstrSize
+  __ blr(R1);
+  __ add(LR, ZR, Operand(R3));
+  __ ret();
+
+  // blr goes here.
+  __ movz(R0, 42, 0);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(AdrBlr, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Misc. arithmetic.
+ASSEMBLER_TEST_GENERATE(Udiv, assembler) {
+  __ movz(R0, 27, 0);
+  __ movz(R1, 9, 0);
+  __ udiv(R2, R0, R1);
+  __ mov(R0, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Udiv, test) {
+  EXPECT(test != NULL);
+  typedef int (*Tst)();
+  EXPECT_EQ(3, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Sdiv, assembler) {
+  __ movz(R0, 27, 0);
+  __ movz(R1, 9, 0);
+  __ neg(R1, R1);
+  __ sdiv(R2, R0, R1);
+  __ mov(R0, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Sdiv, test) {
+  EXPECT(test != NULL);
+  typedef int (*Tst)();
+  EXPECT_EQ(-3, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Udiv_zero, assembler) {
+  __ movz(R0, 27, 0);
+  __ movz(R1, 0, 0);
+  __ udiv(R2, R0, R1);
+  __ mov(R0, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Udiv_zero, test) {
+  EXPECT(test != NULL);
+  typedef int (*Tst)();
+  EXPECT_EQ(0, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Sdiv_zero, assembler) {
+  __ movz(R0, 27, 0);
+  __ movz(R1, 0, 0);
+  __ sdiv(R2, R0, R1);
+  __ mov(R0, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Sdiv_zero, test) {
+  EXPECT(test != NULL);
+  typedef int (*Tst)();
+  EXPECT_EQ(0, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Udiv_corner, assembler) {
+  __ movz(R0, 0x8000, 3);  // R0 <- 0x8000000000000000
+  __ movn(R1, 0, 0);  // R1 <- 0xffffffffffffffff
+  __ udiv(R2, R0, R1);
+  __ mov(R0, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Udiv_corner, test) {
+  EXPECT(test != NULL);
+  typedef int (*Tst)();
+  EXPECT_EQ(0, EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Sdiv_corner, assembler) {
+  __ movz(R3, 0x8000, 3);  // R0 <- 0x8000000000000000
+  __ movn(R1, 0, 0);  // R1 <- 0xffffffffffffffff
+  __ sdiv(R2, R3, R1);
+  __ mov(R0, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Sdiv_corner, test) {
+  EXPECT(test != NULL);
+  typedef int (*Tst)();
+  EXPECT_EQ(static_cast<int64_t>(0x8000000000000000),
+            EXECUTE_TEST_CODE_INT64(Tst, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Lslv, assembler) {
+  __ movz(R1, 21, 0);
+  __ movz(R2, 1, 0);
+  __ lslv(R0, R1, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Lslv, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Lsrv, assembler) {
+  __ movz(R1, 84, 0);
+  __ movz(R2, 1, 0);
+  __ lsrv(R0, R1, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Lsrv, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LShiftingV, assembler) {
+  __ movz(R1, 1, 0);
+  __ movz(R2, 63, 0);
+  __ lslv(R1, R1, R2);
+  __ lsrv(R0, R1, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LShiftingV, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(1, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(RShiftingV, assembler) {
+  __ movz(R1, 1, 0);
+  __ movz(R2, 63, 0);
+  __ lslv(R1, R1, R2);
+  __ asrv(R0, R1, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(RShiftingV, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(-1, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Mult_pos, assembler) {
+  __ movz(R1, 6, 0);
+  __ movz(R2, 7, 0);
+  __ mul(R0, R1, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Mult_pos, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(Mult_neg, assembler) {
+  __ movz(R1, 6, 0);
+  __ movz(R2, 7, 0);
+  __ neg(R2, R2);
+  __ mul(R0, R1, R2);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Mult_neg, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(-42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Loading immediate values without the object pool.
+ASSEMBLER_TEST_GENERATE(LoadImmediateSmall, assembler) {
+  __ LoadImmediate(R0, 42, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateSmall, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMed, assembler) {
+  __ LoadImmediate(R0, 0xf1234123, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMed, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(0xf1234123, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMed2, assembler) {
+  __ LoadImmediate(R0, 0x4321f1234123, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMed2, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(0x4321f1234123, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateLarge, assembler) {
+  __ LoadImmediate(R0, 0x9287436598237465, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateLarge, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(static_cast<int64_t>(0x9287436598237465),
+            EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateSmallNeg, assembler) {
+  __ LoadImmediate(R0, -42, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateSmallNeg, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(-42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg, assembler) {
+  __ LoadImmediate(R0, -0x1212341234, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(-0x1212341234, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg2, assembler) {
+  __ LoadImmediate(R0, -0x1212340000, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg2, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(-0x1212340000, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg3, assembler) {
+  __ LoadImmediate(R0, -0x1200001234, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg3, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(-0x1200001234, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediateMedNeg4, assembler) {
+  __ LoadImmediate(R0, -0x12341234, kNoRegister);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediateMedNeg4, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(-0x12341234, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// Loading immediate values with the object pool.
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPSmall, assembler) {
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadImmediate(R0, 42, PP);
+  __ PopPP();
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPSmall, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(42, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPMed, assembler) {
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadImmediate(R0, 0xf1234123, PP);
+  __ PopPP();
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPMed, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(0xf1234123, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPMed2, assembler) {
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadImmediate(R0, 0x4321f1234124, PP);
+  __ PopPP();
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPMed2, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(0x4321f1234124, EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadImmediatePPLarge, assembler) {
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadImmediate(R0, 0x9287436598237465, PP);
+  __ PopPP();
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadImmediatePPLarge, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(static_cast<int64_t>(0x9287436598237465),
+            EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+// LoadObject null.
+ASSEMBLER_TEST_GENERATE(LoadObjectNull, assembler) {
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadObject(R0, Object::null_object(), PP);
+  __ PopPP();
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadObjectNull, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(reinterpret_cast<int64_t>(Object::null()),
+            EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadObjectTrue, assembler) {
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadObject(R0, Bool::True(), PP);
+  __ PopPP();
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadObjectTrue, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(reinterpret_cast<int64_t>(Bool::True().raw()),
+            EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
+
+
+ASSEMBLER_TEST_GENERATE(LoadObjectFalse, assembler) {
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadObject(R0, Bool::False(), PP);
+  __ PopPP();
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(LoadObjectFalse, test) {
+  typedef int (*SimpleCode)();
+  EXPECT_EQ(reinterpret_cast<int64_t>(Bool::False().raw()),
+            EXECUTE_TEST_CODE_INT64(SimpleCode, test->entry()));
+}
 
 }  // namespace dart
 
diff --git a/runtime/vm/assembler_arm_test.cc b/runtime/vm/assembler_arm_test.cc
index 76cb9b6..c020887 100644
--- a/runtime/vm/assembler_arm_test.cc
+++ b/runtime/vm/assembler_arm_test.cc
@@ -1530,7 +1530,7 @@
   if (TargetCPUFeatures::integer_division_supported()) {
     __ mov(R0, ShifterOperand(27));
     __ mov(R1, ShifterOperand(0));
-    __ udiv(R2, R0, R1);
+    __ sdiv(R2, R0, R1);
     __ mov(R0, ShifterOperand(R2));
   } else {
     __ LoadImmediate(R0, 0);
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index 522928e..cd74865 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -665,6 +665,10 @@
   void MoveRegister(Register to, Register from);
   void PopRegister(Register r);
 
+  // Macros accepting a pp Register argument may attempt to load values from
+  // the object pool when possible. Unless you are sure that the untagged object
+  // pool pointer is in another register, or that it is not available at all,
+  // PP should be passed for pp.
   void AddImmediate(Register reg, const Immediate& imm, Register pp);
   void AddImmediate(const Address& address, const Immediate& imm, Register pp);
 
diff --git a/runtime/vm/bit_vector.h b/runtime/vm/bit_vector.h
index 62cf753..69a9d3f 100644
--- a/runtime/vm/bit_vector.h
+++ b/runtime/vm/bit_vector.h
@@ -12,7 +12,7 @@
 namespace dart {
 
 // Bit vector implementation.
-class BitVector: public ZoneAllocated {
+class BitVector : public ZoneAllocated {
  public:
   // Iterator for the elements of this BitVector.
   class Iterator : public ValueObject {
diff --git a/runtime/vm/bootstrap.cc b/runtime/vm/bootstrap.cc
index cee8b72..d12752f 100644
--- a/runtime/vm/bootstrap.cc
+++ b/runtime/vm/bootstrap.cc
@@ -67,6 +67,11 @@
                Bootstrap::typed_data_source_paths_,
                Bootstrap::typed_data_patch_paths_),
 
+  INIT_LIBRARY(ObjectStore::kProfiler,
+               profiler,
+               Bootstrap::profiler_source_paths_,
+               Bootstrap::profiler_patch_paths_),
+
   { ObjectStore::kNone, NULL, NULL, NULL, NULL }
 };
 
diff --git a/runtime/vm/bootstrap.h b/runtime/vm/bootstrap.h
index 5d3e53b..9a3c09b 100644
--- a/runtime/vm/bootstrap.h
+++ b/runtime/vm/bootstrap.h
@@ -30,6 +30,7 @@
   static const char* math_source_paths_[];
   static const char* mirrors_source_paths_[];
   static const char* typed_data_source_paths_[];
+  static const char* profiler_source_paths_[];
   static const char* utf_source_paths_[];
 
   // Source path mapping for patch URI and 'parts'.
@@ -42,6 +43,7 @@
   static const char* math_patch_paths_[];
   static const char* mirrors_patch_paths_[];
   static const char* typed_data_patch_paths_[];
+  static const char* profiler_patch_paths_[];
 };
 
 }  // namespace dart
diff --git a/runtime/vm/bootstrap_natives.cc b/runtime/vm/bootstrap_natives.cc
index 4682e98..6efff89 100644
--- a/runtime/vm/bootstrap_natives.cc
+++ b/runtime/vm/bootstrap_natives.cc
@@ -53,39 +53,67 @@
 }
 
 
+const uint8_t* BootstrapNatives::Symbol(Dart_NativeFunction* nf) {
+  int num_entries = sizeof(BootStrapEntries) / sizeof(struct NativeEntries);
+  for (int i = 0; i < num_entries; i++) {
+    struct NativeEntries* entry = &(BootStrapEntries[i]);
+    if (reinterpret_cast<Dart_NativeFunction*>(entry->function_) == nf) {
+      return reinterpret_cast<const uint8_t*>(entry->name_);
+    }
+  }
+  return NULL;
+}
+
+
 void Bootstrap::SetupNativeResolver() {
   Library& library = Library::Handle();
 
   Dart_NativeEntryResolver resolver =
       reinterpret_cast<Dart_NativeEntryResolver>(BootstrapNatives::Lookup);
 
+  Dart_NativeEntrySymbol symbol_resolver =
+      reinterpret_cast<Dart_NativeEntrySymbol>(
+          BootstrapNatives::Symbol);
+
   library = Library::AsyncLibrary();
   ASSERT(!library.IsNull());
   library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
 
   library = Library::CoreLibrary();
   ASSERT(!library.IsNull());
   library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
 
   library = Library::MathLibrary();
   ASSERT(!library.IsNull());
   library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
 
   library = Library::MirrorsLibrary();
   ASSERT(!library.IsNull());
   library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
 
   library = Library::InternalLibrary();
   ASSERT(!library.IsNull());
   library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
 
   library = Library::IsolateLibrary();
   ASSERT(!library.IsNull());
   library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
 
   library = Library::TypedDataLibrary();
   ASSERT(!library.IsNull());
   library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
+
+  library = Library::ProfilerLibrary();
+  ASSERT(!library.IsNull());
+  library.set_native_entry_resolver(resolver);
+  library.set_native_entry_symbol_resolver(symbol_resolver);
 }
 
 
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index e6038e0..c4e6ea91 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -348,6 +348,11 @@
   V(WeakProperty_setValue, 2)                                                  \
   V(Uri_isWindowsPlatform, 0)                                                  \
   V(LibraryPrefix_load, 1)                                                     \
+  V(UserTag_new, 2)                                                            \
+  V(UserTag_label, 1)                                                          \
+  V(UserTag_makeCurrent, 1)                                                    \
+  V(Profiler_getCurrentTag, 0)                                                 \
+  V(Profiler_clearCurrentTag, 0)                                               \
 
 
 class BootstrapNatives : public AllStatic {
@@ -356,6 +361,8 @@
                                     int argument_count,
                                     bool* auto_setup_scope);
 
+  static const uint8_t* Symbol(Dart_NativeFunction* nf);
+
 #define DECLARE_BOOTSTRAP_NATIVE(name, ignored)                                \
   static void DN_##name(Dart_NativeArguments args);
 
diff --git a/runtime/vm/class_table.cc b/runtime/vm/class_table.cc
index c461a5e..7909d8d 100644
--- a/runtime/vm/class_table.cc
+++ b/runtime/vm/class_table.cc
@@ -89,6 +89,10 @@
       class_heap_stats_table_ = new_stats_table;
     }
     ASSERT(top_ < capacity_);
+    if (!Class::is_valid_id(top_)) {
+      FATAL1("Fatal error in ClassTable::Register: invalid index %" Pd "\n",
+             top_);
+    }
     cls.set_id(top_);
     table_[top_] = cls.raw();
     top_++;  // Increment next index.
@@ -102,7 +106,7 @@
   if (index >= capacity_) {
     // Grow the capacity of the class table.
     intptr_t new_capacity = index + capacity_increment_;
-    if (new_capacity < capacity_) {
+    if (!Class::is_valid_id(index) || new_capacity < capacity_) {
       FATAL1("Fatal error in ClassTable::Register: invalid index %" Pd "\n",
              index);
     }
diff --git a/runtime/vm/code_generator.cc b/runtime/vm/code_generator.cc
index 0266e1b..1b95b95 100644
--- a/runtime/vm/code_generator.cc
+++ b/runtime/vm/code_generator.cc
@@ -950,21 +950,6 @@
 }
 
 
-// A non-closure object was invoked as a closure, so call the "call" method
-// on it.
-// Arg0: arguments descriptor.
-// Arg1: arguments array, including non-closure object.
-DEFINE_RUNTIME_ENTRY(InvokeNonClosure, 2) {
-  const Array& args_descriptor = Array::CheckedHandle(arguments.ArgAt(0));
-  const Array& function_args = Array::CheckedHandle(arguments.ArgAt(1));
-
-  const Object& result = Object::Handle(
-      DartEntry::InvokeClosure(function_args, args_descriptor));
-  CheckResultError(result);
-  arguments.SetReturn(result);
-}
-
-
 static bool CanOptimizeFunction(const Function& function, Isolate* isolate) {
   const intptr_t kLowInvocationCount = -100000000;
   if (isolate->debugger()->IsStepping() ||
diff --git a/runtime/vm/code_generator.h b/runtime/vm/code_generator.h
index aeb6357..e9a8dbd 100644
--- a/runtime/vm/code_generator.h
+++ b/runtime/vm/code_generator.h
@@ -39,7 +39,6 @@
 DECLARE_RUNTIME_ENTRY(OptimizeInvokedFunction);
 DECLARE_RUNTIME_ENTRY(TraceICCall);
 DECLARE_RUNTIME_ENTRY(PatchStaticCall);
-DECLARE_RUNTIME_ENTRY(InvokeNonClosure);
 DECLARE_RUNTIME_ENTRY(ReThrow);
 DECLARE_RUNTIME_ENTRY(StackOverflow);
 DECLARE_RUNTIME_ENTRY(Throw);
diff --git a/runtime/vm/code_patcher_arm64.cc b/runtime/vm/code_patcher_arm64.cc
index 2e81384..1e03d79 100644
--- a/runtime/vm/code_patcher_arm64.cc
+++ b/runtime/vm/code_patcher_arm64.cc
@@ -14,58 +14,72 @@
 
 RawArray* CodePatcher::GetClosureArgDescAt(uword return_address,
                                            const Code& code) {
-  UNIMPLEMENTED();
-  return NULL;
+  ASSERT(code.ContainsInstructionAt(return_address));
+  CallPattern call(return_address, code);
+  return call.ClosureArgumentsDescriptor();
 }
 
 
 uword CodePatcher::GetStaticCallTargetAt(uword return_address,
                                          const Code& code) {
-  UNIMPLEMENTED();
-  return 0;
+  ASSERT(code.ContainsInstructionAt(return_address));
+  CallPattern call(return_address, code);
+  return call.TargetAddress();
 }
 
 
 void CodePatcher::PatchStaticCallAt(uword return_address,
                                     const Code& code,
                                     uword new_target) {
-  UNIMPLEMENTED();
+  ASSERT(code.ContainsInstructionAt(return_address));
+  CallPattern call(return_address, code);
+  call.SetTargetAddress(new_target);
 }
 
 
 void CodePatcher::PatchInstanceCallAt(uword return_address,
                                       const Code& code,
                                       uword new_target) {
-  UNIMPLEMENTED();
+  ASSERT(code.ContainsInstructionAt(return_address));
+  CallPattern call(return_address, code);
+  call.SetTargetAddress(new_target);
 }
 
 
 int32_t CodePatcher::GetPoolOffsetAt(uword return_address) {
+  // TODO(zra): Needed for debugger.
   UNIMPLEMENTED();
   return 0;
 }
 
 
 void CodePatcher::SetPoolOffsetAt(uword return_address, int32_t offset) {
+  // TODO(zra): Needed for debugger.
   UNIMPLEMENTED();
 }
 
 
 void CodePatcher::InsertCallAt(uword start, uword target) {
-  UNIMPLEMENTED();
+  // The inserted call should not overlap the lazy deopt jump code.
+  ASSERT(start + CallPattern::kLengthInBytes <= target);
+  CallPattern::InsertAt(start, target);
 }
 
 
 uword CodePatcher::GetInstanceCallAt(uword return_address,
                                      const Code& code,
                                      ICData* ic_data) {
-  UNIMPLEMENTED();
-  return 0;
+  ASSERT(code.ContainsInstructionAt(return_address));
+  CallPattern call(return_address, code);
+  if (ic_data != NULL) {
+    *ic_data = call.IcData();
+  }
+  return call.TargetAddress();
 }
 
 
 intptr_t CodePatcher::InstanceCallSizeInBytes() {
-  // The instance call instruction sequence has a variable size on ARM.
+  // The instance call instruction sequence has a variable size on ARM64.
   UNREACHABLE();
   return 0;
 }
@@ -73,8 +87,14 @@
 
 RawFunction* CodePatcher::GetUnoptimizedStaticCallAt(
     uword return_address, const Code& code, ICData* ic_data_result) {
-  UNIMPLEMENTED();
-  return NULL;
+  ASSERT(code.ContainsInstructionAt(return_address));
+  CallPattern static_call(return_address, code);
+  ICData& ic_data = ICData::Handle();
+  ic_data ^= static_call.IcData();
+  if (ic_data_result != NULL) {
+    *ic_data_result = ic_data.raw();
+  }
+  return ic_data.GetTargetAt(0);
 }
 
 
diff --git a/runtime/vm/code_patcher_arm64_test.cc b/runtime/vm/code_patcher_arm64_test.cc
index 7c2fe8f..32970b2 100644
--- a/runtime/vm/code_patcher_arm64_test.cc
+++ b/runtime/vm/code_patcher_arm64_test.cc
@@ -5,6 +5,59 @@
 #include "vm/globals.h"
 #if defined(TARGET_ARCH_ARM64)
 
-// TODO(zra): Port these tests.
+#include "vm/assembler.h"
+#include "vm/code_generator.h"
+#include "vm/code_patcher.h"
+#include "vm/dart_entry.h"
+#include "vm/instructions.h"
+#include "vm/native_entry.h"
+#include "vm/native_entry_test.h"
+#include "vm/stub_code.h"
+#include "vm/symbols.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#define __ assembler->
+
+ASSEMBLER_TEST_GENERATE(IcDataAccess, assembler) {
+  const String& class_name = String::Handle(Symbols::New("ownerClass"));
+  const Script& script = Script::Handle();
+  const Class& owner_class =
+      Class::Handle(Class::New(class_name, script, Scanner::kNoSourcePos));
+  const String& function_name = String::Handle(Symbols::New("callerFunction"));
+  const Function& function = Function::Handle(
+      Function::New(function_name, RawFunction::kRegularFunction,
+                    true, false, false, false, false, owner_class, 0));
+
+  const String& target_name = String::Handle(String::New("targetFunction"));
+  const Array& args_descriptor =
+      Array::Handle(ArgumentsDescriptor::New(1, Object::null_array()));
+  const ICData& ic_data = ICData::ZoneHandle(ICData::New(function,
+                                                         target_name,
+                                                         args_descriptor,
+                                                         15,
+                                                         1));
+
+  __ LoadObject(R5, ic_data, PP);
+  ExternalLabel target_label(
+      "InlineCache", StubCode::OneArgCheckInlineCacheEntryPoint());
+  __ BranchLinkPatchable(&target_label);
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(IcDataAccess, test) {
+  uword return_address =
+      test->entry() + test->code().Size() - Instr::kInstrSize;
+  ICData& ic_data = ICData::Handle();
+  CodePatcher::GetInstanceCallAt(return_address, test->code(), &ic_data);
+  EXPECT_STREQ("targetFunction",
+      String::Handle(ic_data.target_name()).ToCString());
+  EXPECT_EQ(1, ic_data.num_args_tested());
+  EXPECT_EQ(0, ic_data.NumberOfChecks());
+}
+
+}  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/constants_arm.h b/runtime/vm/constants_arm.h
index c1baf8d..d1afca8 100644
--- a/runtime/vm/constants_arm.h
+++ b/runtime/vm/constants_arm.h
@@ -193,6 +193,7 @@
 
 // Register aliases.
 const Register TMP = IP;  // Used as scratch register by assembler.
+const Register TMP2 = kNoRegister;  // There is no second assembler temporary.
 const Register CTX = R9;  // Caches current context in generated code.
 const Register PP = R10;  // Caches object pool pointer in generated code.
 const Register SPREG = SP;  // Stack pointer register.
diff --git a/runtime/vm/constants_arm64.h b/runtime/vm/constants_arm64.h
index 308a279..d16cf72 100644
--- a/runtime/vm/constants_arm64.h
+++ b/runtime/vm/constants_arm64.h
@@ -27,8 +27,8 @@
   R13 = 13,
   R14 = 14,
   R15 = 15,
-  R16 = 16,
-  R17 = 17,
+  R16 = 16,  // IP0 aka TMP
+  R17 = 17,  // IP1 aka TMP2
   R18 = 18,
   R19 = 19,
   R20 = 20,
@@ -36,9 +36,9 @@
   R22 = 22,
   R23 = 23,
   R24 = 24,
-  kLastFreeCpuRegister = 24,
-  R25 = 25,  // IP0
-  R26 = 26,  // IP1
+  R25 = 25,
+  R26 = 26,
+  kLastFreeCpuRegister = 26,
   R27 = 27,  // PP
   R28 = 28,  // CTX
   R29 = 29,  // FP
@@ -53,8 +53,8 @@
   ZR = 33,
 
   // Aliases.
-  IP0 = R25,
-  IP1 = R26,
+  IP0 = R16,
+  IP1 = R17,
   FP = R29,
   LR = R30,
 };
@@ -107,13 +107,12 @@
 const FpuRegister kNoFpuRegister = kNoVRegister;
 
 // Register aliases.
-const Register TMP = R25;  // Used as scratch register by assembler.
-const Register TMP0 = R25;
-const Register TMP1 = R26;
-const Register CTX = R27;  // Caches current context in generated code.
-const Register PP = R26;  // Caches object pool pointer in generated code.
-const Register SPREG = R31;  // Stack pointer register.
+const Register TMP = R16;  // Used as scratch register by assembler.
+const Register TMP2 = R17;
+const Register CTX = R28;  // Caches current context in generated code.
+const Register PP = R27;  // Caches object pool pointer in generated code.
 const Register FPREG = FP;  // Frame pointer register.
+const Register SPREG = R31;  // Stack pointer register.
 const Register ICREG = R5;  // IC data register.
 
 // Exception object is passed in this register to the catch handlers when an
@@ -142,8 +141,8 @@
 const RegList kAbiPreservedCpuRegs =
     (1 << R19) | (1 << R20) | (1 << R21) | (1 << R22) |
     (1 << R23) | (1 << R24) | (1 << R25) | (1 << R26) |
-    (1 << R27) | (1 << R28) | (1 << R29);
-const int kAbiPreservedCpuRegCount = 11;
+    (1 << R27) | (1 << R28);
+const int kAbiPreservedCpuRegCount = 10;
 const VRegister kAbiFirstPreservedFpuReg = V8;
 const VRegister kAbiLastPreservedFpuReg = V15;
 const int kAbiPreservedFpuRegCount = 8;
@@ -154,14 +153,14 @@
     (1 << R4)  | (1 << R5)  | (1 << R6)  | (1 << R7)  |
     (1 << R8)  | (1 << R9)  | (1 << R10) | (1 << R11) |
     (1 << R12) | (1 << R13) | (1 << R14) | (1 << R15) |
-    (1 << R16) | (1 << R17) | (1 << R18) | (1 << R19) |
-    (1 << R20) | (1 << R21) | (1 << R22) | (1 << R23) |
-    (1 << R24);
+    (1 << R18) | (1 << R19) | (1 << R20) | (1 << R21) |
+    (1 << R22) | (1 << R23) | (1 << R24) | (1 << R25) |
+    (1 << R26);
 
 // Registers available to Dart that are not preserved by runtime calls.
 const RegList kDartVolatileCpuRegs =
     kDartAvailableCpuRegs & ~kAbiPreservedCpuRegs;
-const int kDartVolatileCpuRegCount = 19;
+const int kDartVolatileCpuRegCount = 17;
 const VRegister kDartFirstVolatileFpuReg = V0;
 const VRegister kDartLastVolatileFpuReg = V7;
 const int kDartVolatileFpuRegCount = 8;
@@ -259,6 +258,21 @@
   DPSimd2Fixed = B28 | DPSimd1Fixed,
 };
 
+// C3.2.1
+enum CompareAndBranchOp {
+  CompareAndBranchMask = 0x7e000000,
+  CompareAndBranchFixed = CompareBranchFixed | B29,
+  CBZ = CompareBranchFixed,
+  CBNZ = CompareBranchFixed | B24,
+};
+
+// C.3.2.2
+enum ConditionalBranchOp {
+  ConditionalBranchMask = 0xfe000000,
+  ConditionalBranchFixed = CompareBranchFixed | B30,
+  BCOND = ConditionalBranchFixed,
+};
+
 // C3.2.3
 enum ExceptionGenOp {
   ExceptionGenMask = 0xff000000,
@@ -275,6 +289,22 @@
   HINT = SystemFixed | B17 | B16 | B13 | B4 | B3 | B2 | B1 | B0,
 };
 
+// C3.2.5
+enum TestAndBranchOp {
+  TestAndBranchMask = 0x7e000000,
+  TestAndBranchFixed = CompareBranchFixed | B29 | B25,
+  TBZ = TestAndBranchFixed,
+  TBNZ = TestAndBranchFixed | B24,
+};
+
+// C3.2.6
+enum UnconditionalBranchOp {
+  UnconditionalBranchMask = 0x7c000000,
+  UnconditionalBranchFixed = CompareBranchFixed,
+  B = UnconditionalBranchFixed,
+  BL = UnconditionalBranchFixed | B31,
+};
+
 // C3.2.7
 enum UnconditionalBranchRegOp {
   UnconditionalBranchRegMask = 0xfe000000,
@@ -291,6 +321,13 @@
   LDR = LoadStoreRegFixed | B22,
 };
 
+// C3.3.5
+enum LoadRegLiteralOp {
+  LoadRegLiteralMask = 0x3b000000,
+  LoadRegLiteralFixed = LoadStoreFixed | B28,
+  LDRpc = LoadRegLiteralFixed,
+};
+
 // C3.4.1
 enum AddSubImmOp {
   AddSubImmMask = 0x1f000000,
@@ -318,6 +355,14 @@
   MOVK = MoveWideFixed | B30 | B29,
 };
 
+// C3.4.6
+enum PCRelOp {
+  PCRelMask = 0x1f000000,
+  PCRelFixed = DPImmediateFixed,
+  ADR = PCRelFixed,
+  ADRP = PCRelFixed | B31,
+};
+
 // C3.5.1
 enum AddSubShiftExtOp {
   AddSubShiftExtMask = 0x1f000000,
@@ -326,6 +371,25 @@
   SUB = AddSubShiftExtFixed | B30,
 };
 
+// C3.5.8
+enum MiscDP2SourceOp {
+  MiscDP2SourceMask = 0x5fe00000,
+  MiscDP2SourceFixed = DPRegisterFixed | B28 | B23 | B22,
+  UDIV = MiscDP2SourceFixed | B11,
+  SDIV = MiscDP2SourceFixed | B11 | B10,
+  LSLV = MiscDP2SourceFixed | B13,
+  LSRV = MiscDP2SourceFixed | B13 | B10,
+  ASRV = MiscDP2SourceFixed | B13 | B11,
+};
+
+// C3.5.9
+enum MiscDP3SourceOp {
+  MiscDP3SourceMask = 0x1f000000,
+  MiscDP3SourceFixed = DPRegisterFixed | B28 | B24,
+  MADD = MiscDP3SourceFixed,
+};
+
+// C3.5.10
 enum LogicalShiftOp {
   LogicalShiftMask = 0x1f000000,
   LogicalShiftFixed = DPRegisterFixed,
@@ -346,14 +410,22 @@
 _V(DPRegister)                                                                 \
 _V(DPSimd1)                                                                    \
 _V(DPSimd2)                                                                    \
+_V(CompareAndBranch)                                                           \
+_V(ConditionalBranch)                                                          \
 _V(ExceptionGen)                                                               \
 _V(System)                                                                     \
-_V(LoadStoreReg)                                                               \
+_V(TestAndBranch)                                                              \
+_V(UnconditionalBranch)                                                        \
 _V(UnconditionalBranchReg)                                                     \
+_V(LoadStoreReg)                                                               \
+_V(LoadRegLiteral)                                                             \
 _V(AddSubImm)                                                                  \
 _V(LogicalImm)                                                                 \
 _V(MoveWide)                                                                   \
+_V(PCRel)                                                                      \
 _V(AddSubShiftExt)                                                             \
+_V(MiscDP2Source)                                                              \
+_V(MiscDP3Source)                                                              \
 _V(LogicalShift)                                                               \
 
 
@@ -382,7 +454,6 @@
 enum R31Type {
   R31IsSP,
   R31IsZR,
-  R31IsUndef,
 };
 
 // Constants used for the decoding or encoding of the individual fields of
@@ -423,8 +494,19 @@
   kImm12Bits = 12,
   kImm12ShiftShift = 22,
   kImm12ShiftBits = 2,
+  kImm14Shift = 5,
+  kImm14Bits = 14,
   kImm16Shift = 5,
   kImm16Bits = 16,
+  kImm16Mask = 0xffff << kImm16Shift,
+  kImm19Shift = 5,
+  kImm19Bits = 19,
+  kImm19Mask = 0x7ffff << kImm19Shift,
+  kImm26Shift = 0,
+  kImm26Bits = 26,
+
+  kCondShift = 0,
+  kCondBits = 4,
 
   // Bitfield immediates.
   kNShift = 22,
@@ -455,8 +537,8 @@
 
 const uint32_t kImmExceptionIsRedirectedCall = 0xca11;
 const uint32_t kImmExceptionIsUnreachable = 0xdebf;
-const uint32_t kImmExceptionIsPrintf = 0xdeb1;
 const uint32_t kImmExceptionIsDebug = 0xdeb0;
+const uint32_t kImmExceptionIsPrintf = 0xdeb1;
 
 // Helper functions for decoding logical immediates.
 static inline uint64_t RotateRight(
@@ -502,6 +584,18 @@
   static const int32_t kBreakPointInstruction =  // hlt #kImmExceptionIsDebug.
       HLT | (kImmExceptionIsDebug << kImm16Shift);
   static const int kBreakPointInstructionSize = kInstrSize;
+  static const int32_t kRedirectInstruction =
+      HLT | (kImmExceptionIsRedirectedCall << kImm16Shift);
+
+  // Read one particular bit out of the instruction bits.
+  inline int Bit(int nr) const {
+    return (InstructionBits() >> nr) & 1;
+  }
+
+  // Read a bit field out of the instruction bits.
+  inline int Bits(int shift, int count) const {
+    return (InstructionBits() >> shift) & ((1 << count) - 1);
+  }
 
   // Get the raw instruction bits.
   inline int32_t InstructionBits() const {
@@ -513,14 +607,23 @@
     *reinterpret_cast<int32_t*>(this) = value;
   }
 
-  // Read one particular bit out of the instruction bits.
-  inline int Bit(int nr) const {
-    return (InstructionBits() >> nr) & 1;
+  inline void SetMoveWideBits(
+      MoveWideOp op, Register rd, uint16_t imm, int hw, OperandSize sz) {
+    ASSERT((hw >= 0) && (hw <= 3));
+    ASSERT((sz == kDoubleWord) || (sz == kWord));
+    const int32_t size = (sz == kDoubleWord) ? B31 : 0;
+    SetInstructionBits(
+        op | size |
+        (static_cast<int32_t>(rd) << kRdShift) |
+        (static_cast<int32_t>(hw) << kHWShift) |
+        (static_cast<int32_t>(imm) << kImm16Shift));
   }
 
-  // Read a bit field out of the instruction bits.
-  inline int Bits(int shift, int count) const {
-    return (InstructionBits() >> shift) & ((1 << count) - 1);
+  inline void SetUnconditionalBranchRegBits(
+      UnconditionalBranchRegOp op, Register rn) {
+    SetInstructionBits(
+        op |
+        (static_cast<int32_t>(rn) << kRnShift));
   }
 
   inline int NField() const { return Bit(22); }
@@ -557,6 +660,20 @@
   inline int ImmRField() const { return Bits(kImmRShift, kImmRBits); }
   inline int ImmSField() const { return Bits(kImmSShift, kImmSBits); }
 
+  inline int Imm14Field() const { return Bits(kImm14Shift, kImm14Bits); }
+  inline int64_t SImm14Field() const {
+      return (static_cast<int32_t>(Imm14Field()) << 18) >> 18; }
+  inline int Imm19Field() const { return Bits(kImm19Shift, kImm19Bits); }
+  inline int64_t SImm19Field() const {
+      return (static_cast<int32_t>(Imm19Field()) << 13) >> 13; }
+  inline int Imm26Field() const { return Bits(kImm26Shift, kImm26Bits); }
+  inline int64_t SImm26Field() const {
+      return (static_cast<int32_t>(Imm26Field()) << 6) >> 6; }
+
+  inline Condition ConditionField() const {
+    return static_cast<Condition>(Bits(kCondShift, kCondBits));
+  }
+
   // Shift and Extend.
   inline bool IsShift() const {
     return IsLogicalShiftOp() || (Bit(kAddShiftExtendShift) == 0);
diff --git a/runtime/vm/constants_ia32.h b/runtime/vm/constants_ia32.h
index acc1898..6593d23 100644
--- a/runtime/vm/constants_ia32.h
+++ b/runtime/vm/constants_ia32.h
@@ -61,6 +61,7 @@
 
 // Register aliases.
 const Register TMP = kNoRegister;  // No scratch register used by assembler.
+const Register TMP2 = kNoRegister;  // No second assembler scratch register.
 const Register CTX = ESI;  // Caches current context in generated code.
 const Register PP = kNoRegister;  // No object pool pointer.
 const Register SPREG = ESP;  // Stack pointer register.
diff --git a/runtime/vm/constants_mips.h b/runtime/vm/constants_mips.h
index 9aafaa4..4fa1898 100644
--- a/runtime/vm/constants_mips.h
+++ b/runtime/vm/constants_mips.h
@@ -176,6 +176,7 @@
 
 // Register aliases.
 const Register TMP = AT;  // Used as scratch register by assembler.
+const Register TMP2 = kNoRegister;  // No second assembler scratch register.
 const Register CTX = S6;  // Caches current context in generated code.
 const Register PP = S7;  // Caches object pool pointer in generated code.
 const Register SPREG = SP;  // Stack pointer register.
diff --git a/runtime/vm/constants_x64.h b/runtime/vm/constants_x64.h
index 6839b67..f5e76ef 100644
--- a/runtime/vm/constants_x64.h
+++ b/runtime/vm/constants_x64.h
@@ -85,6 +85,7 @@
 
 // Register aliases.
 const Register TMP = R11;  // Used as scratch register by the assembler.
+const Register TMP2 = kNoRegister;  // No second assembler scratch register.
 const Register CTX = R14;  // Caches current context in generated code.
 // Caches object pool pointer in generated code.
 const Register PP = R15;
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index e03377d..3a31cec 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -101,6 +101,7 @@
   CodeObservers::InitOnce();
   ThreadInterrupter::InitOnce();
   Profiler::InitOnce();
+
 #if defined(USING_SIMULATOR)
   Simulator::InitOnce();
 #endif
@@ -249,7 +250,14 @@
   if (FLAG_print_class_table) {
     isolate->class_table()->Print();
   }
+
+  // Setup for profiling.
+  Profiler::InitProfilingForIsolate(isolate);
+
   Service::SendIsolateStartupMessage();
+  // Create tag table.
+  isolate->set_tag_table(
+      GrowableObjectArray::Handle(GrowableObjectArray::New()));
   return Error::null();
 }
 
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 7522f73..71c4de0 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -93,6 +93,16 @@
 }
 
 
+WeakReferenceSetBuilder* ApiState::NewWeakReferenceSetBuilder() {
+  return new WeakReferenceSetBuilder(this);
+}
+
+
+void ApiState::DelayWeakReferenceSet(WeakReferenceSet* reference_set) {
+  WeakReferenceSet::Push(reference_set, &delayed_weak_reference_sets_);
+}
+
+
 Dart_Handle Api::InitNewHandle(Isolate* isolate, RawObject* raw) {
   LocalHandles* local_handles = Api::TopScope(isolate)->local_handles();
   ASSERT(local_handles != NULL);
@@ -742,35 +752,62 @@
 }
 
 
-DART_EXPORT Dart_Handle Dart_NewWeakReferenceSet(
-    Dart_WeakPersistentHandle* keys,
-    intptr_t num_keys,
-    Dart_WeakPersistentHandle* values,
-    intptr_t num_values) {
+DART_EXPORT Dart_WeakReferenceSetBuilder Dart_NewWeakReferenceSetBuilder() {
   Isolate* isolate = Isolate::Current();
   CHECK_ISOLATE(isolate);
   ApiState* state = isolate->api_state();
   ASSERT(state != NULL);
-  if (keys == NULL) {
-    RETURN_NULL_ERROR(keys);
-  }
-  if (num_keys <= 0) {
-    return Api::NewError(
-        "%s expects argument 'num_keys' to be greater than 0.",
-        CURRENT_FUNC);
-  }
-  if (values == NULL) {
-    RETURN_NULL_ERROR(values);
-  }
-  if (num_values <= 0) {
-    return Api::NewError(
-        "%s expects argument 'num_values' to be greater than 0.",
-        CURRENT_FUNC);
-  }
+  return reinterpret_cast<Dart_WeakReferenceSetBuilder>(
+      state->NewWeakReferenceSetBuilder());
+}
 
-  WeakReferenceSet* reference_set = new WeakReferenceSet(keys, num_keys,
-                                                         values, num_values);
+
+DART_EXPORT Dart_WeakReferenceSet Dart_NewWeakReferenceSet(
+    Dart_WeakReferenceSetBuilder set_builder,
+    Dart_WeakPersistentHandle key,
+    Dart_WeakPersistentHandle value) {
+  ASSERT(set_builder != NULL && key != NULL);
+  WeakReferenceSetBuilder* builder =
+      reinterpret_cast<WeakReferenceSetBuilder*>(set_builder);
+  ApiState* state = builder->api_state();
+  ASSERT(state == Isolate::Current()->api_state());
+  WeakReferenceSet* reference_set = builder->NewWeakReferenceSet();
+  reference_set->AppendKey(key);
+  if (value != NULL) {
+    reference_set->AppendValue(value);
+  }
   state->DelayWeakReferenceSet(reference_set);
+  return reinterpret_cast<Dart_WeakReferenceSet>(reference_set);
+}
+
+
+DART_EXPORT Dart_Handle Dart_AppendToWeakReferenceSet(
+    Dart_WeakReferenceSet reference_set,
+    Dart_WeakPersistentHandle key,
+    Dart_WeakPersistentHandle value) {
+  ASSERT(reference_set != NULL);
+  WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set);
+  set->Append(key, value);
+  return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_AppendKeyToWeakReferenceSet(
+    Dart_WeakReferenceSet reference_set,
+    Dart_WeakPersistentHandle key) {
+  ASSERT(reference_set != NULL);
+  WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set);
+  set->AppendKey(key);
+  return Api::Success();
+}
+
+
+DART_EXPORT Dart_Handle Dart_AppendValueToWeakReferenceSet(
+    Dart_WeakReferenceSet reference_set,
+    Dart_WeakPersistentHandle value) {
+  ASSERT(reference_set != NULL);
+  WeakReferenceSet* set = reinterpret_cast<WeakReferenceSet*>(reference_set);
+  set->AppendValue(value);
   return Api::Success();
 }
 
@@ -816,6 +853,52 @@
 }
 
 
+class PrologueWeakVisitor : public HandleVisitor {
+ public:
+  PrologueWeakVisitor(Isolate* isolate,
+                      Dart_GcPrologueWeakHandleCallback callback)
+      :  HandleVisitor(isolate),
+         callback_(callback) {
+  }
+
+  void VisitHandle(uword addr) {
+    NoGCScope no_gc;
+    FinalizablePersistentHandle* handle =
+        reinterpret_cast<FinalizablePersistentHandle*>(addr);
+    RawObject* raw_obj = handle->raw();
+    if (raw_obj->IsHeapObject()) {
+      ASSERT(handle->IsPrologueWeakPersistent());
+      ReusableInstanceHandleScope reused_instance_handle(isolate());
+      Instance& instance = reused_instance_handle.Handle();
+      instance ^= reinterpret_cast<RawInstance*>(handle->raw());
+      intptr_t num_native_fields = instance.NumNativeFields();
+      intptr_t* native_fields = instance.NativeFieldsDataAddr();
+      if (native_fields != NULL) {
+        callback_(isolate()->init_callback_data(),
+                  reinterpret_cast<Dart_WeakPersistentHandle>(addr),
+                  num_native_fields,
+                  native_fields);
+      }
+    }
+  }
+
+ private:
+  Dart_GcPrologueWeakHandleCallback callback_;
+
+  DISALLOW_COPY_AND_ASSIGN(PrologueWeakVisitor);
+};
+
+
+DART_EXPORT Dart_Handle Dart_VisitPrologueWeakHandles(
+    Dart_GcPrologueWeakHandleCallback callback) {
+  Isolate* isolate = Isolate::Current();
+  CHECK_ISOLATE(isolate);
+  PrologueWeakVisitor visitor(isolate, callback);
+  isolate->VisitPrologueWeakPersistentHandles(&visitor);
+  return Api::Success();
+}
+
+
 // --- Initialization and Globals ---
 
 DART_EXPORT const char* Dart_VersionString() {
@@ -3130,6 +3213,7 @@
   if (result.IsError()) {
     return Api::NewHandle(isolate, result.raw());
   }
+
   if (constructor.IsConstructor()) {
     ASSERT(result.IsNull());
   } else {
@@ -3872,8 +3956,7 @@
                                                     intptr_t value) {
   Isolate* isolate = Isolate::Current();
   DARTSCOPE(isolate);
-  ReusableObjectHandleScope reused_obj_handle(isolate);
-  const Instance& instance = Api::UnwrapInstanceHandle(reused_obj_handle, obj);
+  const Instance& instance = Api::UnwrapInstanceHandle(isolate, obj);
   if (instance.IsNull()) {
     RETURN_TYPE_ERROR(isolate, obj, Instance);
   }
@@ -4586,7 +4669,8 @@
 
 DART_EXPORT Dart_Handle Dart_SetNativeResolver(
     Dart_Handle library,
-    Dart_NativeEntryResolver resolver) {
+    Dart_NativeEntryResolver resolver,
+    Dart_NativeEntrySymbol symbol) {
   Isolate* isolate = Isolate::Current();
   DARTSCOPE(isolate);
   const Library& lib = Api::UnwrapLibraryHandle(isolate, library);
@@ -4594,6 +4678,7 @@
     RETURN_TYPE_ERROR(isolate, library, Library);
   }
   lib.set_native_entry_resolver(resolver);
+  lib.set_native_entry_symbol_resolver(symbol);
   return Api::Success();
 }
 
diff --git a/runtime/vm/dart_api_impl_test.cc b/runtime/vm/dart_api_impl_test.cc
index f826f41..fa123f1 100644
--- a/runtime/vm/dart_api_impl_test.cc
+++ b/runtime/vm/dart_api_impl_test.cc
@@ -1389,7 +1389,8 @@
   // Create a test library and Load up a test script in it.
   Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
 
-  Dart_Handle result = Dart_SetNativeResolver(lib, &ByteDataNativeResolver);
+  Dart_Handle result =
+      Dart_SetNativeResolver(lib, &ByteDataNativeResolver, NULL);
   EXPECT_VALID(result);
 
   // Invoke 'main' function.
@@ -1457,7 +1458,8 @@
   Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
 
   Dart_Handle result = Dart_SetNativeResolver(lib,
-                                              &ExternalByteDataNativeResolver);
+                                              &ExternalByteDataNativeResolver,
+                                              NULL);
   EXPECT_VALID(result);
 
   // Invoke 'main' function.
@@ -2309,7 +2311,7 @@
 }
 
 
-TEST_CASE(WeakPersistentHandleExternalAllocationSizeOversized) {
+TEST_CASE(WeakPersistentHandleExternalAllocationSizeNewspaceGC) {
   Dart_Isolate isolate = reinterpret_cast<Dart_Isolate>(Isolate::Current());
   Heap* heap = Isolate::Current()->heap();
   Dart_WeakPersistentHandle weak1 = NULL;
@@ -2350,6 +2352,43 @@
 }
 
 
+TEST_CASE(WeakPersistentHandleExternalAllocationSizeOldspaceGC) {
+  // Check that external allocation in old space can trigger GC.
+  Isolate* isolate = Isolate::Current();
+  Dart_EnterScope();
+  Dart_Handle live = Api::NewHandle(isolate, String::New("live", Heap::kOld));
+  EXPECT_VALID(live);
+  Dart_WeakPersistentHandle weak = NULL;
+  EXPECT_EQ(0, isolate->heap()->ExternalInWords(Heap::kOld));
+  const intptr_t kSmallExternalSize = 1 * KB;
+  {
+    Dart_EnterScope();
+    Dart_Handle dead = Api::NewHandle(isolate, String::New("dead", Heap::kOld));
+    EXPECT_VALID(dead);
+    weak = Dart_NewWeakPersistentHandle(dead,
+                                        NULL,
+                                        kSmallExternalSize,
+                                        NopCallback);
+    EXPECT_VALID(AsHandle(weak));
+    Dart_ExitScope();
+  }
+  EXPECT_EQ(kSmallExternalSize,
+            isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+  // Large enough to trigger GC in old space. Not actually allocated.
+  const intptr_t kHugeExternalSize = Heap::kHeapSizeInMB * MB;
+  Dart_NewWeakPersistentHandle(live,
+                               NULL,
+                               kHugeExternalSize,
+                               NopCallback);
+  // Expect small garbage to be collected.
+  EXPECT_EQ(kHugeExternalSize,
+            isolate->heap()->ExternalInWords(Heap::kOld) * kWordSize);
+  Dart_DeleteWeakPersistentHandle(reinterpret_cast<Dart_Isolate>(isolate),
+                                  weak);
+  Dart_ExitScope();
+}
+
+
 TEST_CASE(WeakPersistentHandleExternalAllocationSizeOddReferents) {
   Heap* heap = Isolate::Current()->heap();
   Dart_WeakPersistentHandle weak1 = NULL;
@@ -2358,13 +2397,13 @@
   static const intptr_t kWeak2ExternalSize = 2 * KB;
   {
     Dart_EnterScope();
-    Dart_Handle dart_null = Dart_Null();  // VM heap object.
-    EXPECT_VALID(dart_null);
+    Dart_Handle dart_true = Dart_True();  // VM heap object.
+    EXPECT_VALID(dart_true);
     weak1 = Dart_NewWeakPersistentHandle(
-        dart_null, NULL, kWeak1ExternalSize, NopCallback);
+        dart_true, NULL, kWeak1ExternalSize, NopCallback);
     EXPECT_VALID(AsHandle(weak1));
     Dart_Handle zero = Dart_NewInteger(0);  // Smi.
-    EXPECT_VALID(dart_null);
+    EXPECT_VALID(zero);
     weak2 = Dart_NewWeakPersistentHandle(
         zero, NULL, kWeak2ExternalSize, NopCallback);
     EXPECT_VALID(AsHandle(weak2));
@@ -2377,7 +2416,7 @@
   Dart_DeleteWeakPersistentHandle(isolate, weak1);
   Dart_DeleteWeakPersistentHandle(isolate, weak2);
   Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
-  EXPECT(heap->ExternalInWords(Heap::kOld) == 0);
+  EXPECT_EQ(0, heap->ExternalInWords(Heap::kOld));
 }
 
 
@@ -2467,21 +2506,24 @@
   }
 
   {
-    Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
-                                          array1, ARRAY_SIZE(array1)));
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
 
-    Dart_WeakPersistentHandle array2[] = { weak2, weak1 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
-                                          array2, ARRAY_SIZE(array2)));
+    Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
 
-    Dart_WeakPersistentHandle array3[] = { weak3, weak2 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
-                                          array3, ARRAY_SIZE(array3)));
+    set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak1, weak1));
 
-    Dart_WeakPersistentHandle array4[] = { weak4, weak3 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
-                                          array4, ARRAY_SIZE(array4)));
+    set = Dart_NewWeakReferenceSet(builder, weak3, weak3);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak2, weak2));
+
+    set = Dart_NewWeakReferenceSet(builder, weak4, weak4);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak3));
 
     Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
   }
@@ -2499,26 +2541,28 @@
 
   {
     Dart_EnterScope();
-    Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
-                                          array1, ARRAY_SIZE(array1)));
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
 
-    Dart_WeakPersistentHandle array2[] = { weak2, weak1 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
-                                          array2, ARRAY_SIZE(array2)));
+    Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
 
-    Dart_WeakPersistentHandle array3[] = { weak2 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
-                                          array3, ARRAY_SIZE(array3)));
+    set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak1, weak1));
+
+    set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+    EXPECT_NOTNULL(set);
 
     // Strong reference to weak3 to retain weak3 and weak4.
     Dart_PersistentHandle weak3_strong_ref =
         Dart_NewPersistentHandle(AsHandle(weak3));
     EXPECT_VALID(AsHandle(weak3_strong_ref));
 
-    Dart_WeakPersistentHandle array4[] = { weak4, weak3 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
-                                          array4, ARRAY_SIZE(array4)));
+    set = Dart_NewWeakReferenceSet(builder, weak4, weak4);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak3));
 
     Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
 
@@ -2539,21 +2583,23 @@
   }
 
   {
-    Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
-                                          array1, ARRAY_SIZE(array1)));
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
 
-    Dart_WeakPersistentHandle array2[] = { weak2, weak1 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
-                                          array2, ARRAY_SIZE(array2)));
+    Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
 
-    Dart_WeakPersistentHandle array3[] = { weak2 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array3, ARRAY_SIZE(array3),
-                                          array3, ARRAY_SIZE(array3)));
+    set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak1, weak1));
 
-    Dart_WeakPersistentHandle array4[] = { weak4, weak3 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array4, ARRAY_SIZE(array4),
-                                          array4, ARRAY_SIZE(array4)));
+    set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+    EXPECT_NOTNULL(set);
+
+    set = Dart_NewWeakReferenceSet(builder, weak4, weak4);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak3));
 
     Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
   }
@@ -2570,16 +2616,20 @@
   }
 
   {
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
+
     Dart_WeakPersistentHandle lweak3 = Dart_NewWeakPersistentHandle(
         Dart_Null(), NULL, 0, NopCallback);
-    Dart_WeakPersistentHandle array1[] = { weak1, strong_weak };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
-                                          array1, ARRAY_SIZE(array1)));
+
+    Dart_WeakReferenceSet set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, strong_weak, strong_weak));
 
     // lweak3 is unreferenced so weak2 is unreferenced and should be cleared
-    Dart_WeakPersistentHandle array2[] = { weak2, lweak3 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
-                                          array2, ARRAY_SIZE(array2)));
+    set = Dart_NewWeakReferenceSet(builder, weak2, weak2);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak3, lweak3));
 
     Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
   }
@@ -2596,6 +2646,9 @@
   }
 
   {
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
+
     Dart_WeakPersistentHandle lweak2 = Dart_NewWeakPersistentHandle(
         Dart_Null(), NULL, 0, NopCallback);
     Dart_WeakPersistentHandle lweak3 = Dart_NewWeakPersistentHandle(
@@ -2603,17 +2656,16 @@
     Dart_WeakPersistentHandle lweak4 = Dart_NewWeakPersistentHandle(
         Dart_Null(), NULL, 0, NopCallback);
     // lweak{2,3,4} are cleared and should have no effect on weak1
-    Dart_WeakPersistentHandle array1[] = { strong_weak,
-                                           lweak2,
-                                           lweak3,
-                                           lweak4 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array1, ARRAY_SIZE(array1),
-                                          array1, ARRAY_SIZE(array1)));
+    Dart_WeakReferenceSet set =
+        Dart_NewWeakReferenceSet(builder, strong_weak, strong_weak);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak2, lweak2));
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak3, lweak3));
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, lweak4, lweak4));
 
     // weak1 is weakly reachable and should be cleared
-    Dart_WeakPersistentHandle array2[] = { weak1 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(array2, ARRAY_SIZE(array2),
-                                          array2, ARRAY_SIZE(array2)));
+    set = Dart_NewWeakReferenceSet(builder, weak1, weak1);
+    EXPECT_NOTNULL(set);
 
     Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
   }
@@ -2690,7 +2742,8 @@
 
   // Garbage collect old space without invoking API callbacks.
   Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
-                                             Heap::kIgnoreApiCallbacks);
+                                             Heap::kIgnoreApiCallbacks,
+                                             Heap::kGCTestCase);
 
   {
     Dart_EnterScope();
@@ -2713,7 +2766,8 @@
   }
 
   Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
-                                             Heap::kInvokeApiCallbacks);
+                                             Heap::kInvokeApiCallbacks,
+                                             Heap::kGCTestCase);
 
   {
     Dart_EnterScope();
@@ -2802,10 +2856,15 @@
 
   // A strongly referenced key should preserve all the values.
   {
-    Dart_WeakPersistentHandle keys[] = { strong_weak };
-    Dart_WeakPersistentHandle values[] = { weak1, weak2, weak3 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
-                                          values, ARRAY_SIZE(values)));
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
+
+    Dart_WeakReferenceSet set =
+        Dart_NewWeakReferenceSet(builder, strong_weak, 0);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak1));
+    EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak2));
+    EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak3));
 
     Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
   }
@@ -2822,10 +2881,13 @@
 
   // Key membership does not imply a strong reference.
   {
-    Dart_WeakPersistentHandle keys[] = { strong_weak, weak3 };
-    Dart_WeakPersistentHandle values[] = { weak1, weak2 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
-                                          values, ARRAY_SIZE(values)));
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
+
+    Dart_WeakReferenceSet set =
+        Dart_NewWeakReferenceSet(builder, strong_weak, weak1);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendToWeakReferenceSet(set, weak3, weak2));
 
     Isolate::Current()->heap()->CollectGarbage(Heap::kOld);
   }
@@ -2904,10 +2966,15 @@
 
   // A strongly referenced key should preserve all the values.
   {
-    Dart_WeakPersistentHandle keys[] = { strong_weak };
-    Dart_WeakPersistentHandle values[] = { weak1, weak2, weak3 };
-    EXPECT_VALID(Dart_NewWeakReferenceSet(keys, ARRAY_SIZE(keys),
-                                          values, ARRAY_SIZE(values)));
+    Dart_WeakReferenceSetBuilder builder = Dart_NewWeakReferenceSetBuilder();
+    EXPECT_NOTNULL(builder);
+
+    Dart_WeakReferenceSet set =
+        Dart_NewWeakReferenceSet(builder, strong_weak, 0);
+    EXPECT_NOTNULL(set);
+    EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak1));
+    EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak2));
+    EXPECT_VALID(Dart_AppendValueToWeakReferenceSet(set, weak3));
 
     GCTestHelper::CollectNewSpace(Heap::kInvokeApiCallbacks);
   }
@@ -3020,7 +3087,8 @@
   global_prologue_callback_status = 3;
   global_epilogue_callback_status = 7;
   Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
-                                             Heap::kIgnoreApiCallbacks);
+                                             Heap::kIgnoreApiCallbacks,
+                                             Heap::kGCTestCase);
   EXPECT_EQ(3, global_prologue_callback_status);
   EXPECT_EQ(7, global_epilogue_callback_status);
 
@@ -3070,7 +3138,8 @@
   // Garbage collect old space again without invoking callbacks.
   // Nothing should change.
   Isolate::Current()->heap()->CollectGarbage(Heap::kOld,
-                                             Heap::kIgnoreApiCallbacks);
+                                             Heap::kIgnoreApiCallbacks,
+                                             Heap::kGCTestCase);
   EXPECT_EQ(6, global_prologue_callback_status);
   EXPECT_EQ(28, global_epilogue_callback_status);
 
@@ -6117,7 +6186,7 @@
       EXPECT(false);
     }
   }
-  result = Dart_SetNativeResolver(result, &PatchNativeResolver);
+  result = Dart_SetNativeResolver(result, &PatchNativeResolver, NULL);
   EXPECT_VALID(result);
 
   Dart_Handle script_url = NewString("theScript");
@@ -6230,23 +6299,23 @@
   Dart_Handle type = Dart_GetType(lib, NewString("Test"), 0, NULL);
   EXPECT_VALID(type);
 
-  result = Dart_SetNativeResolver(Dart_Null(), &MyNativeResolver1);
+  result = Dart_SetNativeResolver(Dart_Null(), &MyNativeResolver1, NULL);
   EXPECT(Dart_IsError(result));
   EXPECT_STREQ(
       "Dart_SetNativeResolver expects argument 'library' to be non-null.",
       Dart_GetError(result));
 
-  result = Dart_SetNativeResolver(Dart_True(), &MyNativeResolver1);
+  result = Dart_SetNativeResolver(Dart_True(), &MyNativeResolver1, NULL);
   EXPECT(Dart_IsError(result));
   EXPECT_STREQ("Dart_SetNativeResolver expects argument 'library' to be of "
                "type Library.",
                Dart_GetError(result));
 
-  result = Dart_SetNativeResolver(error, &MyNativeResolver1);
+  result = Dart_SetNativeResolver(error, &MyNativeResolver1, NULL);
   EXPECT(Dart_IsError(result));
   EXPECT_STREQ("incoming error", Dart_GetError(result));
 
-  result = Dart_SetNativeResolver(lib, &MyNativeResolver1);
+  result = Dart_SetNativeResolver(lib, &MyNativeResolver1, NULL);
   EXPECT_VALID(result);
 
   // Call a function and make sure native resolution works.
@@ -6258,7 +6327,7 @@
   EXPECT_EQ(654321, value);
 
   // A second call succeeds.
-  result = Dart_SetNativeResolver(lib, &MyNativeResolver2);
+  result = Dart_SetNativeResolver(lib, &MyNativeResolver2, NULL);
   EXPECT_VALID(result);
 
   // 'foo' has already been resolved so gets the old value.
@@ -6278,7 +6347,7 @@
   EXPECT_EQ(123456, value);
 
   // A NULL resolver is okay, but resolution will fail.
-  result = Dart_SetNativeResolver(lib, NULL);
+  result = Dart_SetNativeResolver(lib, NULL, NULL);
   EXPECT_VALID(result);
 
   EXPECT_ERROR(Dart_Invoke(type, NewString("baz"), 0, NULL),
@@ -6701,7 +6770,8 @@
     EXPECT_VALID(result);
     lib = Dart_LoadScript(url, source, 0, 0);
     EXPECT_VALID(lib);
-    result = Dart_SetNativeResolver(lib, &IsolateInterruptTestNativeLookup);
+    result =
+        Dart_SetNativeResolver(lib, &IsolateInterruptTestNativeLookup, NULL);
     DART_CHECK_VALID(result);
 
     sync->Notify();
@@ -7063,7 +7133,7 @@
   Dart_Handle lib = Dart_LoadScript(url, source, 0, 0);
   EXPECT_VALID(lib);
   EXPECT(Dart_IsLibrary(lib));
-  result = Dart_SetNativeResolver(lib, &MyNativeClosureResolver);
+  result = Dart_SetNativeResolver(lib, &MyNativeClosureResolver, NULL);
   EXPECT_VALID(result);
 
   result = Dart_Invoke(lib, NewString("testMain"), 0, NULL);
@@ -7210,7 +7280,7 @@
   Dart_Handle lib = Dart_LoadScript(url, source, 0, 0);
   EXPECT_VALID(lib);
   EXPECT(Dart_IsLibrary(lib));
-  result = Dart_SetNativeResolver(lib, &MyStaticNativeClosureResolver);
+  result = Dart_SetNativeResolver(lib, &MyStaticNativeClosureResolver, NULL);
   EXPECT_VALID(result);
 
   result = Dart_Invoke(lib, NewString("testMain"), 0, NULL);
diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h
index ad9a2df..5226aef 100644
--- a/runtime/vm/dart_api_state.h
+++ b/runtime/vm/dart_api_state.h
@@ -215,8 +215,6 @@
     if (SpaceForExternal() == Heap::kNew) {
       SetExternalNewSpaceBit();
     }
-    // TODO(koda): On repeated/large external allocations for existing objects,
-    // without any intervening normal allocation, GC will not trigger.
     isolate->heap()->AllocateExternal(external_size(), SpaceForExternal());
   }
 
@@ -229,8 +227,7 @@
   // Called when the referent has moved, potentially between generations.
   void UpdateRelocated(Isolate* isolate) {
     if (IsSetNewSpaceBit() && (SpaceForExternal() == Heap::kOld)) {
-      isolate->heap()->FreeExternal(external_size(), Heap::kNew);
-      isolate->heap()->AllocateExternal(external_size(), Heap::kOld);
+      isolate->heap()->PromoteExternal(external_size());
       ClearExternalNewSpaceBit();
     }
   }
@@ -561,63 +558,6 @@
 };
 
 
-class WeakReferenceSet {
- public:
-  WeakReferenceSet(Dart_WeakPersistentHandle* keys, intptr_t keys_length,
-                   Dart_WeakPersistentHandle* values, intptr_t values_length)
-      : next_(NULL),
-        keys_(keys), num_keys_(keys_length),
-        values_(values), num_values_(values_length) {
-  }
-  ~WeakReferenceSet() {}
-
-  WeakReferenceSet* next() const { return next_; }
-
-  intptr_t num_keys() const { return num_keys_; }
-  RawObject** get_key(intptr_t i) {
-    ASSERT(i >= 0);
-    ASSERT(i < num_keys_);
-    FinalizablePersistentHandle* ref =
-        FinalizablePersistentHandle::Cast(keys_[i]);
-    return ref->raw_addr();
-  }
-
-  intptr_t num_values() const { return num_values_; }
-  RawObject** get_value(intptr_t i) {
-    ASSERT(i >= 0);
-    ASSERT(i < num_values_);
-    FinalizablePersistentHandle* ref =
-        FinalizablePersistentHandle::Cast(values_[i]);
-    return ref->raw_addr();
-  }
-
-  static WeakReferenceSet* Pop(WeakReferenceSet** queue) {
-    ASSERT(queue != NULL);
-    WeakReferenceSet* head = *queue;
-    if (head != NULL) {
-      *queue = head->next();
-      head->next_ = NULL;
-    }
-    return head;
-  }
-
-  static void Push(WeakReferenceSet* reference_set, WeakReferenceSet** queue) {
-    ASSERT(reference_set != NULL);
-    ASSERT(queue != NULL);
-    reference_set->next_ = *queue;
-    *queue = reference_set;
-  }
-
- private:
-  WeakReferenceSet* next_;
-  Dart_WeakPersistentHandle* keys_;
-  intptr_t num_keys_;
-  Dart_WeakPersistentHandle* values_;
-  intptr_t num_values_;
-  DISALLOW_COPY_AND_ASSIGN(WeakReferenceSet);
-};
-
-
 // Structure used for the implementation of local scopes used in dart_api.
 // These local scopes manage handles and memory allocated in the scope.
 class ApiLocalScope {
@@ -660,6 +600,55 @@
 };
 
 
+class ApiNativeScope {
+ public:
+  ApiNativeScope() {
+    // Currently no support for nesting native scopes.
+    ASSERT(Current() == NULL);
+    Thread::SetThreadLocal(Api::api_native_key_, reinterpret_cast<uword>(this));
+  }
+
+  ~ApiNativeScope() {
+    ASSERT(Current() == this);
+    Thread::SetThreadLocal(Api::api_native_key_, 0);
+  }
+
+  static inline ApiNativeScope* Current() {
+    return reinterpret_cast<ApiNativeScope*>(
+        Thread::GetThreadLocal(Api::api_native_key_));
+  }
+
+  Zone* zone() { return zone_.GetZone(); }
+
+ private:
+  ApiZone zone_;
+};
+
+
+// Api growable arrays use a zone for allocation. The constructor
+// picks the zone from the current isolate if in an isolate
+// environment. When outside an isolate environment it picks the zone
+// from the current native scope.
+template<typename T>
+class ApiGrowableArray : public BaseGrowableArray<T, ValueObject> {
+ public:
+  explicit ApiGrowableArray(int initial_capacity)
+      : BaseGrowableArray<T, ValueObject>(
+          initial_capacity,
+          ApiNativeScope::Current()->zone()) {}
+  ApiGrowableArray()
+      : BaseGrowableArray<T, ValueObject>(
+          ApiNativeScope::Current()->zone()) {}
+  ApiGrowableArray(intptr_t initial_capacity, Zone* zone)
+      : BaseGrowableArray<T, ValueObject>(initial_capacity, zone) {}
+};
+
+
+// Forward declarations.
+class WeakReferenceSetBuilder;
+class WeakReferenceSet;
+
+
 // Implementation of the API State used in dart api for maintaining
 // local scopes, persistent handles etc. These are setup on a per isolate
 // basis and destroyed when the isolate is shutdown.
@@ -758,6 +747,10 @@
     }
   }
 
+  void VisitPrologueWeakHandles(HandleVisitor* visitor) {
+    prologue_weak_persistent_handles().VisitHandles(visitor);
+  }
+
   bool IsValidLocalHandle(Dart_Handle object) const {
     ApiLocalScope* scope = top_scope_;
     while (scope != NULL) {
@@ -822,9 +815,9 @@
     return acquired_error_;
   }
 
-  void DelayWeakReferenceSet(WeakReferenceSet* reference_set) {
-    WeakReferenceSet::Push(reference_set, &delayed_weak_reference_sets_);
-  }
+  WeakReferenceSetBuilder* NewWeakReferenceSetBuilder();
+
+  void DelayWeakReferenceSet(WeakReferenceSet* reference_set);
 
  private:
   PersistentHandles persistent_handles_;
@@ -844,45 +837,107 @@
 };
 
 
-class ApiNativeScope {
+class WeakReferenceSet {
  public:
-  ApiNativeScope() {
-    // Currently no support for nesting native scopes.
-    ASSERT(Current() == NULL);
-    Thread::SetThreadLocal(Api::api_native_key_, reinterpret_cast<uword>(this));
+  explicit WeakReferenceSet(Zone* zone)
+      : next_(NULL),
+        keys_(1, zone),
+        values_(1, zone) {
+  }
+  ~WeakReferenceSet() {}
+
+  WeakReferenceSet* next() const { return next_; }
+
+  intptr_t num_keys() const { return keys_.length(); }
+  RawObject** get_key(intptr_t i) {
+    ASSERT(i >= 0);
+    ASSERT(i < num_keys());
+    FinalizablePersistentHandle* ref =
+        FinalizablePersistentHandle::Cast(keys_[i]);
+    return ref->raw_addr();
   }
 
-  ~ApiNativeScope() {
-    ASSERT(Current() == this);
-    Thread::SetThreadLocal(Api::api_native_key_, 0);
+  intptr_t num_values() const { return values_.length(); }
+  RawObject** get_value(intptr_t i) {
+    ASSERT(i >= 0);
+    ASSERT(i < num_values());
+    FinalizablePersistentHandle* ref =
+        FinalizablePersistentHandle::Cast(values_[i]);
+    return ref->raw_addr();
   }
 
-  static inline ApiNativeScope* Current() {
-    return reinterpret_cast<ApiNativeScope*>(
-        Thread::GetThreadLocal(Api::api_native_key_));
+  bool SingletonKeyEqualsValue() const {
+    ASSERT((num_keys() == 1) && (num_values() == 1));
+    return (keys_[0] == values_[0]);
   }
 
-  Zone* zone() { return zone_.GetZone(); }
+  void Append(Dart_WeakPersistentHandle key, Dart_WeakPersistentHandle value) {
+    keys_.Add(key);
+    values_.Add(value);
+  }
+
+  void AppendKey(Dart_WeakPersistentHandle key) {
+    keys_.Add(key);
+  }
+
+  void AppendValue(Dart_WeakPersistentHandle value) {
+    values_.Add(value);
+  }
+
+  static WeakReferenceSet* Pop(WeakReferenceSet** queue) {
+    ASSERT(queue != NULL);
+    WeakReferenceSet* head = *queue;
+    if (head != NULL) {
+      *queue = head->next();
+      head->next_ = NULL;
+    }
+    return head;
+  }
+
+  static void Push(WeakReferenceSet* reference_set, WeakReferenceSet** queue) {
+    ASSERT(reference_set != NULL);
+    ASSERT(queue != NULL);
+    reference_set->next_ = *queue;
+    *queue = reference_set;
+  }
+
+  void* operator new(uword size, Zone* zone) {
+    return reinterpret_cast<void*>(zone->AllocUnsafe(size));
+  }
+
+  // Disallow explicit deallocation of WeakReferenceSet.
+  void operator delete(void* pointer) { UNREACHABLE(); }
 
  private:
-  ApiZone zone_;
+  WeakReferenceSet* next_;
+  ApiGrowableArray<Dart_WeakPersistentHandle> keys_;
+  ApiGrowableArray<Dart_WeakPersistentHandle> values_;
+
+  DISALLOW_COPY_AND_ASSIGN(WeakReferenceSet);
 };
 
 
-// Api growable arrays use a zone for allocation. The constructor
-// picks the zone from the current isolate if in an isolate
-// environment. When outside an isolate environment it picks the zone
-// from the current native scope.
-template<typename T>
-class ApiGrowableArray : public BaseGrowableArray<T, ValueObject> {
+class WeakReferenceSetBuilder {
  public:
-  explicit ApiGrowableArray(int initial_capacity)
-      : BaseGrowableArray<T, ValueObject>(
-          initial_capacity,
-          ApiNativeScope::Current()->zone()) {}
-  ApiGrowableArray()
-      : BaseGrowableArray<T, ValueObject>(
-          ApiNativeScope::Current()->zone()) {}
+  ApiState* api_state() const {
+    return api_state_;
+  }
+
+  WeakReferenceSet* NewWeakReferenceSet() {
+    return new (zone_) WeakReferenceSet(zone_);
+  }
+
+ private:
+  explicit WeakReferenceSetBuilder(ApiState* api_state)
+      : api_state_(api_state),
+        zone_(api_state->top_scope()->zone()) {
+  }
+
+  ApiState* api_state_;
+  Zone* zone_;
+
+  friend class ApiState;
+  DISALLOW_IMPLICIT_CONSTRUCTORS(WeakReferenceSetBuilder);
 };
 
 
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 3d487b2..d87143b 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -28,6 +28,8 @@
 namespace dart {
 
 DEFINE_FLAG(bool, verbose_debug, false, "Verbose debugger messages");
+DEFINE_FLAG(bool, trace_debugger_stacktrace, false,
+            "Trace debugger stacktrace collection");
 
 
 Debugger::EventHandler* Debugger::event_handler_ = NULL;
@@ -51,10 +53,12 @@
 
 SourceBreakpoint::SourceBreakpoint(intptr_t id,
                                    const Script& script,
-                                   intptr_t token_pos)
+                                   intptr_t token_pos,
+                                   intptr_t end_token_pos)
     : id_(id),
       script_(script.raw()),
       token_pos_(token_pos),
+      end_token_pos_(end_token_pos),
       is_resolved_(false),
       is_enabled_(false),
       next_(NULL),
@@ -84,6 +88,7 @@
          (token_pos <= func.end_token_pos()));
   function_ = func.raw();
   token_pos_ = token_pos;
+  end_token_pos_ = token_pos;
   line_number_ = -1;  // Recalcualte lazily.
   is_resolved_ = true;
 }
@@ -446,6 +451,10 @@
     RawLocalVarDescriptors::VarInfo var_info;
     var_descriptors_.GetInfo(i, &var_info);
     if (var_info.kind == RawLocalVarDescriptors::kSavedEntryContext) {
+      if (FLAG_trace_debugger_stacktrace) {
+        OS::PrintErr("\tFound saved entry ctx at index %" Pd "\n",
+                     var_info.index);
+      }
       return GetLocalContextVar(var_info.index);
     }
   }
@@ -464,6 +473,10 @@
     RawLocalVarDescriptors::VarInfo var_info;
     var_descriptors_.GetInfo(i, &var_info);
     if (var_info.kind == RawLocalVarDescriptors::kSavedCurrentContext) {
+      if (FLAG_trace_debugger_stacktrace) {
+        OS::PrintErr("\tFound saved current ctx at index %" Pd "\n",
+                     var_info.index);
+      }
       return GetLocalContextVar(var_info.index);
     }
   }
@@ -625,6 +638,52 @@
 }
 
 
+void ActivationFrame::PrintContextMismatchError(
+    const String& var_name,
+    intptr_t ctx_slot,
+    intptr_t frame_ctx_level,
+    intptr_t var_ctx_level) {
+  OS::PrintErr("-------------------------\n"
+               "Encountered context mismatch\n"
+               "\tvar name: %s\n"
+               "\tctx_slot: %" Pd "\n"
+               "\tframe_ctx_level: %" Pd "\n"
+               "\tvar_ctx_level: %" Pd "\n\n",
+               var_name.ToCString(),
+               ctx_slot,
+               frame_ctx_level,
+               var_ctx_level);
+
+  OS::PrintErr("-------------------------\n"
+               "Current frame:\n%s\n",
+               this->ToCString());
+
+  OS::PrintErr("-------------------------\n"
+               "Context contents:\n");
+  ctx_.Dump(8);
+
+  OS::PrintErr("-------------------------\n"
+               "Debugger stack trace...\n\n");
+  DebuggerStackTrace* stack =
+      Isolate::Current()->debugger()->StackTrace();
+  intptr_t num_frames = stack->Length();
+  for (intptr_t i = 0; i < num_frames; i++) {
+    ActivationFrame* frame = stack->FrameAt(i);
+    OS::PrintErr("#%04" Pd " %s", i, frame->ToCString());
+  }
+
+  OS::PrintErr("-------------------------\n"
+               "All frames...\n\n");
+  StackFrameIterator iterator(false);
+  StackFrame* frame = iterator.NextFrame();
+  intptr_t num = 0;
+  while ((frame != NULL)) {
+    frame = iterator.NextFrame();
+    OS::PrintErr("#%04" Pd " %s\n", num++, frame->ToCString());
+  }
+}
+
+
 void ActivationFrame::VariableAt(intptr_t i,
                                  String* name,
                                  intptr_t* token_pos,
@@ -648,6 +707,8 @@
     ASSERT(var_info.kind == RawLocalVarDescriptors::kContextVar);
     if (ctx_.IsNull()) {
       // The context has been removed by the optimizing compiler.
+      //
+      // TODO(turnidge): This may be erroneous.  Revisit.
       *value = Symbols::OptimizedOut().raw();
       return;
     }
@@ -660,6 +721,11 @@
     intptr_t level_diff = frame_ctx_level - var_ctx_level;
     intptr_t ctx_slot = var_info.index;
     if (level_diff == 0) {
+      if ((ctx_slot < 0) ||
+          (ctx_slot >= ctx_.num_variables())) {
+        PrintContextMismatchError(*name, ctx_slot,
+                                  frame_ctx_level, var_ctx_level);
+      }
       ASSERT((ctx_slot >= 0) && (ctx_slot < ctx_.num_variables()));
       *value = ctx_.At(ctx_slot);
     } else {
@@ -669,6 +735,12 @@
         level_diff--;
         var_ctx = var_ctx.parent();
       }
+      if (var_ctx.IsNull() ||
+          (ctx_slot < 0) ||
+          (ctx_slot >= var_ctx.num_variables())) {
+        PrintContextMismatchError(*name, ctx_slot,
+                                  frame_ctx_level, var_ctx_level);
+      }
       ASSERT(!var_ctx.IsNull());
       ASSERT((ctx_slot >= 0) && (ctx_slot < var_ctx.num_variables()));
       *value = var_ctx.At(ctx_slot);
@@ -694,18 +766,22 @@
 
 
 const char* ActivationFrame::ToCString() {
-  const char* kFormat = "Function: '%s' url: '%s' line: %d";
-
   const String& url = String::Handle(SourceUrl());
   intptr_t line = LineNumber();
   const char* func_name = Debugger::QualifiedFunctionName(function());
-
-  intptr_t len =
-      OS::SNPrint(NULL, 0, kFormat, func_name, url.ToCString(), line);
-  len++;  // String terminator.
-  char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
-  OS::SNPrint(chars, len, kFormat, func_name, url.ToCString(), line);
-  return chars;
+  return Isolate::Current()->current_zone()->
+      PrintToString("[ Frame pc(0x%" Px ") fp(0x%" Px ") sp(0x%" Px ")\n"
+                    "\tfunction = %s\n"
+                    "\turl = %s\n"
+                    "\tline = %" Pd "\n"
+                    "\tcontext = %s\n"
+                    "\tcontext level = %" Pd " ]\n",
+                    pc(), fp(), sp(),
+                    func_name,
+                    url.ToCString(),
+                    line,
+                    ctx_.ToCString(),
+                    ContextLevel());
 }
 
 
@@ -1065,21 +1141,42 @@
       new ActivationFrame(pc, frame->fp(), frame->sp(), code,
                           deopt_frame, deopt_frame_offset);
 
-  // Recover the context for this frame.
-  if (callee_activation == NULL) {
-    // No callee.  Use incoming entry context.  Could be from
-    // isolate's top context or from an entry frame.
-    ASSERT(!entry_ctx.IsNull());
-    activation->SetContext(entry_ctx);
+  // Is there a closure call at the current PC?
+  //
+  // We can't just check the callee_activation to see if it is a
+  // closure function, because it may not be on the stack yet.
+  bool is_closure_call = false;
+  const PcDescriptors& pc_desc =
+      PcDescriptors::Handle(code.pc_descriptors());
+  ASSERT(code.ContainsInstructionAt(pc));
+  for (int i = 0; i < pc_desc.Length(); i++) {
+    if (pc_desc.PC(i) == pc &&
+        pc_desc.DescriptorKind(i) == PcDescriptors::kClosureCall) {
+      is_closure_call = true;
+      break;
+    }
+  }
 
-  } else if (callee_activation->function().IsClosureFunction()) {
+  // Recover the context for this frame.
+  if (is_closure_call) {
     // If the callee is a closure, we should have stored the context
     // in the current frame before making the call.
     const Context& closure_call_ctx =
         Context::Handle(isolate, activation->GetSavedCurrentContext());
     ASSERT(!closure_call_ctx.IsNull());
     activation->SetContext(closure_call_ctx);
-
+    if (FLAG_trace_debugger_stacktrace) {
+      OS::PrintErr("\tUsing closure call ctx: %s\n",
+                   closure_call_ctx.ToCString());
+    }
+  } else if (callee_activation == NULL) {
+    // No callee available.  Use incoming entry context.  Could be from
+    // isolate's top context or from an entry frame.
+    ASSERT(!entry_ctx.IsNull());
+    activation->SetContext(entry_ctx);
+    if (FLAG_trace_debugger_stacktrace) {
+      OS::PrintErr("\tUsing entry ctx: %s\n", entry_ctx.ToCString());
+    }
   } else {
     // Use the context provided by our callee.  This is either the
     // callee's context or a context that was saved in the callee's
@@ -1090,6 +1187,13 @@
     const Context& callee_ctx =
         Context::Handle(isolate, callee_activation->GetSavedEntryContext());
     activation->SetContext(callee_ctx);
+    if (FLAG_trace_debugger_stacktrace) {
+      OS::PrintErr("\tUsing callee call ctx: %s\n",
+                   callee_ctx.ToCString());
+    }
+  }
+  if (FLAG_trace_debugger_stacktrace) {
+    OS::PrintErr("\tLine number: %" Pd "\n", activation->LineNumber());
   }
   return activation;
 }
@@ -1132,9 +1236,17 @@
        frame != NULL;
        frame = iterator.NextFrame()) {
     ASSERT(frame->IsValid());
+    if (FLAG_trace_debugger_stacktrace) {
+      OS::PrintErr("CollectStackTrace: visiting frame:\n\t%s\n",
+                   frame->ToCString());
+    }
     if (frame->IsEntryFrame()) {
       current_activation = NULL;
       entry_ctx = reinterpret_cast<EntryFrame*>(frame)->SavedContext();
+      if (FLAG_trace_debugger_stacktrace) {
+        OS::PrintErr("\tFound saved ctx in  entry frame:\n\t%s\n",
+                     entry_ctx.ToCString());
+      }
 
     } else if (frame->IsDartFrame()) {
       code = frame->LookupDartCode();
@@ -1144,6 +1256,13 @@
              !it.Done();
              it.Advance()) {
           inlined_code = it.code();
+          if (FLAG_trace_debugger_stacktrace) {
+            const Function& function =
+                Function::Handle(inlined_code.function());
+            ASSERT(!function.IsNull());
+            OS::PrintErr("CollectStackTrace: visiting inlined function: %s\n",
+                         function.ToFullyQualifiedCString());
+          }
           intptr_t deopt_frame_offset = it.GetDeoptFpOffset();
           current_activation = CollectDartFrame(isolate,
                                                 it.pc(),
@@ -1302,14 +1421,22 @@
 }
 
 
-// Given a function and a token position, return the best fit
+// Given a function and a token range, return the best fit
 // token position to set a breakpoint. The best fit is the safe point
-// with the lowest compiled code address that follows the requsted
-// token position.
+// with the lowest compiled code address within the token range.
 intptr_t Debugger::ResolveBreakpointPos(const Function& func,
-                                        intptr_t requested_token_pos) {
+                                        intptr_t requested_token_pos,
+                                        intptr_t last_token_pos) {
   ASSERT(func.HasCode());
   ASSERT(!func.HasOptimizedCode());
+
+  if (requested_token_pos < func.token_pos()) {
+    requested_token_pos = func.token_pos();
+  }
+  if (last_token_pos > func.end_token_pos()) {
+    last_token_pos = func.end_token_pos();
+  }
+
   Code& code = Code::Handle(func.unoptimized_code());
   ASSERT(!code.IsNull());
   PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
@@ -1317,20 +1444,14 @@
   intptr_t best_fit_pos = INT_MAX;
   uword lowest_pc = kUwordMax;
   intptr_t lowest_pc_index = -1;
+
   for (intptr_t i = 0; i < desc.Length(); i++) {
     intptr_t desc_token_pos = desc.TokenPos(i);
     ASSERT(desc_token_pos >= 0);
     if (IsSafePoint(desc, i)) {
-      if ((desc_token_pos < func.token_pos()) ||
-          (desc_token_pos > func.end_token_pos())) {
-        // The position is outside of the function token range. This can
-        // happen in constructors, for initializer expressions that are
-        // inlined in the field declaration.
-        ASSERT(func.IsConstructor());
-        continue;
-      }
-      if (desc_token_pos < requested_token_pos) {
-        // This descriptor is before the first acceptable token position.
+      if ((desc_token_pos < requested_token_pos) ||
+          (desc_token_pos > last_token_pos)) {
+        // This descriptor is outside the desired token range.
         continue;
       }
       if (desc_token_pos < best_fit_pos) {
@@ -1542,7 +1663,8 @@
 
 
 SourceBreakpoint* Debugger::SetBreakpoint(const Script& script,
-                                          intptr_t token_pos) {
+                                          intptr_t token_pos,
+                                          intptr_t last_token_pos) {
   Function& func = Function::Handle(isolate_);
   func = FindBestFit(script, token_pos);
   if (func.IsNull()) {
@@ -1565,14 +1687,15 @@
     // have already been compiled. We can resolve the breakpoint now.
     DeoptimizeWorld();
     func ^= functions.At(0);
-    intptr_t breakpoint_pos = ResolveBreakpointPos(func, token_pos);
+    intptr_t breakpoint_pos =
+        ResolveBreakpointPos(func, token_pos, last_token_pos);
     if (breakpoint_pos >= 0) {
       SourceBreakpoint* bpt = GetSourceBreakpoint(script, breakpoint_pos);
       if (bpt != NULL) {
         // A source breakpoint for this location already exists.
         return bpt;
       }
-      bpt = new SourceBreakpoint(nextId(), script, token_pos);
+      bpt = new SourceBreakpoint(nextId(), script, token_pos, last_token_pos);
       bpt->SetResolved(func, breakpoint_pos);
       RegisterSourceBreakpoint(bpt);
 
@@ -1584,6 +1707,14 @@
         MakeCodeBreakpointsAt(func, bpt);
       }
       bpt->Enable();
+      if (FLAG_verbose_debug) {
+        intptr_t line_number;
+        script.GetTokenLocation(breakpoint_pos, &line_number, NULL);
+        OS::Print("Resolved breakpoint for "
+                  "function '%s' at line %" Pd "\n",
+                  func.ToFullyQualifiedCString(),
+                  line_number);
+      }
       SignalBpResolved(bpt);
       return bpt;
     }
@@ -1600,7 +1731,7 @@
   }
   SourceBreakpoint* bpt = GetSourceBreakpoint(script, token_pos);
   if (bpt == NULL) {
-    bpt = new SourceBreakpoint(nextId(), script, token_pos);
+    bpt = new SourceBreakpoint(nextId(), script, token_pos, last_token_pos);
   }
   RegisterSourceBreakpoint(bpt);
   bpt->Enable();
@@ -1639,7 +1770,9 @@
       const Function& target_function) {
   ASSERT(!target_function.IsNull());
   const Script& script = Script::Handle(target_function.script());
-  return SetBreakpoint(script, target_function.token_pos());
+  return SetBreakpoint(script,
+                       target_function.token_pos(),
+                       target_function.end_token_pos());
 }
 
 
@@ -1684,7 +1817,7 @@
   SourceBreakpoint* bpt = NULL;
   ASSERT(first_token_idx <= last_token_idx);
   while ((bpt == NULL) && (first_token_idx <= last_token_idx)) {
-    bpt = SetBreakpoint(script, first_token_idx);
+    bpt = SetBreakpoint(script, first_token_idx, last_token_idx);
     first_token_idx++;
   }
   if ((bpt == NULL) && FLAG_verbose_debug) {
@@ -2150,7 +2283,8 @@
       // and set the code breakpoints.
       if (!bpt->IsResolved()) {
         // Resolve source breakpoint in the newly compiled function.
-        intptr_t bp_pos = ResolveBreakpointPos(func, bpt->token_pos());
+        intptr_t bp_pos =
+            ResolveBreakpointPos(func, bpt->token_pos(), bpt->end_token_pos());
         if (bp_pos < 0) {
           if (FLAG_verbose_debug) {
             OS::Print("Failed resolving breakpoint for function '%s'\n",
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 0664bac..65cd192 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -27,10 +27,14 @@
 // SourceBreakpoint.
 class SourceBreakpoint {
  public:
-  SourceBreakpoint(intptr_t id, const Script& script, intptr_t token_pos);
+  SourceBreakpoint(intptr_t id,
+                   const Script& script,
+                   intptr_t token_pos,
+                   intptr_t end_token_pos);
 
   RawFunction* function() const { return function_; }
   intptr_t token_pos() const { return token_pos_; }
+  intptr_t end_token_pos() const { return end_token_pos_; }
   intptr_t id() const { return id_; }
 
   RawScript* script() { return script_; }
@@ -56,6 +60,7 @@
   const intptr_t id_;
   RawScript* script_;
   intptr_t token_pos_;
+  intptr_t end_token_pos_;
   bool is_resolved_;
   bool is_enabled_;
   SourceBreakpoint* next_;
@@ -177,6 +182,11 @@
   void PrintToJSONObject(JSONObject* jsobj);
 
  private:
+  void PrintContextMismatchError(const String& var_name,
+                                 intptr_t ctx_slot,
+                                 intptr_t frame_ctx_level,
+                                 intptr_t var_ctx_level);
+
   intptr_t PcDescIndex();
   intptr_t TryIndex();
   void GetPcDescriptors();
@@ -392,12 +402,12 @@
   RawFunction* FindInnermostClosure(const Function& function,
                                     intptr_t token_pos);
   intptr_t ResolveBreakpointPos(const Function& func,
-                                intptr_t requested_token_pos);
+                                intptr_t requested_token_pos,
+                                intptr_t last_token_pos);
   void DeoptimizeWorld();
   void SetInternalBreakpoints(const Function& target_function);
-  SourceBreakpoint* SetBreakpoint(const Script& script, intptr_t token_pos);
-  SourceBreakpoint* SetBreakpoint(const Function& target_function,
-                                  intptr_t first_token_pos,
+  SourceBreakpoint* SetBreakpoint(const Script& script,
+                                  intptr_t token_pos,
                                   intptr_t last_token_pos);
   void RemoveInternalBreakpoints();
   void UnlinkCodeBreakpoints(SourceBreakpoint* src_bpt);
diff --git a/runtime/vm/debugger_api_impl.cc b/runtime/vm/debugger_api_impl.cc
index 5baae81..4eedf6e 100644
--- a/runtime/vm/debugger_api_impl.cc
+++ b/runtime/vm/debugger_api_impl.cc
@@ -308,6 +308,24 @@
 }
 
 
+DART_EXPORT Dart_Handle Dart_GetFunctionOrigin(Dart_Handle function_in) {
+  Isolate* isolate = Isolate::Current();
+  DARTSCOPE(isolate);
+  UNWRAP_AND_CHECK_PARAM(Function, function, function_in);
+
+  Dart_Handle state = Api::CheckIsolateState(isolate);
+  if (::Dart_IsError(state)) {
+    return state;
+  }
+
+  const Class& cls = Class::Handle(function.origin());
+  if (!cls.IsTopLevel()) {
+    return Dart_NewInteger(cls.id());
+  }
+  return Api::Null();
+}
+
+
 DART_EXPORT Dart_Handle Dart_GetLocalVariables(
                             Dart_ActivationFrame activation_frame) {
   Isolate* isolate = Isolate::Current();
diff --git a/runtime/vm/debugger_api_impl_test.cc b/runtime/vm/debugger_api_impl_test.cc
index 744306a..6c7fa81 100644
--- a/runtime/vm/debugger_api_impl_test.cc
+++ b/runtime/vm/debugger_api_impl_test.cc
@@ -1393,7 +1393,8 @@
   LoadScript(kScriptChars);
 
   Dart_Handle result = Dart_SetNativeResolver(script_lib,
-                                              &InterruptNativeResolver);
+                                              &InterruptNativeResolver,
+                                              NULL);
   EXPECT_VALID(result);
 
   Dart_Handle retval = Invoke("main");
diff --git a/runtime/vm/disassembler_arm64.cc b/runtime/vm/disassembler_arm64.cc
index 2e453d1..ca2c3e8 100644
--- a/runtime/vm/disassembler_arm64.cc
+++ b/runtime/vm/disassembler_arm64.cc
@@ -34,6 +34,7 @@
   void PrintShiftExtendRm(Instr* instr);
   void PrintMemOperand(Instr* instr);
   void PrintS(Instr* instr);
+  void PrintCondition(Instr* instr);
 
   // Handle formatting of instructions and their options.
   int FormatRegister(Instr* instr, const char* option);
@@ -84,8 +85,8 @@
 static const char* reg_names[kNumberOfCpuRegisters] = {
   "r0",  "r1",  "r2",  "r3",  "r4",  "r5",  "r6",  "r7",
   "r8",  "r9",  "r10", "r11", "r12", "r13", "r14", "r15",
-  "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
-  "r24", "ip0", "ip1", "pp",  "ctx", "fp",  "lr",  "r31",
+  "ip0", "ip1", "r18", "r19", "r20", "r21", "r22", "r23",
+  "r24", "r25", "r26", "pp",  "ctx", "fp",  "lr",  "r31",
 };
 
 
@@ -115,6 +116,20 @@
 };
 
 
+// These condition names are defined in a way to match the native disassembler
+// formatting. See for example the command "objdump -d <binary file>".
+static const char* cond_names[kMaxCondition] = {
+  "eq", "ne", "cs" , "cc" , "mi" , "pl" , "vs" , "vc" ,
+  "hi", "ls", "ge", "lt", "gt", "le", "", "invalid",
+};
+
+
+// Print the condition guarding the instruction.
+void ARM64Decoder::PrintCondition(Instr* instr) {
+  Print(cond_names[instr->ConditionField()]);
+}
+
+
 // Print the register shift operands for the instruction. Generally used for
 // data processing instructions.
 void ARM64Decoder::PrintShiftExtendRm(Instr* instr) {
@@ -180,6 +195,18 @@
     Print("]");
   } else {
     switch (instr->Bits(10, 2)) {
+      case 0: {
+        // rn + signed 9-bit immediate, pre-index, no writeback.
+        const int32_t imm9 = instr->SImm9Field();
+        Print("[");
+        PrintRegister(rn, R31IsSP);
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                           remaining_size_in_buffer(),
+                           ", #%d",
+                           imm9);
+        Print("]");
+        break;
+      }
       case 1: {
         const int32_t imm9 = instr->SImm9Field();
         // rn + signed 9-bit immediate, post-index, writeback.
@@ -192,18 +219,6 @@
                                    imm9);
         break;
       }
-      case 3: {
-        const int32_t imm9 = instr->SImm9Field();
-        // rn + signed 9-bit immediate, pre-index, writeback.
-        Print("[");
-        PrintRegister(rn, R31IsSP);
-        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
-                                   remaining_size_in_buffer(),
-                                   ", #%d",
-                                   imm9);
-        Print("] !");
-        break;
-      }
       case 2: {
         const Register rm = instr->RmField();
         const Extend ext = instr->ExtendTypeField();
@@ -222,6 +237,18 @@
         Print("]");
         break;
       }
+      case 3: {
+        const int32_t imm9 = instr->SImm9Field();
+        // rn + signed 9-bit immediate, pre-index, writeback.
+        Print("[");
+        PrintRegister(rn, R31IsSP);
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                   remaining_size_in_buffer(),
+                                   ", #%d",
+                                   imm9);
+        Print("] !");
+        break;
+      }
       default: {
         Print("???");
       }
@@ -250,6 +277,10 @@
     int reg = instr->RtField();
     PrintRegister(reg, R31IsZR);
     return 2;
+  } else if (format[1] == 'a') {  // 'ra: Ra register
+    int reg = instr->RaField();
+    PrintRegister(reg, R31IsZR);
+    return 2;
   }
   UNREACHABLE();
   return -1;
@@ -263,6 +294,71 @@
 // characters that were consumed from the formatting string.
 int ARM64Decoder::FormatOption(Instr* instr, const char* format) {
   switch (format[0]) {
+    case 'b': {
+      if (format[3] == 'i') {
+        ASSERT(STRING_STARTS_WITH(format, "bitimm"));
+        const uint64_t imm = instr->ImmLogical();
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                   remaining_size_in_buffer(),
+                                   "0x%"Px64,
+                                   imm);
+        return 6;
+      } else {
+        ASSERT(STRING_STARTS_WITH(format, "bitpos"));
+        int bitpos = instr->Bits(19, 4) | (instr->Bit(31) << 5);
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                   remaining_size_in_buffer(),
+                                   "#%d",
+                                   bitpos);
+        return 6;
+      }
+    }
+    case 'c': {
+      ASSERT(STRING_STARTS_WITH(format, "cond"));
+      PrintCondition(instr);
+      return 4;
+    }
+    case 'd': {
+      if (format[4] == '2') {
+        ASSERT(STRING_STARTS_WITH(format, "dest26"));
+        int64_t off = instr->SImm26Field() << 2;
+        uword destination = reinterpret_cast<uword>(instr) + off;
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                   remaining_size_in_buffer(),
+                                   "%#" Px "",
+                                   destination);
+      } else {
+        if (format[5] == '4') {
+          ASSERT(STRING_STARTS_WITH(format, "dest14"));
+          int64_t off = instr->SImm14Field() << 2;
+          uword destination = reinterpret_cast<uword>(instr) + off;
+          buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                     remaining_size_in_buffer(),
+                                     "%#" Px "",
+                                     destination);
+        } else {
+          ASSERT(STRING_STARTS_WITH(format, "dest19"));
+          int64_t off = instr->SImm19Field() << 2;
+          uword destination = reinterpret_cast<uword>(instr) + off;
+          buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                     remaining_size_in_buffer(),
+                                     "%#" Px "",
+                                     destination);
+        }
+      }
+      return 6;
+    }
+    case 'h': {
+      ASSERT(STRING_STARTS_WITH(format, "hw"));
+      const int shift = instr->HWField() << 4;
+      if (shift != 0) {
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                   remaining_size_in_buffer(),
+                                   "lsl %d",
+                                   shift);
+      }
+      return 2;
+    }
     case 'i': {  // 'imm12, imm16
       uint64_t imm;
       int ret = 5;
@@ -288,6 +384,38 @@
                                  imm);
       return ret;
     }
+    case 'm': {
+      ASSERT(STRING_STARTS_WITH(format, "memop"));
+      PrintMemOperand(instr);
+      return 5;
+    }
+    case 'p': {
+      if (format[2] == 'a') {
+        ASSERT(STRING_STARTS_WITH(format, "pcadr"));
+        const int64_t immhi = instr->SImm19Field();
+        const int64_t immlo = instr->Bits(29, 2);
+        const int64_t off = (immhi << 2) | immlo;
+        const int64_t pc = reinterpret_cast<int64_t>(instr);
+        const int64_t dest = pc + off;
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                   remaining_size_in_buffer(),
+                                   "0x%"Px64,
+                                   dest);
+      } else {
+        ASSERT(STRING_STARTS_WITH(format, "pcldr"));
+        const int64_t off = instr->SImm19Field() << 2;
+        const int64_t pc = reinterpret_cast<int64_t>(instr);
+        const int64_t dest = pc + off;
+        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
+                                   remaining_size_in_buffer(),
+                                   "0x%"Px64,
+                                   dest);
+      }
+      return 5;
+    }
+    case 'r': {
+      return FormatRegister(instr, format);
+    }
     case 's': {  // 's: S flag.
       if (format[1] == 'h') {
         ASSERT(STRING_STARTS_WITH(format, "shift_op"));
@@ -328,34 +456,6 @@
         UNREACHABLE();
       }
     }
-    case 'r': {
-      return FormatRegister(instr, format);
-    }
-    case 'h': {
-      ASSERT(STRING_STARTS_WITH(format, "hw"));
-      const int shift = instr->HWField() << 4;
-      if (shift != 0) {
-        buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
-                                   remaining_size_in_buffer(),
-                                   "lsl %d",
-                                   shift);
-      }
-      return 2;
-    }
-    case 'm': {
-      ASSERT(STRING_STARTS_WITH(format, "memop"));
-      PrintMemOperand(instr);
-      return 5;
-    }
-    case 'b': {
-      ASSERT(STRING_STARTS_WITH(format, "bitimm"));
-      const uint64_t imm = instr->ImmLogical();
-      buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
-                                 remaining_size_in_buffer(),
-                                 "0x%"Px64,
-                                 imm);
-      return 6;
-    }
     default: {
       UNREACHABLE();
       break;
@@ -421,13 +521,31 @@
 }
 
 
+void ARM64Decoder::DecodeLoadRegLiteral(Instr* instr) {
+  if ((instr->Bit(31) != 0) || (instr->Bit(29) != 0) ||
+      (instr->Bits(24, 3) != 0)) {
+    Unknown(instr);
+  }
+  if (instr->Bit(30)) {
+    Format(instr, "ldrx 'rt, 'pcldr");
+  } else {
+    Format(instr, "ldrw 'rt, 'pcldr");
+  }
+}
+
+
 void ARM64Decoder::DecodeAddSubImm(Instr* instr) {
   switch (instr->Bit(30)) {
     case 0: {
-      if ((instr->RdField() == R31) && (instr->SFField())) {
+      if ((instr->RdField() == R31) && (instr->SField() == 1)) {
         Format(instr, "cmni'sf 'rn, 'imm12s");
       } else {
-        Format(instr, "addi'sf's 'rd, 'rn, 'imm12s");
+        if (((instr->RdField() == R31) || (instr->RnField() == R31)) &&
+            (instr->Imm12Field() == 0) && (instr->Bit(29) == 0)) {
+          Format(instr, "mov'sf 'rd, 'rn");
+        } else {
+          Format(instr, "addi'sf's 'rd, 'rn, 'imm12s");
+        }
       }
       break;
     }
@@ -452,9 +570,14 @@
     case 0:
       Format(instr, "andi'sf 'rd, 'rn, 'bitimm");
       break;
-    case 1:
-      Format(instr, "orri'sf 'rd, 'rn, 'bitimm");
+    case 1: {
+      if (instr->RnField() == R31) {
+        Format(instr, "mov'sf 'rd, 'bitimm");
+      } else {
+        Format(instr, "orri'sf 'rd, 'rn, 'bitimm");
+      }
       break;
+    }
     case 2:
       Format(instr, "eori'sf 'rd, 'rn, 'bitimm");
       break;
@@ -468,6 +591,16 @@
 }
 
 
+void ARM64Decoder::DecodePCRel(Instr* instr) {
+  const int op = instr->Bit(31);
+  if (op == 0) {
+    Format(instr, "adr 'rd, 'pcadr");
+  } else {
+    Unknown(instr);
+  }
+}
+
+
 void ARM64Decoder::DecodeDPImmediate(Instr* instr) {
   if (instr->IsMoveWideOp()) {
     DecodeMoveWide(instr);
@@ -475,6 +608,8 @@
     DecodeAddSubImm(instr);
   } else if (instr->IsLogicalImmOp()) {
     DecodeLogicalImm(instr);
+  } else if (instr->IsPCRelOp()) {
+    DecodePCRel(instr);
   } else {
     Unknown(instr);
   }
@@ -531,6 +666,44 @@
 }
 
 
+void ARM64Decoder::DecodeCompareAndBranch(Instr* instr) {
+  const int op = instr->Bit(24);
+  if (op == 0) {
+    Format(instr, "cbz'sf 'rt, 'dest19");
+  } else {
+    Format(instr, "cbnz'sf 'rt, 'dest19");
+  }
+}
+
+
+void ARM64Decoder::DecodeConditionalBranch(Instr* instr) {
+  if ((instr->Bit(24) != 0) || (instr->Bit(4) != 0)) {
+    Unknown(instr);
+    return;
+  }
+  Format(instr, "b'cond 'dest19");
+}
+
+
+void ARM64Decoder::DecodeTestAndBranch(Instr* instr) {
+  const int op = instr->Bit(24);
+  if (op == 0) {
+    Format(instr, "tbz'sf 'rt, 'bitpos, 'dest14");
+  } else {
+    Format(instr, "tbnz'sf 'rt, 'bitpos, 'dest14");
+  }
+}
+
+
+void ARM64Decoder::DecodeUnconditionalBranch(Instr* instr) {
+  const int op = instr->Bit(31);
+  if (op == 0) {
+    Format(instr, "b 'dest26");
+  } else {
+    Format(instr, "bl 'dest26");
+  }
+}
+
 void ARM64Decoder::DecodeCompareBranch(Instr* instr) {
   if (instr->IsExceptionGenOp()) {
     DecodeExceptionGen(instr);
@@ -538,6 +711,14 @@
     DecodeSystem(instr);
   } else if (instr->IsUnconditionalBranchRegOp()) {
     DecodeUnconditionalBranchReg(instr);
+  } else if (instr->IsCompareAndBranchOp()) {
+    DecodeCompareAndBranch(instr);
+  } else if (instr->IsConditionalBranchOp()) {
+    DecodeConditionalBranch(instr);
+  } else if (instr->IsTestAndBranchOp()) {
+    DecodeTestAndBranch(instr);
+  } else if (instr->IsUnconditionalBranchOp()) {
+    DecodeUnconditionalBranch(instr);
   } else {
     Unknown(instr);
   }
@@ -547,6 +728,8 @@
 void ARM64Decoder::DecodeLoadStore(Instr* instr) {
   if (instr->IsLoadStoreRegOp()) {
     DecodeLoadStoreReg(instr);
+  } else if (instr->IsLoadRegLiteralOp()) {
+    DecodeLoadRegLiteral(instr);
   } else {
     Unknown(instr);
   }
@@ -587,9 +770,15 @@
     case 1:
       Format(instr, "bic'sf 'rd, 'rn, 'shift_op");
       break;
-    case 2:
-      Format(instr, "orr'sf 'rd, 'rn, 'shift_op");
+    case 2: {
+      if ((instr->RnField() == R31) && (instr->IsShift()) &&
+          (instr->Imm16Field() == 0) && (instr->ShiftTypeField() == LSL)) {
+        Format(instr, "mov'sf 'rd, 'rm");
+      } else {
+        Format(instr, "orr'sf 'rd, 'rn, 'shift_op");
+      }
       break;
+    }
     case 3:
       Format(instr, "orn'sf 'rd, 'rn, 'shift_op");
       break;
@@ -612,11 +801,58 @@
 }
 
 
+void ARM64Decoder::DecodeMiscDP2Source(Instr* instr) {
+  if (instr->Bit(29) != 0) {
+    Unknown(instr);
+  }
+
+  const int op = instr->Bits(10, 5);
+  switch (op) {
+    case 2:
+      Format(instr, "udiv'sf 'rd, 'rn, 'rm");
+      break;
+    case 3:
+      Format(instr, "sdiv'sf 'rd, 'rn, 'rm");
+      break;
+    case 8:
+      Format(instr, "lsl'sf 'rd, 'rn, 'rm");
+      break;
+    case 9:
+      Format(instr, "lsr'sf 'rd, 'rn, 'rm");
+      break;
+    case 10:
+      Format(instr, "asr'sf 'rd, 'rn, 'rm");
+      break;
+    default:
+      Unknown(instr);
+      break;
+  }
+}
+
+
+void ARM64Decoder::DecodeMiscDP3Source(Instr* instr) {
+  if ((instr->Bits(29, 2) == 0) && (instr->Bits(21, 3) == 0) &&
+      (instr->Bit(15) == 0)) {
+    if (instr->RaField() == R31) {
+      Format(instr, "mul'sf, 'rd, 'rn, 'rm");
+    } else {
+      Format(instr, "madd'sf 'rd, 'rn, 'rm, 'ra");
+    }
+  } else {
+    Unknown(instr);
+  }
+}
+
+
 void ARM64Decoder::DecodeDPRegister(Instr* instr) {
   if (instr->IsAddSubShiftExtOp()) {
     DecodeAddSubShiftExt(instr);
   } else if (instr->IsLogicalShiftOp()) {
     DecodeLogicalShift(instr);
+  } else if (instr->IsMiscDP2SourceOp()) {
+    DecodeMiscDP2Source(instr);
+  } else if (instr->IsMiscDP3SourceOp()) {
+    DecodeMiscDP3Source(instr);
   } else {
     Unknown(instr);
   }
@@ -646,9 +882,10 @@
     DecodeDPRegister(instr);
   } else if (instr->IsDPSimd1Op()) {
     DecodeDPSimd1(instr);
-  } else {
-    ASSERT(instr->IsDPSimd2Op());
+  } else if (instr->IsDPSimd2Op()) {
     DecodeDPSimd2(instr);
+  } else {
+    Unknown(instr);
   }
 }
 
diff --git a/runtime/vm/flow_graph_allocator.cc b/runtime/vm/flow_graph_allocator.cc
index 00e12a47..db6d1e0 100644
--- a/runtime/vm/flow_graph_allocator.cc
+++ b/runtime/vm/flow_graph_allocator.cc
@@ -105,6 +105,9 @@
   if (TMP != kNoRegister) {
     blocked_cpu_registers_[TMP] = true;
   }
+  if (TMP2 != kNoRegister) {
+    blocked_cpu_registers_[TMP2] = true;
+  }
   if (PP != kNoRegister) {
     blocked_cpu_registers_[PP] = true;
   }
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index 845c7ff..387f31a 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -953,6 +953,9 @@
   if (TMP != kNoRegister) {
     blocked_registers[TMP] = true;
   }
+  if (TMP2 != kNoRegister) {
+    blocked_registers[TMP2] = true;
+  }
   if (PP != kNoRegister) {
     blocked_registers[PP] = true;
   }
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index bbf06f5..dd4fc01 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -301,7 +301,8 @@
           PolymorphicInstanceCallInstr* instance_call =
               current->AsPolymorphicInstanceCall();
           if (!inline_only_recognized_methods ||
-              instance_call->HasSingleRecognizedTarget()) {
+              instance_call->HasSingleRecognizedTarget() ||
+              instance_call->HasSingleDispatcherTarget()) {
             instance_calls_.Add(InstanceCallInfo(instance_call, graph));
           } else {
             // Method not inlined because inlining too deep and method
@@ -473,9 +474,9 @@
       inlining_call_sites_ = call_sites_temp;
       collected_call_sites_->Clear();
       // Inline call sites at the current depth.
+      InlineInstanceCalls();
       InlineStaticCalls();
       InlineClosureCalls();
-      InlineInstanceCalls();
       // Increment the inlining depth. Checked before recursive inlining.
       ++inlining_depth_;
     }
@@ -712,9 +713,18 @@
         return false;
       }
 
-      collected_call_sites_->FindCallSites(callee_graph,
-                                           inlining_depth_,
-                                           &inlined_info_);
+      if (function.IsInvokeFieldDispatcher() ||
+          function.IsNoSuchMethodDispatcher()) {
+        // Append call sites to the currently processed list so that dispatcher
+        // methods get inlined regardless of the current depth.
+        inlining_call_sites_->FindCallSites(callee_graph,
+                                            0,
+                                            &inlined_info_);
+      } else {
+        collected_call_sites_->FindCallSites(callee_graph,
+                                             inlining_depth_,
+                                             &inlined_info_);
+      }
 
       // Add the function to the cache.
       if (!in_cache) {
diff --git a/runtime/vm/gc_marker.cc b/runtime/vm/gc_marker.cc
index 25640ff..caa53a0 100644
--- a/runtime/vm/gc_marker.cc
+++ b/runtime/vm/gc_marker.cc
@@ -365,16 +365,26 @@
     while (queue != NULL) {
       WeakReferenceSet* reference_set = WeakReferenceSet::Pop(&queue);
       ASSERT(reference_set != NULL);
+      intptr_t num_keys = reference_set->num_keys();
+      intptr_t num_values = reference_set->num_values();
+      if ((num_keys == 1) && (num_values == 1) &&
+          reference_set->SingletonKeyEqualsValue()) {
+        // We do not have to process sets that have just one key/value pair
+        // and the key and value are identical.
+        continue;
+      }
       bool is_unreachable = true;
       // Test each key object for reachability.  If a key object is
       // reachable, all value objects should be marked.
-      for (intptr_t k = 0; k < reference_set->num_keys(); ++k) {
+      for (intptr_t k = 0; k < num_keys; ++k) {
         if (!IsUnreachable(*reference_set->get_key(k))) {
-          for (intptr_t v = 0; v < reference_set->num_values(); ++v) {
+          for (intptr_t v = 0; v < num_values; ++v) {
             visitor->VisitPointer(reference_set->get_value(v));
           }
           is_unreachable = false;
-          delete reference_set;
+          // Since we have found a key object that is reachable and all
+          // value objects have been marked we can break out of iterating
+          // this set and move on to the next set.
           break;
         }
       }
@@ -389,17 +399,16 @@
       DrainMarkingStack(isolate, visitor);
     } else {
       // Break out of the loop if there has been no forward process.
+      // All key objects in the weak reference sets are unreachable
+      // so we reset the weak reference sets queue.
+      state->set_delayed_weak_reference_sets(NULL);
       break;
     }
   }
-  // Deallocate any unmarked references on the delay queue.
-  if (state->delayed_weak_reference_sets() != NULL) {
-    WeakReferenceSet* queue = state->delayed_weak_reference_sets();
-    state->set_delayed_weak_reference_sets(NULL);
-    while (queue != NULL) {
-      delete WeakReferenceSet::Pop(&queue);
-    }
-  }
+  ASSERT(state->delayed_weak_reference_sets() == NULL);
+  // All weak reference sets are zone allocated and unmarked references which
+  // were on the delay queue will be freed when the zone is released in the
+  // epilog callback.
 }
 
 
diff --git a/runtime/vm/heap.cc b/runtime/vm/heap.cc
index a0743ab..675f98b 100644
--- a/runtime/vm/heap.cc
+++ b/runtime/vm/heap.cc
@@ -104,6 +104,9 @@
   } else {
     ASSERT(space == kOld);
     old_space_->AllocateExternal(size);
+    if (old_space_->NeedsGarbageCollection()) {
+      CollectGarbage(kOld);
+    }
   }
 }
 
@@ -116,6 +119,11 @@
   }
 }
 
+void Heap::PromoteExternal(intptr_t size) {
+  new_space_->FreeExternal(size);
+  old_space_->AllocateExternal(size);
+}
+
 bool Heap::Contains(uword addr) const {
   return new_space_->Contains(addr) ||
       old_space_->Contains(addr);
@@ -203,31 +211,30 @@
 }
 
 
-void Heap::CollectGarbage(Space space, ApiCallbacks api_callbacks) {
+void Heap::CollectGarbage(Space space,
+                          ApiCallbacks api_callbacks,
+                          GCReason reason) {
   Isolate* isolate = Isolate::Current();
   TIMERSCOPE(isolate, time_gc);
   bool invoke_api_callbacks = (api_callbacks == kInvokeApiCallbacks);
   switch (space) {
     case kNew: {
       VMTagScope tagScope(isolate, VMTag::kGCNewSpaceTagId);
-      RecordBeforeGC(kNew, kNewSpace);
+      RecordBeforeGC(kNew, reason);
       UpdateClassHeapStatsBeforeGC(kNew);
       new_space_->Scavenge(invoke_api_callbacks);
       RecordAfterGC();
       PrintStats();
-      // TODO(koda): Replace promotion failure tracking with
-      // old_space_->NeedsGarbageCollection.
-      if (new_space_->HadPromotionFailure() || old_space_->NeedExternalGC()) {
+      if (old_space_->NeedsGarbageCollection()) {
         // Old collections should call the API callbacks.
-        CollectGarbage(kOld, kInvokeApiCallbacks);
+        CollectGarbage(kOld, kInvokeApiCallbacks, kPromotion);
       }
       break;
     }
     case kOld:
     case kCode: {
       VMTagScope tagScope(isolate, VMTag::kGCOldSpaceTagId);
-      bool promotion_failure = new_space_->HadPromotionFailure();
-      RecordBeforeGC(kOld, promotion_failure ? kPromotionFailure : kOldSpace);
+      RecordBeforeGC(kOld, reason);
       UpdateClassHeapStatsBeforeGC(kOld);
       old_space_->MarkSweep(invoke_api_callbacks);
       RecordAfterGC();
@@ -252,13 +259,12 @@
 
 
 void Heap::CollectGarbage(Space space) {
-  ApiCallbacks api_callbacks;
   if (space == kOld) {
-    api_callbacks = kInvokeApiCallbacks;
+    CollectGarbage(space, kInvokeApiCallbacks, kOldSpace);
   } else {
-    api_callbacks = kIgnoreApiCallbacks;
+    ASSERT(space == kNew);
+    CollectGarbage(space, kInvokeApiCallbacks, kNewSpace);
   }
-  CollectGarbage(space, api_callbacks);
 }
 
 
@@ -408,8 +414,8 @@
   switch (gc_reason) {
     case kNewSpace:
       return "new space";
-    case kPromotionFailure:
-      return "promotion failure";
+    case kPromotion:
+      return "promotion";
     case kOldSpace:
       return "old space";
     case kFull:
diff --git a/runtime/vm/heap.h b/runtime/vm/heap.h
index 7d40316..0c49d75 100644
--- a/runtime/vm/heap.h
+++ b/runtime/vm/heap.h
@@ -48,7 +48,7 @@
 
   enum GCReason {
     kNewSpace,
-    kPromotionFailure,
+    kPromotion,
     kOldSpace,
     kFull,
     kGCAtAlloc,
@@ -106,6 +106,8 @@
   // Track external data.
   void AllocateExternal(intptr_t size, Space space);
   void FreeExternal(intptr_t size, Space space);
+  // Move external size from new to old space. Does not by itself trigger GC.
+  void PromoteExternal(intptr_t size);
 
   // Heap contains the specified address.
   bool Contains(uword addr) const;
@@ -142,7 +144,7 @@
   RawObject* FindObject(FindObjectVisitor* visitor) const;
 
   void CollectGarbage(Space space);
-  void CollectGarbage(Space space, ApiCallbacks api_callbacks);
+  void CollectGarbage(Space space, ApiCallbacks api_callbacks, GCReason reason);
   void CollectAllGarbage();
 
   // Enables growth control on the page space heaps.  This should be
diff --git a/runtime/vm/instructions_arm64.cc b/runtime/vm/instructions_arm64.cc
index 0d1b10a..6617537 100644
--- a/runtime/vm/instructions_arm64.cc
+++ b/runtime/vm/instructions_arm64.cc
@@ -21,13 +21,16 @@
       target_address_pool_index_(-1),
       args_desc_(Array::Handle()),
       ic_data_(ICData::Handle()) {
-  UNIMPLEMENTED();
-}
+  ASSERT(code.ContainsInstructionAt(pc));
+  // Last instruction: blr ip0.
+  ASSERT(*(reinterpret_cast<uint32_t*>(end_) - 1) == 0xd63f0200);
 
-
-int CallPattern::LengthInBytes() {
-  UNIMPLEMENTED();
-  return 0;
+  Register reg;
+  ic_data_load_end_ =
+      InstructionPattern::DecodeLoadWordFromPool(end_ - Instr::kInstrSize,
+                                                 &reg,
+                                                 &target_address_pool_index_);
+  ASSERT(reg == IP0);
 }
 
 
@@ -40,8 +43,23 @@
                                            const Array& object_pool,
                                            Register* reg,
                                            Object* obj) {
-  UNIMPLEMENTED();
-  return 0;
+  // 1. LoadWordFromPool
+  // or
+  // 2. LoadDecodableImmediate
+  uword start = 0;
+  Instr* instr = Instr::At(end - Instr::kInstrSize);
+  if (instr->IsLoadStoreRegOp()) {
+    // Case 1.
+    intptr_t index = 0;
+    start = DecodeLoadWordFromPool(end, reg, &index);
+    *obj = object_pool.At(index);
+  } else {
+    // Case 2.
+    intptr_t value = 0;
+    start = DecodeLoadWordImmediate(end, reg, &value);
+    *obj = reinterpret_cast<RawObject*>(value);
+  }
+  return start;
 }
 
 
@@ -53,8 +71,69 @@
 uword InstructionPattern::DecodeLoadWordImmediate(uword end,
                                                   Register* reg,
                                                   intptr_t* value) {
-  UNIMPLEMENTED();
-  return 0;
+  // 1. LoadWordFromPool
+  // or
+  // 2. LoadWordFromPool
+  //    orri
+  // or
+  // 3. LoadPatchableImmediate
+  uword start = end - Instr::kInstrSize;
+  Instr* instr = Instr::At(start);
+  bool odd = false;
+
+  // Case 2.
+  if (instr->IsLogicalImmOp()) {
+    ASSERT(instr->Bit(29) == 1);
+    odd = true;
+    // end points at orri so that we can pass it to DecodeLoadWordFromPool.
+    end = start;
+    start -= Instr::kInstrSize;
+    instr = Instr::At(start);
+    // Case 2 falls through to case 1.
+  }
+
+  // Case 1.
+  if (instr->IsLoadStoreRegOp()) {
+    start = DecodeLoadWordFromPool(end, reg, value);
+    if (odd) {
+      *value |= 1;
+    }
+    return start;
+  }
+
+  // Case 3.
+  // movk dst, imm3, 3; movk dst, imm2, 2; movk dst, imm1, 1; movz dst, imm0, 0
+  ASSERT(instr->IsMoveWideOp());
+  ASSERT(instr->Bits(29, 2) == 3);
+  ASSERT(instr->HWField() == 3);  // movk dst, imm3, 3
+  *reg = instr->RdField();
+  *value = static_cast<int64_t>(instr->Imm16Field()) << 48;
+
+  start -= Instr::kInstrSize;
+  instr = Instr::At(start);
+  ASSERT(instr->IsMoveWideOp());
+  ASSERT(instr->Bits(29, 2) == 3);
+  ASSERT(instr->HWField() == 2);  // movk dst, imm2, 2
+  ASSERT(instr->RdField() == *reg);
+  *value |= static_cast<int64_t>(instr->Imm16Field()) << 32;
+
+  start -= Instr::kInstrSize;
+  instr = Instr::At(start);
+  ASSERT(instr->IsMoveWideOp());
+  ASSERT(instr->Bits(29, 2) == 3);
+  ASSERT(instr->HWField() == 1);  // movk dst, imm1, 1
+  ASSERT(instr->RdField() == *reg);
+  *value |= static_cast<int64_t>(instr->Imm16Field()) << 16;
+
+  start -= Instr::kInstrSize;
+  instr = Instr::At(start);
+  ASSERT(instr->IsMoveWideOp());
+  ASSERT(instr->Bits(29, 2) == 2);
+  ASSERT(instr->HWField() == 0);  // movz dst, imm0, 0
+  ASSERT(instr->RdField() == *reg);
+  *value |= static_cast<int64_t>(instr->Imm16Field());
+
+  return start;
 }
 
 
@@ -66,62 +145,187 @@
 uword InstructionPattern::DecodeLoadWordFromPool(uword end,
                                                  Register* reg,
                                                  intptr_t* index) {
-  UNIMPLEMENTED();
-  return 0;
+  // 1. ldr dst, [pp, offset]
+  // or
+  // 2. movz dst, low_offset, 0
+  //    movk dst, hi_offset, 1 (optional)
+  //    ldr dst, [pp, dst]
+  uword start = end - Instr::kInstrSize;
+  Instr* instr = Instr::At(start);
+  intptr_t offset = 0;
+
+  // Last instruction is always an ldr into a 64-bit X register.
+  ASSERT(instr->IsLoadStoreRegOp() && (instr->Bit(22) == 1) &&
+        (instr->Bits(30, 2) == 3));
+
+  // Grab the destination register from the ldr instruction.
+  *reg = instr->RtField();
+
+  if (instr->Bit(24) == 1) {
+    // pp + scaled unsigned 12-bit immediate offset.
+    // Case 1.
+    offset = instr->Imm12Field() << 3;
+  } else {
+    ASSERT(instr->Bits(10, 2) == 2);
+    // We have to look at the preceding one or two instructions to find the
+    // offset.
+
+    start -= Instr::kInstrSize;
+    instr = Instr::At(start);
+    ASSERT(instr->IsMoveWideOp());
+    ASSERT(instr->RdField() == *reg);
+    if (instr->Bits(29, 2) == 2) {  // movz dst, low_offset, 0
+      ASSERT(instr->HWField() == 0);
+      offset = instr->Imm16Field();
+      // no high offset.
+    } else {
+      ASSERT(instr->Bits(29, 2) == 3);  // movk dst, high_offset, 1
+      ASSERT(instr->HWField() == 1);
+      offset = instr->Imm16Field() << 16;
+
+      start -= Instr::kInstrSize;
+      instr = Instr::At(start);
+      ASSERT(instr->IsMoveWideOp());
+      ASSERT(instr->RdField() == *reg);
+      ASSERT(instr->Bits(29, 2) == 2);  // movz dst, low_offset, 0
+      ASSERT(instr->HWField() == 0);
+      offset |= instr->Imm16Field();
+    }
+  }
+  ASSERT(Utils::IsAligned(offset, 8));
+  *index = (offset - Array::data_offset()) / 8;
+  return start;
 }
 
 
 RawICData* CallPattern::IcData() {
-  UNIMPLEMENTED();
-  return NULL;
+  if (ic_data_.IsNull()) {
+    Register reg;
+    args_desc_load_end_ =
+        InstructionPattern::DecodeLoadObject(ic_data_load_end_,
+                                             object_pool_,
+                                             &reg,
+                                             &ic_data_);
+    ASSERT(reg == R5);
+  }
+  return ic_data_.raw();
 }
 
 
 RawArray* CallPattern::ClosureArgumentsDescriptor() {
-  UNIMPLEMENTED();
-  return NULL;
+  if (args_desc_.IsNull()) {
+    IcData();  // Loading of the ic_data must be decoded first, if not already.
+    Register reg;
+    InstructionPattern::DecodeLoadObject(args_desc_load_end_,
+                                         object_pool_,
+                                         &reg,
+                                         &args_desc_);
+    ASSERT(reg == R4);
+  }
+  return args_desc_.raw();
 }
 
 
 uword CallPattern::TargetAddress() const {
-  UNIMPLEMENTED();
-  return 0;
+  ASSERT(target_address_pool_index_ >= 0);
+  const Object& target_address =
+      Object::Handle(object_pool_.At(target_address_pool_index_));
+  ASSERT(target_address.IsSmi());
+  // The address is stored in the object array as a RawSmi.
+  return reinterpret_cast<uword>(target_address.raw());
 }
 
 
 void CallPattern::SetTargetAddress(uword target_address) const {
-  UNIMPLEMENTED();
+  ASSERT(Utils::IsAligned(target_address, 4));
+  // The address is stored in the object array as a RawSmi.
+  const Smi& smi = Smi::Handle(reinterpret_cast<RawSmi*>(target_address));
+  object_pool_.SetAt(target_address_pool_index_, smi);
+  // No need to flush the instruction cache, since the code is not modified.
 }
 
 
 void CallPattern::InsertAt(uword pc, uword target_address) {
-  UNIMPLEMENTED();
+  Instr* movz0 = Instr::At(pc + (0 * Instr::kInstrSize));
+  Instr* movk1 = Instr::At(pc + (1 * Instr::kInstrSize));
+  Instr* movk2 = Instr::At(pc + (2 * Instr::kInstrSize));
+  Instr* movk3 = Instr::At(pc + (3 * Instr::kInstrSize));
+  Instr* blr = Instr::At(pc + (4 * Instr::kInstrSize));
+  const uint32_t w0 = Utils::Low32Bits(target_address);
+  const uint32_t w1 = Utils::High32Bits(target_address);
+  const uint16_t h0 = Utils::Low16Bits(w0);
+  const uint16_t h1 = Utils::High16Bits(w0);
+  const uint16_t h2 = Utils::Low16Bits(w1);
+  const uint16_t h3 = Utils::High16Bits(w1);
+
+  movz0->SetMoveWideBits(MOVZ, IP0, h0, 0, kDoubleWord);
+  movk1->SetMoveWideBits(MOVK, IP0, h1, 1, kDoubleWord);
+  movk2->SetMoveWideBits(MOVK, IP0, h2, 2, kDoubleWord);
+  movk3->SetMoveWideBits(MOVK, IP0, h3, 3, kDoubleWord);
+  blr->SetUnconditionalBranchRegBits(BLR, IP0);
+
+  ASSERT(kLengthInBytes == 5 * Instr::kInstrSize);
+  CPU::FlushICache(pc, kLengthInBytes);
 }
 
 
 JumpPattern::JumpPattern(uword pc, const Code& code) : pc_(pc) { }
 
 
-int JumpPattern::pattern_length_in_bytes() {
-  UNIMPLEMENTED();
-  return 0;
-}
-
-
 bool JumpPattern::IsValid() const {
-  UNIMPLEMENTED();
-  return false;
+  Instr* movz0 = Instr::At(pc_ + (0 * Instr::kInstrSize));
+  Instr* movk1 = Instr::At(pc_ + (1 * Instr::kInstrSize));
+  Instr* movk2 = Instr::At(pc_ + (2 * Instr::kInstrSize));
+  Instr* movk3 = Instr::At(pc_ + (3 * Instr::kInstrSize));
+  Instr* br = Instr::At(pc_ + (4 * Instr::kInstrSize));
+  return (movz0->IsMoveWideOp()) && (movz0->Bits(29, 2) == 2) &&
+         (movk1->IsMoveWideOp()) && (movk1->Bits(29, 2) == 3) &&
+         (movk2->IsMoveWideOp()) && (movk2->Bits(29, 2) == 3) &&
+         (movk3->IsMoveWideOp()) && (movk3->Bits(29, 2) == 3) &&
+         (br->IsUnconditionalBranchRegOp()) && (br->Bits(16, 5) == 31);
 }
 
 
 uword JumpPattern::TargetAddress() const {
-  UNIMPLEMENTED();
-  return 0;
+  Instr* movz0 = Instr::At(pc_ + (0 * Instr::kInstrSize));
+  Instr* movk1 = Instr::At(pc_ + (1 * Instr::kInstrSize));
+  Instr* movk2 = Instr::At(pc_ + (2 * Instr::kInstrSize));
+  Instr* movk3 = Instr::At(pc_ + (3 * Instr::kInstrSize));
+  const uint16_t imm0 = movz0->Imm16Field();
+  const uint16_t imm1 = movk1->Imm16Field();
+  const uint16_t imm2 = movk2->Imm16Field();
+  const uint16_t imm3 = movk3->Imm16Field();
+  const int64_t target =
+      (static_cast<int64_t>(imm0)) |
+      (static_cast<int64_t>(imm1) << 16) |
+      (static_cast<int64_t>(imm2) << 32) |
+      (static_cast<int64_t>(imm3) << 48);
+  return target;
 }
 
 
 void JumpPattern::SetTargetAddress(uword target_address) const {
-  UNIMPLEMENTED();
+  Instr* movz0 = Instr::At(pc_ + (0 * Instr::kInstrSize));
+  Instr* movk1 = Instr::At(pc_ + (1 * Instr::kInstrSize));
+  Instr* movk2 = Instr::At(pc_ + (2 * Instr::kInstrSize));
+  Instr* movk3 = Instr::At(pc_ + (3 * Instr::kInstrSize));
+  const int32_t movz0_bits = movz0->InstructionBits();
+  const int32_t movk1_bits = movk1->InstructionBits();
+  const int32_t movk2_bits = movk2->InstructionBits();
+  const int32_t movk3_bits = movk3->InstructionBits();
+
+  const uint32_t w0 = Utils::Low32Bits(target_address);
+  const uint32_t w1 = Utils::High32Bits(target_address);
+  const uint16_t h0 = Utils::Low16Bits(w0);
+  const uint16_t h1 = Utils::High16Bits(w0);
+  const uint16_t h2 = Utils::Low16Bits(w1);
+  const uint16_t h3 = Utils::High16Bits(w1);
+
+  movz0->SetInstructionBits((movz0_bits & ~kImm16Mask) | (h0 << kImm16Shift));
+  movk1->SetInstructionBits((movk1_bits & ~kImm16Mask) | (h1 << kImm16Shift));
+  movk2->SetInstructionBits((movk2_bits & ~kImm16Mask) | (h2 << kImm16Shift));
+  movk3->SetInstructionBits((movk3_bits & ~kImm16Mask) | (h3 << kImm16Shift));
+  CPU::FlushICache(pc_, 4 * Instr::kInstrSize);
 }
 
 }  // namespace dart
diff --git a/runtime/vm/instructions_arm64.h b/runtime/vm/instructions_arm64.h
index 7cda4cd..d25b378 100644
--- a/runtime/vm/instructions_arm64.h
+++ b/runtime/vm/instructions_arm64.h
@@ -59,7 +59,7 @@
 
   // This constant length is only valid for inserted call patterns used for
   // lazy deoptimization. Regular call pattern may vary in length.
-  static int LengthInBytes();
+  static const int kLengthInBytes = 5 * Instr::kInstrSize;
 
   static void InsertAt(uword pc, uword target_address);
 
@@ -82,9 +82,11 @@
  public:
   JumpPattern(uword pc, const Code& code);
 
-  static const int kLengthInBytes = 3 * Instr::kInstrSize;
+  static const int kLengthInBytes = 5 * Instr::kInstrSize;
 
-  static int pattern_length_in_bytes();
+  int pattern_length_in_bytes() const {
+    return kLengthInBytes;
+  }
 
   bool IsValid() const;
   uword TargetAddress() const;
diff --git a/runtime/vm/instructions_arm64_test.cc b/runtime/vm/instructions_arm64_test.cc
index 7c2fe8f..fb97900 100644
--- a/runtime/vm/instructions_arm64_test.cc
+++ b/runtime/vm/instructions_arm64_test.cc
@@ -5,6 +5,64 @@
 #include "vm/globals.h"
 #if defined(TARGET_ARCH_ARM64)
 
-// TODO(zra): Port these tests.
+#include "vm/assembler.h"
+#include "vm/cpu.h"
+#include "vm/instructions.h"
+#include "vm/stub_code.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#define __ assembler->
+
+ASSEMBLER_TEST_GENERATE(Call, assembler) {
+  __ BranchLinkPatchable(&StubCode::InvokeDartCodeLabel());
+  __ ret();
+}
+
+
+ASSEMBLER_TEST_RUN(Call, test) {
+  // The return address, which must be the address of an instruction contained
+  // in the code, points to the Ret instruction above, i.e. one instruction
+  // before the end of the code buffer.
+  CallPattern call(test->entry() + test->code().Size() - Instr::kInstrSize,
+                   test->code());
+  EXPECT_EQ(StubCode::InvokeDartCodeLabel().address(),
+            call.TargetAddress());
+}
+
+
+ASSEMBLER_TEST_GENERATE(Jump, assembler) {
+  __ BranchPatchable(&StubCode::InvokeDartCodeLabel());
+  __ BranchPatchable(&StubCode::AllocateArrayLabel());
+}
+
+
+ASSEMBLER_TEST_RUN(Jump, test) {
+  const Code& code = test->code();
+  const Instructions& instrs = Instructions::Handle(code.instructions());
+  bool status =
+      VirtualMemory::Protect(reinterpret_cast<void*>(instrs.EntryPoint()),
+                             instrs.size(),
+                             VirtualMemory::kReadWrite);
+  EXPECT(status);
+  JumpPattern jump1(test->entry(), test->code());
+  EXPECT_EQ(StubCode::InvokeDartCodeLabel().address(),
+            jump1.TargetAddress());
+  JumpPattern jump2(test->entry() + jump1.pattern_length_in_bytes(),
+                    test->code());
+  EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
+            jump2.TargetAddress());
+  uword target1 = jump1.TargetAddress();
+  uword target2 = jump2.TargetAddress();
+  jump1.SetTargetAddress(target2);
+  jump2.SetTargetAddress(target1);
+  EXPECT_EQ(StubCode::AllocateArrayLabel().address(),
+            jump1.TargetAddress());
+  EXPECT_EQ(StubCode::InvokeDartCodeLabel().address(),
+            jump2.TargetAddress());
+}
+
+}  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index 993a614..c296af7 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -2160,6 +2160,13 @@
 }
 
 
+bool PolymorphicInstanceCallInstr::HasSingleDispatcherTarget() const {
+  if (!ic_data().HasOneTarget()) return false;
+  const Function& target = Function::Handle(ic_data().GetTargetAt(0));
+  return target.IsNoSuchMethodDispatcher() || target.IsInvokeFieldDispatcher();
+}
+
+
 LocationSummary* StaticCallInstr::MakeLocationSummary(bool optimizing) const {
   return MakeCallSummary();
 }
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 3d867f6..3bd24ba 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -89,7 +89,7 @@
   V(::, cos, MathCos, 1282146521)                                              \
   V(::, min, MathMin, 1022567780)                                              \
   V(::, max, MathMax, 612058870)                                               \
-  V(::, _doublePow, MathDoublePow, 1591032382)                                 \
+  V(::, _doublePow, MathDoublePow, 2125162289)                                 \
   V(Float32x4, Float32x4., Float32x4Constructor, 1314950569)                   \
   V(Float32x4, Float32x4.zero, Float32x4Zero, 1432281809)                      \
   V(Float32x4, Float32x4.splat, Float32x4Splat, 1148280442)                    \
@@ -2980,6 +2980,8 @@
 
   bool HasSingleRecognizedTarget() const;
 
+  bool HasSingleDispatcherTarget() const;
+
   virtual intptr_t CallCount() const { return ic_data().AggregateCount(); }
 
   DECLARE_INSTRUCTION(PolymorphicInstanceCall)
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index b40b3b3..e08297b 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -182,31 +182,53 @@
 
 
 LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
-  const intptr_t kNumInputs = 0;
-  const intptr_t kNumTemps = 1;
-  LocationSummary* result =
-      new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
-  result->set_out(0, Location::RegisterLocation(R0));
-  result->set_temp(0, Location::RegisterLocation(R4));  // Arg. descriptor.
-  return result;
+  return MakeCallSummary();
 }
 
 
 void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // The arguments to the stub include the closure, as does the arguments
-  // descriptor.
-  Register temp_reg = locs()->temp(0).reg();
+  // Load closure object (first argument) in R1.
   int argument_count = ArgumentCount();
+  __ ldr(R1, Address(SP, (argument_count - 1) * kWordSize));
+
+  // Load arguments descriptor in R4.
   const Array& arguments_descriptor =
       Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
                                                  argument_names()));
-  __ LoadObject(temp_reg, arguments_descriptor);
-  ASSERT(temp_reg == R4);
-  compiler->GenerateDartCall(deopt_id(),
-                             token_pos(),
-                             &StubCode::CallClosureFunctionLabel(),
-                             PcDescriptors::kClosureCall,
-                             locs());
+  __ LoadObject(R4, arguments_descriptor);
+
+  // Load the closure function into R0.
+  __ ldr(R0, FieldAddress(R1, Closure::function_offset()));
+
+  // Load closure context in CTX; note that CTX has already been preserved.
+  __ ldr(CTX, FieldAddress(R1, Closure::context_offset()));
+
+  // R4: Arguments descriptor.
+  // R0: Function.
+  __ ldr(R2, FieldAddress(R0, Function::code_offset()));
+
+  // R2: code.
+  // R5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+  __ LoadImmediate(R5, 0);
+  __ ldr(R2, FieldAddress(R2, Code::instructions_offset()));
+  __ AddImmediate(R2, Instructions::HeaderSize() - kHeapObjectTag);
+  __ blx(R2);
+  compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+                                 deopt_id(),
+                                 token_pos());
+  compiler->RecordSafepoint(locs());
+  // Marks either the continuation point in unoptimized code or the
+  // deoptimization point in optimized code, after call.
+  const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+  if (compiler->is_optimizing()) {
+    compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+  } else {
+    // Add deoptimization continuation point after the call and before the
+    // arguments are removed.
+    compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+                                   deopt_id_after,
+                                   token_pos());
+  }
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index d6a5573..e1dfcde 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -5825,31 +5825,53 @@
 
 
 LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
-  const intptr_t kNumInputs = 0;
-  const intptr_t kNumTemps = 1;
-  LocationSummary* result =
-      new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
-  result->set_out(0, Location::RegisterLocation(EAX));
-  result->set_temp(0, Location::RegisterLocation(EDX));  // Arg. descriptor.
-  return result;
+  return MakeCallSummary();
 }
 
 
 void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // The arguments to the stub include the closure, as does the arguments
-  // descriptor.
-  Register temp_reg = locs()->temp(0).reg();
-  int argument_count = ArgumentCount();
+  // Load closure object (first argument) in EDI.
+  intptr_t argument_count = ArgumentCount();
+  __ movl(EDI, Address(ESP, (argument_count - 1) * kWordSize));
+
+  // Load arguments descriptors.
   const Array& arguments_descriptor =
       Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
                                                  argument_names()));
-  __ LoadObject(temp_reg, arguments_descriptor);
-  ASSERT(temp_reg == EDX);
-  compiler->GenerateDartCall(deopt_id(),
-                             token_pos(),
-                             &StubCode::CallClosureFunctionLabel(),
-                             PcDescriptors::kClosureCall,
-                             locs());
+  __ LoadObject(EDX, arguments_descriptor);
+
+  // Load the closure function into EAX.
+  __ movl(EAX, FieldAddress(EDI, Closure::function_offset()));
+
+  // Load closure context in CTX; note that CTX has already been preserved.
+  __ movl(CTX, FieldAddress(EDI, Closure::context_offset()));
+
+  // EBX: Code (compiled code or lazy compile stub).
+  __ movl(EBX, FieldAddress(EAX, Function::code_offset()));
+
+  // EAX: Function.
+  // EDX: Arguments descriptor array.
+  // ECX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+  __ xorl(ECX, ECX);
+  __ movl(EBX, FieldAddress(EBX, Code::instructions_offset()));
+  __ addl(EBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+  __ call(EBX);
+  compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+                                 deopt_id(),
+                                 token_pos());
+  compiler->RecordSafepoint(locs());
+  // Marks either the continuation point in unoptimized code or the
+  // deoptimization point in optimized code, after call.
+  const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+  if (compiler->is_optimizing()) {
+    compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+  } else {
+    // Add deoptimization continuation point after the call and before the
+    // arguments are removed.
+    compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+                                   deopt_id_after,
+                                   token_pos());
+  }
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index ad6a564..7725d2c 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -205,31 +205,51 @@
 
 
 LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
-  const intptr_t kNumInputs = 0;
-  const intptr_t kNumTemps = 1;
-  LocationSummary* result =
-      new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
-  result->set_out(0, Location::RegisterLocation(V0));
-  result->set_temp(0, Location::RegisterLocation(S4));  // Arg. descriptor.
-  return result;
+  return MakeCallSummary();
 }
 
 
 void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // The arguments to the stub include the closure, as does the arguments
-  // descriptor.
-  Register temp_reg = locs()->temp(0).reg();
+  // Load closure object (first argument) in T1.
   int argument_count = ArgumentCount();
+  __ lw(T1, Address(SP, (argument_count - 1) * kWordSize));
+
+  // Load arguments descriptor in S4.
   const Array& arguments_descriptor =
       Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
                                                  argument_names()));
-  ASSERT(temp_reg == S4);
-  __ LoadObject(temp_reg, arguments_descriptor);
-  compiler->GenerateDartCall(deopt_id(),
-                             token_pos(),
-                             &StubCode::CallClosureFunctionLabel(),
-                             PcDescriptors::kClosureCall,
-                             locs());
+  __ LoadObject(S4, arguments_descriptor);
+
+  // Load the closure function in T0.
+  __ lw(T0, FieldAddress(T1, Closure::function_offset()));
+
+  // Load closure context in CTX; note that CTX has already been preserved.
+  __ lw(CTX, FieldAddress(T1, Closure::context_offset()));
+
+  // Load closure function code in T2.
+  // S4: arguments descriptor array.
+  // S5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+  __ LoadImmediate(S5, 0);
+  __ lw(T2, FieldAddress(T0, Function::code_offset()));
+  __ lw(T2, FieldAddress(T2, Code::instructions_offset()));
+  __ AddImmediate(T2, Instructions::HeaderSize() - kHeapObjectTag);
+  __ jalr(T2);
+  compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+                                 deopt_id(),
+                                 token_pos());
+  compiler->RecordSafepoint(locs());
+  // Marks either the continuation point in unoptimized code or the
+  // deoptimization point in optimized code, after call.
+  const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+  if (compiler->is_optimizing()) {
+    compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+  } else {
+    // Add deoptimization continuation point after the call and before the
+    // arguments are removed.
+    compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+                                   deopt_id_after,
+                                   token_pos());
+  }
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index bbdd1fc..c6c7def 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -5414,31 +5414,53 @@
 
 
 LocationSummary* ClosureCallInstr::MakeLocationSummary(bool opt) const {
-  const intptr_t kNumInputs = 0;
-  const intptr_t kNumTemps = 1;
-  LocationSummary* result =
-      new LocationSummary(kNumInputs, kNumTemps, LocationSummary::kCall);
-  result->set_out(0, Location::RegisterLocation(RAX));
-  result->set_temp(0, Location::RegisterLocation(R10));  // Arg. descriptor.
-  return result;
+  return MakeCallSummary();
 }
 
 
 void ClosureCallInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
-  // The arguments to the stub include the closure, as does the arguments
-  // descriptor.
-  Register temp_reg = locs()->temp(0).reg();
-  int argument_count = ArgumentCount();
+  // Load closure object (first argument) in R13.
+  intptr_t argument_count = ArgumentCount();
+  __ movq(R13, Address(RSP, (argument_count - 1) * kWordSize));
+
+  // Arguments descriptor is expected in R10.
   const Array& arguments_descriptor =
       Array::ZoneHandle(ArgumentsDescriptor::New(argument_count,
                                                  argument_names()));
-  __ LoadObject(temp_reg, arguments_descriptor, PP);
-  ASSERT(temp_reg == R10);
-  compiler->GenerateDartCall(deopt_id(),
-                             token_pos(),
-                             &StubCode::CallClosureFunctionLabel(),
-                             PcDescriptors::kClosureCall,
-                             locs());
+  __ LoadObject(R10, arguments_descriptor, PP);
+
+  // Load the actual closure function.
+  __ movq(RAX, FieldAddress(R13, Closure::function_offset()));
+
+  // Load closure context in CTX; note that CTX has already been preserved.
+  __ movq(CTX, FieldAddress(R13, Closure::context_offset()));
+
+  // Load closure function code in RAX.
+  __ movq(RCX, FieldAddress(RAX, Function::code_offset()));
+
+  // RAX: Function.
+  // R10: Arguments descriptor array.
+  // RBX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
+  __ xorq(RBX, RBX);
+  __ movq(RCX, FieldAddress(RCX, Code::instructions_offset()));
+  __ addq(RCX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
+  __ call(RCX);
+  compiler->AddCurrentDescriptor(PcDescriptors::kClosureCall,
+                                 deopt_id(),
+                                 token_pos());
+  compiler->RecordSafepoint(locs());
+  // Marks either the continuation point in unoptimized code or the
+  // deoptimization point in optimized code, after call.
+  const intptr_t deopt_id_after = Isolate::ToDeoptAfter(deopt_id());
+  if (compiler->is_optimizing()) {
+    compiler->AddDeoptIndexAtCall(deopt_id_after, token_pos());
+  } else {
+    // Add deoptimization continuation point after the call and before the
+    // arguments are removed.
+    compiler->AddCurrentDescriptor(PcDescriptors::kDeopt,
+                                   deopt_id_after,
+                                   token_pos());
+  }
   __ Drop(argument_count);
 }
 
diff --git a/runtime/vm/intrinsifier.cc b/runtime/vm/intrinsifier.cc
index 993fe06..6c6f03a9 100644
--- a/runtime/vm/intrinsifier.cc
+++ b/runtime/vm/intrinsifier.cc
@@ -125,6 +125,7 @@
 
   // Set up all core lib functions that can be intrisified.
   lib = Library::CoreLibrary();
+  ASSERT(!lib.IsNull());
   CORE_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
 
   // Integer intrinsics are in the core library, but we don't want to intrinsify
@@ -135,12 +136,19 @@
 
   // Set up all math lib functions that can be intrisified.
   lib = Library::MathLibrary();
+  ASSERT(!lib.IsNull());
   MATH_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
 
   // Set up all dart:typed_data lib functions that can be intrisified.
   lib = Library::TypedDataLibrary();
+  ASSERT(!lib.IsNull());
   TYPED_DATA_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
 
+  // Setup all dart:profiler lib functions that can be intrinsified.
+  lib = Library::ProfilerLibrary();
+  ASSERT(!lib.IsNull());
+  PROFILER_LIB_INTRINSIC_LIST(SETUP_FUNCTION);
+
 #undef SETUP_FUNCTION
 }
 
@@ -172,6 +180,8 @@
     TYPED_DATA_LIB_INTRINSIC_LIST(FIND_INTRINSICS);
   } else if (lib.raw() == Library::MathLibrary()) {
     MATH_LIB_INTRINSIC_LIST(FIND_INTRINSICS);
+  } else if (lib.raw() == Library::ProfilerLibrary()) {
+    PROFILER_LIB_INTRINSIC_LIST(FIND_INTRINSICS);
   }
 #undef FIND_INTRINSICS
 }
diff --git a/runtime/vm/intrinsifier.h b/runtime/vm/intrinsifier.h
index 82f4d11..9f36db3 100644
--- a/runtime/vm/intrinsifier.h
+++ b/runtime/vm/intrinsifier.h
@@ -134,6 +134,11 @@
   V(_Float64x2Array, ., TypedData_Float64x2Array_factory, 1654170890)          \
 
 
+#define PROFILER_LIB_INTRINSIC_LIST(V)                                         \
+  V(_UserTag, makeCurrent, UserTag_makeCurrent, 1038211262)                    \
+  V(::, _getCurrentTag, Profiler_getCurrentTag, 1559793589)                    \
+  V(::, _clearCurrentTag, Profiler_clearCurrentTag, 874694572)                 \
+
 // TODO(srdjan): Implement _FixedSizeArrayIterator, get:current and
 //   _FixedSizeArrayIterator, moveNext.
 
@@ -158,6 +163,7 @@
   CORE_INTEGER_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
   MATH_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
   TYPED_DATA_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
+  PROFILER_LIB_INTRINSIC_LIST(DECLARE_FUNCTION)
 
 #undef DECLARE_FUNCTION
 };
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 1b29a2b..84ef39b 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -1707,6 +1707,52 @@
 }
 
 
+// On stack: user tag (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+  // R1: Isolate.
+  Isolate* isolate = Isolate::Current();
+  __ LoadImmediate(R1, reinterpret_cast<uword>(isolate));
+  // R0: UserTag.
+  __ ldr(R0, Address(SP, + 0 * kWordSize));
+  // Set Isolate::current_tag_.
+  __ str(R0, Address(R1, Isolate::current_tag_offset()));
+  // R0: UserTag's tag.
+  __ ldr(R0, FieldAddress(R0, UserTag::tag_offset()));
+  // Set Isolate::user_tag_.
+  __ str(R0, Address(R1, Isolate::user_tag_offset()));
+  // Set return value.
+  const int32_t raw_null = reinterpret_cast<int32_t>(Object::null());
+  __ LoadImmediate(R0, raw_null);
+  __ Ret();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+  // R1: Isolate.
+  Isolate* isolate = Isolate::Current();
+  __ LoadImmediate(R1, reinterpret_cast<uword>(isolate));
+  // Set return value to Isolate::current_tag_.
+  __ ldr(R0, Address(R1, Isolate::current_tag_offset()));
+  __ Ret();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+  // R1: Isolate.
+  Isolate* isolate = Isolate::Current();
+  __ LoadImmediate(R1, reinterpret_cast<uword>(isolate));
+  // Set return value to Isolate::current_tag_.
+  __ ldr(R0, Address(R1, Isolate::current_tag_offset()));
+  // Clear Isolate::current_tag_.
+  const int32_t raw_null = reinterpret_cast<int32_t>(UserTag::null());
+  __ LoadImmediate(R2, raw_null);
+  __ str(R2, Address(R1, Isolate::current_tag_offset()));
+  // Clear Isolate::user_tag_.
+  __ eor(R2, R2, ShifterOperand(R2));
+  __ str(R2, Address(R1, Isolate::user_tag_offset()));
+  __ Ret();
+}
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index 6369048..3312c6b 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -403,6 +403,20 @@
 }
 
 
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+  UNIMPLEMENTED();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+  UNIMPLEMENTED();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+  UNIMPLEMENTED();
+}
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index 6b4b5d2..cbf7d00 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -1739,6 +1739,62 @@
   StringEquality(assembler, kTwoByteStringCid);
 }
 
+
+// On stack: user tag (+1), return-address (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+  Isolate* isolate = Isolate::Current();
+  const Address current_tag_addr =
+      Address::Absolute(reinterpret_cast<uword>(isolate) +
+                        Isolate::current_tag_offset());
+  const Address user_tag_addr =
+      Address::Absolute(reinterpret_cast<uword>(isolate) +
+                        Isolate::user_tag_offset());
+  // EAX: UserTag.
+  __ movl(EAX, Address(ESP, + 1 * kWordSize));
+  // Set Isolate::current_tag_.
+  __ movl(current_tag_addr, EAX);
+  // EAX: UserTag's tag.
+  __ movl(EAX, FieldAddress(EAX, UserTag::tag_offset()));
+  // Set Isolate::user_tag_.
+  __ movl(user_tag_addr, EAX);
+  // Set return value.
+  const Immediate& raw_null =
+      Immediate(reinterpret_cast<int32_t>(Object::null()));
+  __ movl(EAX, raw_null);
+  __ ret();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+  Isolate* isolate = Isolate::Current();
+  const Address current_tag_addr =
+      Address::Absolute(reinterpret_cast<uword>(isolate) +
+                        Isolate::current_tag_offset());
+  // Set return value to Isolate::current_tag_.
+  __ movl(EAX, current_tag_addr);
+  __ ret();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+  Isolate* isolate = Isolate::Current();
+  const Address current_tag_addr =
+      Address::Absolute(reinterpret_cast<uword>(isolate) +
+                        Isolate::current_tag_offset());
+  const Address user_tag_addr =
+      Address::Absolute(reinterpret_cast<uword>(isolate) +
+                        Isolate::user_tag_offset());
+  // Set return value to Isolate::current_tag_.
+  __ movl(EAX, current_tag_addr);
+  // Clear Isolate::current_tag_.
+  const Immediate& raw_null =
+      Immediate(reinterpret_cast<int32_t>(UserTag::null()));
+  __ movl(current_tag_addr, raw_null);
+  // Clear Isolate::user_tag_.
+  __ movl(user_tag_addr, Immediate(0));
+  __ ret();
+}
+
 #undef __
 }  // namespace dart
 
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 14b3105..677be141 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -1778,6 +1778,50 @@
   StringEquality(assembler, kTwoByteStringCid);
 }
 
+// On stack: user tag (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+  // T1: Isolate.
+  Isolate* isolate = Isolate::Current();
+  __ LoadImmediate(T1, reinterpret_cast<uword>(isolate));
+  // V0: UserTag.
+  __ lw(V0, Address(SP, + 0 * kWordSize));
+  // Set Isolate::current_tag_.
+  __ sw(V0, Address(T1, Isolate::current_tag_offset()));
+  // V0: UserTag's tag.
+  __ lw(V0, FieldAddress(V0, UserTag::tag_offset()));
+  // Set Isolate::user_tag_.
+  __ sw(V0, Address(T1, Isolate::user_tag_offset()));
+  // Set return value.
+  __ Ret();
+  __ delay_slot()->LoadImmediate(V0, reinterpret_cast<int32_t>(Object::null()));
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+  // V0: Isolate.
+  Isolate* isolate = Isolate::Current();
+  __ LoadImmediate(V0, reinterpret_cast<uword>(isolate));
+  // Set return value.
+  __ Ret();
+  __ delay_slot()->lw(V0, Address(V0, Isolate::current_tag_offset()));
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+  // T1: Isolate.
+  Isolate* isolate = Isolate::Current();
+  __ LoadImmediate(T1, reinterpret_cast<uword>(isolate));
+  // Set return value to Isolate::current_tag_.
+  __ lw(V0, Address(T1, Isolate::current_tag_offset()));
+  // Clear Isolate::current_tag_.
+  const int32_t raw_null = reinterpret_cast<int32_t>(UserTag::null());
+  __ LoadImmediate(T0, raw_null);
+  __ sw(T0, Address(T1, Isolate::current_tag_offset()));
+  // Clear Isolate::user_tag_.
+  __ Ret();
+  __ delay_slot()->sw(ZR, Address(T1, Isolate::user_tag_offset()));
+}
+
 }  // namespace dart
 
 #endif  // defined TARGET_ARCH_MIPS
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index b329fb1..0f0d065 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -1647,6 +1647,56 @@
   StringEquality(assembler, kTwoByteStringCid);
 }
 
+
+// On stack: user tag (+1), return-address (+0).
+void Intrinsifier::UserTag_makeCurrent(Assembler* assembler) {
+  // RBX: Isolate.
+  Isolate* isolate = Isolate::Current();
+  const Immediate& isolate_address =
+      Immediate(reinterpret_cast<int64_t>(isolate));
+  __ movq(RBX, isolate_address);
+  // RAX: UserTag.
+  __ movq(RAX, Address(RSP, + 1 * kWordSize));
+  // Set Isolate::current_tag_.
+  __ movq(Address(RBX, Isolate::current_tag_offset()), RAX);
+  // RAX: UserTag's tag.
+  __ movq(RAX, FieldAddress(RAX, UserTag::tag_offset()));
+  // Set Isolate::user_tag_.
+  __ movq(Address(RBX, Isolate::user_tag_offset()), RAX);
+  // Set return value.
+  __ LoadObject(RAX, Object::null_object(), PP);
+  __ ret();
+}
+
+
+void Intrinsifier::Profiler_getCurrentTag(Assembler* assembler) {
+  // RBX: Isolate.
+  Isolate* isolate = Isolate::Current();
+  const Immediate& isolate_address =
+      Immediate(reinterpret_cast<int64_t>(isolate));
+  __ movq(RBX, isolate_address);
+  // Set return value to Isolate::current_tag_.
+  __ movq(RAX, Address(RBX, Isolate::current_tag_offset()));
+  __ ret();
+}
+
+
+void Intrinsifier::Profiler_clearCurrentTag(Assembler* assembler) {
+  // RBX: Isolate.
+  Isolate* isolate = Isolate::Current();
+  const Immediate& isolate_address =
+      Immediate(reinterpret_cast<int64_t>(isolate));
+  __ movq(RBX, isolate_address);
+  // Set return value to Isolate::current_tag_.
+  __ movq(RAX, Address(RBX, Isolate::current_tag_offset()));
+  // Clear Isolate::current_tag_.
+  __ LoadObject(RCX, Object::null_object(), PP);
+  __ movq(Address(RBX, Isolate::current_tag_offset()), RCX);
+  // Clear Isolate::user_tag_.
+  __ movq(Address(RBX, Isolate::user_tag_offset()), Immediate(0));
+  __ ret();
+}
+
 #undef __
 
 }  // namespace dart
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index aa8f2a7..511da89 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -336,11 +336,14 @@
       object_id_ring_(NULL),
       profiler_data_(NULL),
       thread_state_(NULL),
+      tag_table_(GrowableObjectArray::null()),
+      current_tag_(UserTag::null()),
       next_(NULL),
       REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_INITIALIZERS)
       REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_INIT)
       reusable_handles_() {
   set_vm_tag(VMTag::kIdleTagId);
+  set_user_tag(UserTags::kNoUserTag);
 }
 #undef REUSABLE_HANDLE_SCOPE_INIT
 #undef REUSABLE_HANDLE_INITIALIZERS
@@ -408,9 +411,6 @@
   Isolate* result = new Isolate();
   ASSERT(result != NULL);
 
-  // Setup for profiling.
-  Profiler::InitProfilingForIsolate(result);
-
   // Add to isolate list.
   AddIsolateTolist(result);
 
@@ -453,7 +453,6 @@
     }
   }
 
-
   return result;
 }
 
@@ -852,6 +851,12 @@
   // Visit the top context which is stored in the isolate.
   visitor->VisitPointer(reinterpret_cast<RawObject**>(&top_context_));
 
+  // Visit the current tag which is stored in the isolate.
+  visitor->VisitPointer(reinterpret_cast<RawObject**>(&current_tag_));
+
+  // Visit the tag table which is stored in the isolate.
+  visitor->VisitPointer(reinterpret_cast<RawObject**>(&tag_table_));
+
   // Visit objects in the debugger.
   debugger()->VisitObjectPointers(visitor);
 
@@ -870,6 +875,13 @@
 }
 
 
+void Isolate::VisitPrologueWeakPersistentHandles(HandleVisitor* visitor) {
+  if (api_state() != NULL) {
+    api_state()->VisitPrologueWeakHandles(visitor);
+  }
+}
+
+
 void Isolate::PrintToJSONStream(JSONStream* stream, bool ref) {
   JSONObject jsobj(stream);
   jsobj.AddProperty("type", (ref ? "@Isolate" : "Isolate"));
@@ -902,10 +914,14 @@
   }
   {
     JSONObject jsheap(&jsobj, "heap");
-    jsheap.AddProperty("usedNew", heap()->UsedInWords(Heap::kNew));
-    jsheap.AddProperty("capacityNew", heap()->CapacityInWords(Heap::kNew));
-    jsheap.AddProperty("usedOld", heap()->UsedInWords(Heap::kOld));
-    jsheap.AddProperty("capacityOld", heap()->CapacityInWords(Heap::kOld));
+    jsheap.AddProperty("usedNew",
+                       heap()->UsedInWords(Heap::kNew) * kWordSize);
+    jsheap.AddProperty("capacityNew",
+                       heap()->CapacityInWords(Heap::kNew) * kWordSize);
+    jsheap.AddProperty("usedOld",
+                       heap()->UsedInWords(Heap::kOld) * kWordSize);
+    jsheap.AddProperty("capacityOld",
+                       heap()->CapacityInWords(Heap::kOld) * kWordSize);
   }
 
   // TODO(turnidge): Don't compute a full stack trace every time we
@@ -971,6 +987,24 @@
 }
 
 
+void Isolate::set_tag_table(const GrowableObjectArray& value) {
+  tag_table_ = value.raw();
+}
+
+
+void Isolate::set_current_tag(const UserTag& tag) {
+  intptr_t user_tag = tag.tag();
+  set_user_tag(static_cast<uword>(user_tag));
+  current_tag_ = tag.raw();
+}
+
+
+void Isolate::clear_current_tag() {
+  set_user_tag(UserTags::kNoUserTag);
+  current_tag_ = UserTag::null();
+}
+
+
 void Isolate::VisitIsolates(IsolateVisitor* visitor) {
   if (visitor == NULL) {
     return;
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 2998fd8..b699552 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -30,6 +30,7 @@
 class Error;
 class Field;
 class Function;
+class GrowableObjectArray;
 class HandleScope;
 class HandleVisitor;
 class Heap;
@@ -42,18 +43,21 @@
 class MessageHandler;
 class Mutex;
 class Object;
+class ObjectIdRing;
 class ObjectPointerVisitor;
 class ObjectStore;
 class RawInstance;
 class RawArray;
 class RawContext;
 class RawDouble;
+class RawGrowableObjectArray;
 class RawMint;
 class RawObject;
 class RawInteger;
 class RawError;
 class RawFloat32x4;
 class RawInt32x4;
+class RawUserTag;
 class SampleBuffer;
 class Simulator;
 class StackResource;
@@ -61,7 +65,7 @@
 class StubCode;
 class TypeArguments;
 class TypeParameter;
-class ObjectIdRing;
+class UserTag;
 
 
 class IsolateVisitor {
@@ -113,8 +117,9 @@
                            bool validate_frames);
 
   // Visits weak object pointers.
-  void VisitWeakPersistentHandles(HandleVisitor* visit,
+  void VisitWeakPersistentHandles(HandleVisitor* visitor,
                                   bool visit_prologue_weak_persistent_handles);
+  void VisitPrologueWeakPersistentHandles(HandleVisitor* visitor);
 
   StoreBuffer* store_buffer() { return &store_buffer_; }
   static intptr_t store_buffer_offset() {
@@ -470,6 +475,23 @@
     return &vm_tag_counters_;
   }
 
+  uword user_tag() const {
+    return user_tag_;
+  }
+  static intptr_t user_tag_offset() {
+    return OFFSET_OF(Isolate, user_tag_);
+  }
+  static intptr_t current_tag_offset() {
+    return OFFSET_OF(Isolate, current_tag_);
+  }
+
+  RawGrowableObjectArray* tag_table() const { return tag_table_; }
+  void set_tag_table(const GrowableObjectArray& value);
+
+  RawUserTag* current_tag() const { return current_tag_; }
+  void set_current_tag(const UserTag& tag);
+  void clear_current_tag();
+
 #if defined(DEBUG)
 #define REUSABLE_HANDLE_SCOPE_ACCESSORS(object)                                \
   void set_reusable_##object##_handle_scope_active(bool value) {               \
@@ -499,6 +521,11 @@
 
   void ProfileIdle();
 
+  void set_user_tag(uword tag) {
+    user_tag_ = tag;
+  }
+
+
   template<class T> T* AllocateReusableHandle();
 
   static ThreadLocalKey isolate_key;
@@ -553,6 +580,9 @@
   InterruptableThreadState* thread_state_;
 
   VMTagCounters vm_tag_counters_;
+  uword user_tag_;
+  RawGrowableObjectArray* tag_table_;
+  RawUserTag* current_tag_;
 
   // Isolate list next pointer.
   Isolate* next_;
diff --git a/runtime/vm/native_entry.cc b/runtime/vm/native_entry.cc
index 525bfea..9e5fa8e 100644
--- a/runtime/vm/native_entry.cc
+++ b/runtime/vm/native_entry.cc
@@ -8,6 +8,7 @@
 
 #include "vm/dart_api_impl.h"
 #include "vm/dart_api_state.h"
+#include "vm/object_store.h"
 #include "vm/tags.h"
 
 
@@ -42,6 +43,37 @@
 }
 
 
+const uint8_t* NativeEntry::ResolveSymbolInLibrary(const Library& library,
+                                                uword pc) {
+  if (library.native_entry_symbol_resolver() == 0) {
+    // Cannot reverse lookup native entries.
+    return NULL;
+  }
+  Dart_NativeEntrySymbol symbol_resolver =
+      library.native_entry_symbol_resolver();
+  return symbol_resolver(reinterpret_cast<Dart_NativeFunction>(pc));
+}
+
+
+const uint8_t* NativeEntry::ResolveSymbol(uword pc) {
+  Isolate* isolate = Isolate::Current();
+  const GrowableObjectArray& libs =
+      GrowableObjectArray::Handle(isolate->object_store()->libraries());
+  ASSERT(!libs.IsNull());
+  intptr_t num_libs = libs.Length();
+  Library& lib = Library::Handle();
+  for (intptr_t i = 0; i < num_libs; i++) {
+    lib ^= libs.At(i);
+    ASSERT(!lib.IsNull());
+    const uint8_t* r = ResolveSymbolInLibrary(lib, pc);
+    if (r != NULL) {
+      return r;
+    }
+  }
+  return NULL;
+}
+
+
 const ExternalLabel& NativeEntry::NativeCallWrapperLabel() {
   return native_call_label;
 }
diff --git a/runtime/vm/native_entry.h b/runtime/vm/native_entry.h
index 6c98d3a..53fbb03 100644
--- a/runtime/vm/native_entry.h
+++ b/runtime/vm/native_entry.h
@@ -95,6 +95,9 @@
                                       const String& function_name,
                                       int number_of_arguments,
                                       bool* auto_setup_scope);
+  static const uint8_t* ResolveSymbolInLibrary(const Library& library,
+                                               uword pc);
+  static const uint8_t* ResolveSymbol(uword pc);
   static void NativeCallWrapper(Dart_NativeArguments args,
                                 Dart_NativeFunction func);
   static const ExternalLabel& NativeCallWrapperLabel();
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index f81ec3d..d1ec93c 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -9,6 +9,7 @@
 #include "vm/assembler.h"
 #include "vm/cpu.h"
 #include "vm/bigint_operations.h"
+#include "vm/bit_vector.h"
 #include "vm/bootstrap.h"
 #include "vm/class_finalizer.h"
 #include "vm/code_generator.h"
@@ -1141,6 +1142,25 @@
   object_store->set_mirror_reference_class(cls);
   RegisterPrivateClass(cls, Symbols::_MirrorReference(), lib);
 
+  // Pre-register the profiler library so we can place the vm class
+  // UserTag there rather than the core library.
+  lib = Library::LookupLibrary(Symbols::DartProfiler());
+  if (lib.IsNull()) {
+    lib = Library::NewLibraryHelper(Symbols::DartProfiler(), true);
+    lib.Register();
+    isolate->object_store()->set_bootstrap_library(ObjectStore::kProfiler,
+                                                   lib);
+  }
+  ASSERT(!lib.IsNull());
+  ASSERT(lib.raw() == Library::ProfilerLibrary());
+
+  lib = Library::LookupLibrary(Symbols::DartProfiler());
+  ASSERT(!lib.IsNull());
+  cls = Class::New<UserTag>();
+  object_store->set_user_tag_class(cls);
+  RegisterPrivateClass(cls, Symbols::_UserTag(), lib);
+  pending_classes.Add(cls);
+
   // Setup some default native field classes which can be extended for
   // specifying native fields in dart classes.
   Library::InitNativeWrappersLibrary(isolate);
@@ -1439,6 +1459,9 @@
 
   cls = Class::New<MirrorReference>();
   object_store->set_mirror_reference_class(cls);
+
+  cls = Class::New<UserTag>();
+  object_store->set_user_tag_class(cls);
 }
 
 
@@ -8937,6 +8960,7 @@
   result.raw_ptr()->exports_ = Object::empty_array().raw();
   result.raw_ptr()->loaded_scripts_ = Array::null();
   result.set_native_entry_resolver(NULL);
+  result.set_native_entry_symbol_resolver(NULL);
   result.raw_ptr()->corelib_imported_ = true;
   result.set_debuggable(false);
   result.raw_ptr()->load_state_ = RawLibrary::kAllocated;
@@ -9189,6 +9213,11 @@
 }
 
 
+RawLibrary* Library::ProfilerLibrary() {
+  return Isolate::Current()->object_store()->profiler_library();
+}
+
+
 const char* Library::ToCString() const {
   const char* kFormat = "Library:'%s'";
   const String& name = String::Handle(url());
@@ -10157,6 +10186,9 @@
       "%2" Pd " kind=%d scope=0x%04x begin=%" Pd " end=%" Pd " name=%s\n";
   for (intptr_t i = 0; i < Length(); i++) {
     String& var_name = String::Handle(GetName(i));
+    if (var_name.IsNull()) {
+      var_name = Symbols::Empty().raw();
+    }
     RawLocalVarDescriptors::VarInfo info;
     GetInfo(i, &info);
     len += OS::SNPrint(NULL, 0, kFormat,
@@ -10171,6 +10203,9 @@
   intptr_t num_chars = 0;
   for (intptr_t i = 0; i < Length(); i++) {
     String& var_name = String::Handle(GetName(i));
+    if (var_name.IsNull()) {
+      var_name = Symbols::Empty().raw();
+    }
     RawLocalVarDescriptors::VarInfo info;
     GetInfo(i, &info);
     num_chars += OS::SNPrint((buffer + num_chars),
@@ -11168,25 +11203,52 @@
   if (IsNull()) {
     return "Context (Null)";
   }
+  Zone* zone = Isolate::Current()->current_zone();
   const Context& parent_ctx = Context::Handle(parent());
   if (parent_ctx.IsNull()) {
-    const char* kFormat = "Context num_variables:% " Pd "";
-    intptr_t len = OS::SNPrint(NULL, 0, kFormat, num_variables()) + 1;
-    char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
-    OS::SNPrint(chars, len, kFormat, num_variables());
-    return chars;
+    return zone->PrintToString("Context@%p num_variables:% " Pd "",
+                               this->raw(), num_variables());
   } else {
     const char* parent_str = parent_ctx.ToCString();
-    const char* kFormat = "Context num_variables:% " Pd " parent:{ %s }";
-    intptr_t len = OS::SNPrint(NULL, 0, kFormat,
-                               num_variables(), parent_str) + 1;
-    char* chars = Isolate::Current()->current_zone()->Alloc<char>(len);
-    OS::SNPrint(chars, len, kFormat, num_variables(), parent_str);
-    return chars;
+    return zone->PrintToString(
+        "Context@%p num_variables:% " Pd " parent:{ %s }",
+        this->raw(), num_variables(), parent_str);
   }
 }
 
 
+static void IndentN(int count) {
+  for (int i = 0; i < count; i++) {
+    OS::PrintErr(" ");
+  }
+}
+
+
+void Context::Dump(int indent) const {
+  if (IsNull()) {
+    IndentN(indent);
+    OS::PrintErr("Context@null\n");
+    return;
+  }
+
+  IndentN(indent);
+  OS::PrintErr("Context@%p vars(%" Pd ") {\n", this->raw(), num_variables());
+  Object& obj = Object::Handle();
+  for (intptr_t i = 0; i < num_variables(); i++) {
+    IndentN(indent + 2);
+    obj = At(i);
+    OS::PrintErr("[%" Pd "] = %s\n", i, obj.ToCString());
+  }
+
+  const Context& parent_ctx = Context::Handle(parent());
+  if (!parent_ctx.IsNull()) {
+    parent_ctx.Dump(indent + 2);
+  }
+  IndentN(indent);
+  OS::PrintErr("}\n");
+}
+
+
 void Context::PrintToJSONStream(JSONStream* stream, bool ref) const {
   Object::PrintToJSONStream(stream, ref);
 }
@@ -12621,6 +12683,17 @@
 }
 
 
+intptr_t* Instance::NativeFieldsDataAddr() const {
+  NoGCScope no_gc;
+  RawTypedData* native_fields =
+      reinterpret_cast<RawTypedData*>(*NativeFieldsAddr());
+  if (native_fields == TypedData::null()) {
+    return NULL;
+  }
+  return reinterpret_cast<intptr_t*>(native_fields->ptr()->data_);
+}
+
+
 void Instance::SetNativeField(int index, intptr_t value) const {
   ASSERT(IsValidNativeIndex(index));
   Object& native_fields = Object::Handle(*NativeFieldsAddr());
@@ -18049,14 +18122,16 @@
         // Traverse inlined frames.
         for (InlinedFunctionsIterator it(code, pc); !it.Done(); it.Advance()) {
           function = it.function();
-          code = it.code();
-          ASSERT(function.raw() == code.function());
-          uword pc = it.pc();
-          ASSERT(pc != 0);
-          ASSERT(code.EntryPoint() <= pc);
-          ASSERT(pc < (code.EntryPoint() + code.Size()));
-          total_len += PrintOneStacktrace(
-              isolate, &frame_strings, pc, function, code, *frame_index);
+          if (function.is_visible() || FLAG_verbose_stacktrace) {
+            code = it.code();
+            ASSERT(function.raw() == code.function());
+            uword pc = it.pc();
+            ASSERT(pc != 0);
+            ASSERT(code.EntryPoint() <= pc);
+            ASSERT(pc < (code.EntryPoint() + code.Size()));
+            total_len += PrintOneStacktrace(
+                isolate, &frame_strings, pc, function, code, *frame_index);
+          }
           (*frame_index)++;  // To account for inlined frames.
         }
       } else {
@@ -18266,4 +18341,129 @@
 }
 
 
+void UserTag::MakeActive() const {
+  Isolate* isolate = Isolate::Current();
+  ASSERT(isolate != NULL);
+  isolate->set_current_tag(*this);
+}
+
+
+void UserTag::ClearActive() {
+  Isolate* isolate = Isolate::Current();
+  ASSERT(isolate != NULL);
+  isolate->clear_current_tag();
+}
+
+
+RawUserTag* UserTag::New(const String& label, Heap::Space space) {
+  Isolate* isolate = Isolate::Current();
+  ASSERT(isolate->object_store()->user_tag_class() != Class::null());
+  ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+  // Canonicalize by name.
+  UserTag& result = UserTag::Handle(FindTagInIsolate(isolate, label));
+  if (!result.IsNull()) {
+    // Tag already exists, return existing instance.
+    return result.raw();
+  }
+  if (TagTableIsFull(isolate)) {
+    const String& error = String::Handle(
+        String::NewFormatted("UserTag instance limit (%" Pd ") reached.",
+                             UserTags::kMaxUserTags));
+    const Array& args = Array::Handle(Array::New(1));
+    args.SetAt(0, error);
+    Exceptions::ThrowByType(Exceptions::kUnsupported, args);
+  }
+  // No tag with label exists, create and register with isolate tag table.
+  {
+    RawObject* raw = Object::Allocate(UserTag::kClassId,
+                                      UserTag::InstanceSize(),
+                                      space);
+    NoGCScope no_gc;
+    result ^= raw;
+  }
+  result.set_label(label);
+  AddTagToIsolate(isolate, result);
+  return result.raw();
+}
+
+
+RawUserTag* UserTag::FindTagInIsolate(Isolate* isolate, const String& label) {
+  ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+  const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+      isolate, isolate->tag_table());
+  UserTag& other = UserTag::Handle(isolate);
+  String& tag_label = String::Handle(isolate);
+  for (intptr_t i = 0; i < tag_table.Length(); i++) {
+    other ^= tag_table.At(i);
+    ASSERT(!other.IsNull());
+    tag_label ^= other.label();
+    ASSERT(!tag_label.IsNull());
+    if (tag_label.Equals(label)) {
+      return other.raw();
+    }
+  }
+  return UserTag::null();
+}
+
+
+void UserTag::AddTagToIsolate(Isolate* isolate, const UserTag& tag) {
+  ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+  const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+      isolate, isolate->tag_table());
+  ASSERT(!TagTableIsFull(isolate));
+#if defined(DEBUG)
+  // Verify that no existing tag has the same tag id.
+  UserTag& other = UserTag::Handle(isolate);
+  for (intptr_t i = 0; i < tag_table.Length(); i++) {
+    other ^= tag_table.At(i);
+    ASSERT(!other.IsNull());
+    ASSERT(tag.tag() != other.tag());
+  }
+#endif
+  // Generate the UserTag tag id by taking the length of the isolate's
+  // tag table + kUserTagIdOffset.
+  uword tag_id = tag_table.Length() + UserTags::kUserTagIdOffset;
+  ASSERT(tag_id >= UserTags::kUserTagIdOffset);
+  ASSERT(tag_id < (UserTags::kUserTagIdOffset + UserTags::kMaxUserTags));
+  tag.set_tag(tag_id);
+  tag_table.Add(tag);
+}
+
+
+bool UserTag::TagTableIsFull(Isolate* isolate) {
+  ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+  const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+      isolate, isolate->tag_table());
+  ASSERT(tag_table.Length() <= UserTags::kMaxUserTags);
+  return tag_table.Length() == UserTags::kMaxUserTags;
+}
+
+
+RawUserTag* UserTag::FindTagById(uword tag_id) {
+  Isolate* isolate = Isolate::Current();
+  ASSERT(isolate->tag_table() != GrowableObjectArray::null());
+  const GrowableObjectArray& tag_table = GrowableObjectArray::Handle(
+      isolate, isolate->tag_table());
+  UserTag& tag = UserTag::Handle(isolate);
+  for (intptr_t i = 0; i < tag_table.Length(); i++) {
+    tag ^= tag_table.At(i);
+    if (tag.tag() == tag_id) {
+      return tag.raw();
+    }
+  }
+  return UserTag::null();
+}
+
+
+const char* UserTag::ToCString() const {
+  const String& tag_label = String::Handle(label());
+  return tag_label.ToCString();
+}
+
+
+void UserTag::PrintToJSONStream(JSONStream* stream, bool ref) const {
+  Instance::PrintToJSONStream(stream, ref);
+}
+
+
 }  // namespace dart
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 5ba48aa..38296c7 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -18,6 +18,7 @@
 #include "vm/os.h"
 #include "vm/raw_object.h"
 #include "vm/scanner.h"
+#include "vm/tags.h"
 
 namespace dart {
 
@@ -690,8 +691,12 @@
     raw_ptr()->handle_vtable_ = value;
   }
 
+  static bool is_valid_id(intptr_t value) {
+    return RawObject::ClassIdTag::is_valid(value);
+  }
   intptr_t id() const { return raw_ptr()->id_; }
   void set_id(intptr_t value) const {
+    ASSERT(is_valid_id(value));
     raw_ptr()->id_ = value;
   }
 
@@ -2608,6 +2613,13 @@
   void set_native_entry_resolver(Dart_NativeEntryResolver value) const {
     raw_ptr()->native_entry_resolver_ = value;
   }
+  Dart_NativeEntrySymbol native_entry_symbol_resolver() const {
+    return raw_ptr()->native_entry_symbol_resolver_;
+  }
+  void set_native_entry_symbol_resolver(
+      Dart_NativeEntrySymbol native_symbol_resolver) const {
+    raw_ptr()->native_entry_symbol_resolver_ = native_symbol_resolver;
+  }
 
   RawError* Patch(const Script& script) const;
 
@@ -2646,6 +2658,7 @@
   static RawLibrary* MirrorsLibrary();
   static RawLibrary* NativeWrappersLibrary();
   static RawLibrary* TypedDataLibrary();
+  static RawLibrary* ProfilerLibrary();
 
   // Eagerly compile all classes and functions in the library.
   static RawError* CompileAll();
@@ -3286,6 +3299,9 @@
     StorePointer(&raw_ptr()->exception_handlers_, handlers.raw());
   }
 
+  // TODO(turnidge): Consider dropping this function and making
+  // everybody use owner().  Currently this function is misused - even
+  // while generating the snapshot.
   RawFunction* function() const {
     return reinterpret_cast<RawFunction*>(raw_ptr()->owner_);
   }
@@ -3457,6 +3473,8 @@
   }
   inline void SetAt(intptr_t context_index, const Instance& value) const;
 
+  void Dump(int indent = 0) const;
+
   static const intptr_t kBytesPerElement = kWordSize;
   static const intptr_t kMaxElements = kSmiMax / kBytesPerElement;
 
@@ -4015,6 +4033,7 @@
     return ((index >= 0) && (index < clazz()->ptr()->num_native_fields_));
   }
 
+  intptr_t* NativeFieldsDataAddr() const;
   inline intptr_t GetNativeField(int index) const;
   inline void GetNativeFields(uint16_t num_fields,
                               intptr_t* field_values) const;
@@ -4059,6 +4078,7 @@
   RawObject** NativeFieldsAddr() const {
     return FieldAddrAtOffset(sizeof(RawObject));
   }
+
   void SetFieldAtOffset(intptr_t offset, const Object& value) const {
     StorePointer(FieldAddrAtOffset(offset), value.raw());
   }
@@ -6642,6 +6662,46 @@
 };
 
 
+class UserTag : public Instance {
+ public:
+  uword tag() const { return raw_ptr()->tag(); }
+  void set_tag(uword t) const {
+    ASSERT(t >= UserTags::kUserTagIdOffset);
+    ASSERT(t < UserTags::kUserTagIdOffset + UserTags::kMaxUserTags);
+    raw_ptr()->tag_ = t;
+  };
+  static intptr_t tag_offset() { return OFFSET_OF(RawUserTag, tag_); }
+
+  RawString* label() const {
+    return raw_ptr()->label_;
+  }
+
+  void MakeActive() const;
+  static void ClearActive();
+
+  static intptr_t InstanceSize() {
+    return RoundedAllocationSize(sizeof(RawUserTag));
+  }
+
+  static RawUserTag* New(const String& label,
+                         Heap::Space space = Heap::kOld);
+
+  static bool TagTableIsFull(Isolate* isolate);
+  static RawUserTag* FindTagById(uword tag_id);
+
+ private:
+  static RawUserTag* FindTagInIsolate(Isolate* isolate, const String& label);
+  static void AddTagToIsolate(Isolate* isolate, const UserTag& tag);
+
+  void set_label(const String& tag_label) const {
+    StorePointer(&raw_ptr()->label_, tag_label.raw());
+  }
+
+  FINAL_HEAP_OBJECT_IMPLEMENTATION(UserTag, Instance);
+  friend class Class;
+};
+
+
 // Breaking cycles and loops.
 RawClass* Object::clazz() const {
   uword raw_value = reinterpret_cast<uword>(raw_);
diff --git a/runtime/vm/object_arm64_test.cc b/runtime/vm/object_arm64_test.cc
index 77096ff..33e6df2 100644
--- a/runtime/vm/object_arm64_test.cc
+++ b/runtime/vm/object_arm64_test.cc
@@ -6,6 +6,51 @@
 #include "vm/globals.h"
 #if defined(TARGET_ARCH_ARM64)
 
-// TODO(zra): Port these tests.
+#include "vm/assembler.h"
+#include "vm/object.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+#define __ assembler->
+
+// Generate a simple dart code sequence.
+// This is used to test Code and Instruction object creation.
+void GenerateIncrement(Assembler* assembler) {
+  __ movz(R0, 0, 0);
+  __ Push(R0);
+  __ add(R0, R0, Operand(1));
+  __ str(R0, Address(SP));
+  __ ldr(R1, Address(SP));
+  __ add(R1, R1, Operand(1));
+  __ Pop(R0);
+  __ mov(R0, R1);
+  __ ret();
+}
+
+
+// Generate a dart code sequence that embeds a string object in it.
+// This is used to test Embedded String objects in the instructions.
+void GenerateEmbedStringInCode(Assembler* assembler, const char* str) {
+  const String& string_object =
+      String::ZoneHandle(String::New(str, Heap::kOld));
+  __ PushPP();  // Save caller's pool pointer and load a new one here.
+  __ LoadPoolPointer(PP);
+  __ LoadObject(R0, string_object, PP);
+  __ PopPP();  // Restore caller's pool pointer.
+  __ ret();
+}
+
+
+// Generate a dart code sequence that embeds a smi object in it.
+// This is used to test Embedded Smi objects in the instructions.
+void GenerateEmbedSmiInCode(Assembler* assembler, intptr_t value) {
+  const Smi& smi_object = Smi::ZoneHandle(Smi::New(value));
+  const int64_t val = reinterpret_cast<int64_t>(smi_object.raw());
+  __ LoadImmediate(R0, val, kNoRegister);
+  __ ret();
+}
+
+}  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index bc9e326..7dcc99f 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -53,6 +53,7 @@
     jsregexp_class_(Class::null()),
     weak_property_class_(Class::null()),
     mirror_reference_class_(Class::null()),
+    user_tag_class_(Class::null()),
     symbol_table_(Array::null()),
     canonical_type_arguments_(Array::null()),
     async_library_(Library::null()),
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 3656235..f18f203 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -30,6 +30,7 @@
     kMath,
     kMirrors,
     kTypedData,
+    kProfiler,
   };
 
   ~ObjectStore();
@@ -274,6 +275,13 @@
     mirror_reference_class_ = value.raw();
   }
 
+  RawClass* user_tag_class() const {
+    return user_tag_class_;
+  }
+  void set_user_tag_class(const Class& value) {
+    user_tag_class_ = value.raw();
+  }
+
   RawArray* symbol_table() const { return symbol_table_; }
   void set_symbol_table(const Array& value) { symbol_table_ = value.raw(); }
 
@@ -294,6 +302,7 @@
   RawLibrary* math_library() const { return math_library_; }
   RawLibrary* mirrors_library() const { return mirrors_library_; }
   RawLibrary* typed_data_library() const { return typed_data_library_; }
+  RawLibrary* profiler_library() const { return profiler_library_; }
   void set_bootstrap_library(BootstrapLibraryId index, const Library& value) {
     switch (index) {
       case kAsync:
@@ -323,6 +332,9 @@
       case kTypedData:
         typed_data_library_ = value.raw();
         break;
+      case kProfiler:
+        profiler_library_ = value.raw();
+        break;
       default:
         UNREACHABLE();
     }
@@ -475,6 +487,7 @@
   RawClass* jsregexp_class_;
   RawClass* weak_property_class_;
   RawClass* mirror_reference_class_;
+  RawClass* user_tag_class_;
   RawArray* symbol_table_;
   RawArray* canonical_type_arguments_;
   RawLibrary* async_library_;
@@ -489,6 +502,7 @@
   RawLibrary* native_wrappers_library_;
   RawLibrary* root_library_;
   RawLibrary* typed_data_library_;
+  RawLibrary* profiler_library_;
   RawGrowableObjectArray* libraries_;
   RawGrowableObjectArray* pending_classes_;
   RawGrowableObjectArray* pending_functions_;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 052b904..b6db312 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4,7 +4,6 @@
 
 // TODO(zra): Remove when tests are ready to enable.
 #include "platform/globals.h"
-#if !defined(TARGET_ARCH_ARM64)
 
 #include "vm/assembler.h"
 #include "vm/bigint_operations.h"
@@ -2498,7 +2497,7 @@
   intptr_t retval = 0;
 #if defined(USING_SIMULATOR)
   retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
-      static_cast<int32_t>(entry_point), 0, 0, 0, 0));
+      static_cast<intptr_t>(entry_point), 0, 0, 0, 0));
 #else
   typedef intptr_t (*IncrementCode)();
   retval = reinterpret_cast<IncrementCode>(entry_point)();
@@ -2523,7 +2522,7 @@
   intptr_t retval = 0;
 #if defined(USING_SIMULATOR)
   retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
-      static_cast<int32_t>(entry_point), 0, 0, 0, 0));
+      static_cast<intptr_t>(entry_point), 0, 0, 0, 0));
 #else
   typedef intptr_t (*IncrementCode)();
   retval = reinterpret_cast<IncrementCode>(entry_point)();
@@ -2551,7 +2550,7 @@
   uword retval = 0;
 #if defined(USING_SIMULATOR)
   retval = bit_copy<uword, int64_t>(Simulator::Current()->Call(
-      static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
+      static_cast<intptr_t>(instructions.EntryPoint()), 0, 0, 0, 0));
 #else
   typedef uword (*EmbedStringCode)();
   retval = reinterpret_cast<EmbedStringCode>(instructions.EntryPoint())();
@@ -2578,7 +2577,7 @@
   intptr_t retval = 0;
 #if defined(USING_SIMULATOR)
   retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
-      static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
+      static_cast<intptr_t>(instructions.EntryPoint()), 0, 0, 0, 0));
 #else
   typedef intptr_t (*EmbedSmiCode)();
   retval = reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
@@ -2600,7 +2599,7 @@
   intptr_t retval = 0;
 #if defined(USING_SIMULATOR)
   retval = bit_copy<intptr_t, int64_t>(Simulator::Current()->Call(
-      static_cast<int32_t>(instructions.EntryPoint()), 0, 0, 0, 0));
+      static_cast<intptr_t>(instructions.EntryPoint()), 0, 0, 0, 0));
 #else
   typedef intptr_t (*EmbedSmiCode)();
   retval = reinterpret_cast<EmbedSmiCode>(instructions.EntryPoint())();
@@ -2993,6 +2992,9 @@
 }
 
 
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
 TEST_CASE(StackTraceFormat) {
   const char* kScriptChars =
       "void baz() {\n"
@@ -3053,6 +3055,7 @@
       "#10     main (dart:test-lib:37:24)");
 }
 
+#endif  // !defined(TARGET_ARCH_ARM64)
 
 TEST_CASE(WeakProperty_PreserveCrossGen) {
   Isolate* isolate = Isolate::Current();
@@ -3461,6 +3464,9 @@
 }
 
 
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
 static RawFunction* GetFunction(const Class& cls, const char* name) {
   const Function& result = Function::Handle(cls.LookupDynamicFunction(
       String::Handle(String::New(name))));
@@ -3600,6 +3606,8 @@
   EXPECT_EQ(func_x.raw(), func_x_from_index.raw());
 }
 
+#endif  // !defined(TARGET_ARCH_ARM64)
+
 
 TEST_CASE(FindClosureIndex) {
   // Allocate the class first.
@@ -3680,6 +3688,9 @@
 }
 
 
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
 static void PrintMetadata(const char* name, const Object& data) {
   if (data.IsError()) {
     OS::Print("Error in metadata evaluation for %s: '%s'\n",
@@ -3885,6 +3896,8 @@
   EXPECT(!func_b.IsInlineable());
 }
 
+#endif  // !defined(TARGET_ARCH_ARM64)
+
 
 TEST_CASE(SpecialClassesHaveEmptyArrays) {
   ObjectStore* object_store = Isolate::Current()->object_store();
@@ -3917,6 +3930,9 @@
 }
 
 
+// TODO(zra): Enable test when arm64 is ready.
+#if !defined(TARGET_ARCH_ARM64)
+
 TEST_CASE(ToUserCString) {
   const char* kScriptChars =
       "var simple = 'simple';\n"
@@ -4012,6 +4028,6 @@
   heap->IterateObjects(&verifier);
 }
 
-}  // namespace dart
-
 #endif  // !defined(TARGET_ARCH_ARM64)
+
+}  // namespace dart
diff --git a/runtime/vm/pages.cc b/runtime/vm/pages.cc
index d3cab91..5ebe9b1 100644
--- a/runtime/vm/pages.cc
+++ b/runtime/vm/pages.cc
@@ -703,6 +703,11 @@
     grow_heap_ = Utils::Maximum(capacity_growth_in_pages, heap_growth_rate_);
     heap->RecordData(PageSpace::kPageGrowth, capacity_growth_in_pages);
   }
+  // Limit shrinkage: allow growth by at least half the pages freed by GC.
+  intptr_t freed_pages =
+      (before.capacity_in_words - after.capacity_in_words) /
+      PageSpace::kPageSizeInWords;
+  grow_heap_ = Utils::Maximum(grow_heap_, freed_pages / 2);
   heap->RecordData(PageSpace::kGarbageRatio, collected_garbage_ratio);
   heap->RecordData(PageSpace::kGCTimeFraction,
                    garbage_collection_time_fraction);
diff --git a/runtime/vm/pages.h b/runtime/vm/pages.h
index 07dfac8..220cdaa 100644
--- a/runtime/vm/pages.h
+++ b/runtime/vm/pages.h
@@ -199,7 +199,8 @@
                     GrowthPolicy growth_policy = kControlGrowth);
 
   bool NeedsGarbageCollection() const {
-    return page_space_controller_.NeedsGarbageCollection(usage_);
+    return page_space_controller_.NeedsGarbageCollection(usage_) ||
+           NeedsExternalGC();
   }
 
   intptr_t UsedInWords() const { return usage_.used_in_words; }
@@ -242,7 +243,7 @@
     return page_space_controller_.is_enabled();
   }
 
-  bool NeedExternalGC() {
+  bool NeedsExternalGC() const {
     return UsedInWords() + ExternalInWords() > max_capacity_in_words_;
   }
 
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 6645901..73cee00 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -1407,32 +1407,47 @@
                                                        no_args);
 
   // Pass arguments 1..n to the closure call.
-  ArgumentListNode* closure_args = new ArgumentListNode(token_pos);
+  ArgumentListNode* args = new ArgumentListNode(token_pos);
   const Array& names = Array::Handle(Array::New(desc.NamedCount(), Heap::kOld));
   // Positional parameters.
   intptr_t i = 1;
   for (; i < desc.PositionalCount(); ++i) {
-    closure_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
+    args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
   }
   // Named parameters.
   for (; i < desc.Count(); i++) {
-    closure_args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
+    args->Add(new LoadLocalNode(token_pos, scope->VariableAt(i)));
     intptr_t index = i - desc.PositionalCount();
     names.SetAt(index, String::Handle(desc.NameAt(index)));
   }
-  closure_args->set_names(names);
+  args->set_names(names);
 
-  EnsureSavedCurrentContext();
-  ClosureCallNode* closure_call = new ClosureCallNode(token_pos,
-                                                      getter_call,
-                                                      closure_args);
+  const Class& owner = Class::Handle(func.Owner());
+  ASSERT(!owner.IsNull());
+  AstNode* result = NULL;
+  if (owner.IsSignatureClass() && name.Equals(Symbols::Call())) {
+    EnsureSavedCurrentContext();
+    result = new ClosureCallNode(token_pos, getter_call, args);
+  } else {
+    result = BuildClosureCall(token_pos, getter_call, args);
+  }
 
-  ReturnNode* return_node = new ReturnNode(token_pos, closure_call);
+  ReturnNode* return_node = new ReturnNode(token_pos, result);
   current_block_->statements->Add(return_node);
   return CloseBlock();
 }
 
 
+AstNode* Parser::BuildClosureCall(intptr_t token_pos,
+                                  AstNode* closure,
+                                  ArgumentListNode* arguments) {
+  return new InstanceCallNode(token_pos,
+                              closure,
+                              Symbols::Call(),
+                              arguments);
+}
+
+
 void Parser::SkipBlock() {
   ASSERT(CurrentToken() == Token::kLBRACE);
   GrowableArray<Token::Kind> token_stack(8);
@@ -1901,7 +1916,7 @@
     for (int i = 1; i < arguments->length(); i++) {
       closure_arguments->Add(arguments->NodeAt(i));
     }
-    return new ClosureCallNode(supercall_pos, closure, closure_arguments);
+    return BuildClosureCall(supercall_pos, closure, closure_arguments);
   }
   if (is_no_such_method) {
     arguments = BuildNoSuchMethodArguments(
@@ -6722,7 +6737,7 @@
     EnsureSavedCurrentContext();
     // Function literal in assert implies a call.
     const intptr_t pos = condition->token_pos();
-    condition = new ClosureCallNode(pos, condition, new ArgumentListNode(pos));
+    condition = BuildClosureCall(pos, condition, new ArgumentListNode(pos));
   } else if (condition->IsConditionalExprNode()) {
     ConditionalExprNode* cond_expr = condition->AsConditionalExprNode();
     cond_expr->set_true_expr(InsertClosureCallNodes(cond_expr->true_expr()));
@@ -8241,12 +8256,12 @@
                                        false,
                                        Class::ZoneHandle(cls.raw()),
                                        func_name);
-        return new ClosureCallNode(call_pos, closure, arguments);
+        return BuildClosureCall(call_pos, closure, arguments);
       }
     } else {
       EnsureSavedCurrentContext();
       closure = GenerateStaticFieldLookup(field, call_pos);
-      return new ClosureCallNode(call_pos, closure, arguments);
+      return BuildClosureCall(call_pos, closure, arguments);
     }
     // Could not resolve static method: throw a NoSuchMethodError.
     return ThrowNoSuchMethodError(ident_pos,
@@ -8286,7 +8301,7 @@
   ASSERT(CurrentToken() == Token::kLPAREN);
   EnsureSavedCurrentContext();
   ArgumentListNode* arguments = ParseActualParameters(NULL, kAllowConst);
-  return new ClosureCallNode(call_pos, closure, arguments);
+  return BuildClosureCall(call_pos, closure, arguments);
 }
 
 
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index e1da304..d013abf 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -658,6 +658,10 @@
 
   void AddEqualityNullCheck();
 
+  AstNode* BuildClosureCall(intptr_t token_pos,
+                            AstNode* closure,
+                            ArgumentListNode* arguments);
+
   RawInstance* TryCanonicalize(const Instance& instance, intptr_t token_pos);
 
   Isolate* isolate() const { return isolate_; }
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index 24d3da9..b52c5b4 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -35,6 +35,8 @@
             "Maximum number stack frames walked. Minimum 1. Maximum 255.");
 DEFINE_FLAG(bool, profile_verify_stack_walk, false,
             "Verify instruction addresses while walking the stack.");
+DEFINE_FLAG(bool, profile_native_stack, true,
+            "Use native stack in profiler.");
 
 bool Profiler::initialized_ = false;
 SampleBuffer* Profiler::sample_buffer_ = NULL;
@@ -564,9 +566,20 @@
       PrintOverwrittenCode(&obj);
     } else if (kind() == kTagCode) {
       if (name() == NULL) {
-        const char* tag_name = start() == 0 ? "root" : VMTag::TagName(start());
-        ASSERT(tag_name != NULL);
-        SetName(tag_name);
+        if (UserTags::IsUserTag(start())) {
+          const char* tag_name = UserTags::TagName(start());
+          ASSERT(tag_name != NULL);
+          SetName(tag_name);
+        } else if (VMTag::IsVMTag(start()) ||
+                   VMTag::IsRuntimeEntryTag(start()) ||
+                   VMTag::IsNativeEntryTag(start())) {
+          const char* tag_name = VMTag::TagName(start());
+          ASSERT(tag_name != NULL);
+          SetName(tag_name);
+        } else {
+          ASSERT(start() == 0);
+          SetName("root");
+        }
       }
       PrintTagCode(&obj);
     } else {
@@ -926,7 +939,14 @@
       min_time_ = timestamp;
     }
     // Make sure VM tag is created.
+    if (VMTag::IsNativeEntryTag(sample->vm_tag())) {
+      CreateTag(VMTag::kNativeTagId);
+    } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) {
+      CreateTag(VMTag::kRuntimeTagId);
+    }
     CreateTag(sample->vm_tag());
+    // Make sure user tag is created.
+    CreateUserTag(sample->user_tag());
     // Exclusive tick for bottom frame.
     Tick(sample->At(0), true, timestamp);
     // Inclusive tick for all frames.
@@ -962,6 +982,25 @@
     region->set_creation_serial(visited());
   }
 
+  void CreateUserTag(uword tag) {
+    if (tag == 0) {
+      // None set.
+      return;
+    }
+    intptr_t index = tag_code_table_->FindIndex(tag);
+    if (index >= 0) {
+      // Already created.
+      return;
+    }
+    CodeRegion* region = new CodeRegion(CodeRegion::kTagCode,
+                                        tag,
+                                        tag + 1,
+                                        0);
+    index = tag_code_table_->InsertCodeRegion(region);
+    ASSERT(index >= 0);
+    region->set_creation_serial(visited());
+  }
+
   void Tick(uword pc, bool exclusive, int64_t timestamp) {
     CodeRegionTable::TickResult r;
     intptr_t serial = exclusive ? -1 : visited();
@@ -1101,6 +1140,25 @@
     root_->Tick();
     CodeRegionTrieNode* current = root_;
     if (use_tags()) {
+      intptr_t user_tag_index = FindTagIndex(sample->user_tag());
+      if (user_tag_index >= 0) {
+        current = current->GetChild(user_tag_index);
+        // Give the tag a tick.
+        current->Tick();
+      }
+      if (VMTag::IsNativeEntryTag(sample->vm_tag())) {
+        // Insert a dummy kNativeTagId node.
+        intptr_t tag_index = FindTagIndex(VMTag::kNativeTagId);
+        current = current->GetChild(tag_index);
+        // Give the tag a tick.
+        current->Tick();
+      } else if (VMTag::IsRuntimeEntryTag(sample->vm_tag())) {
+        // Insert a dummy kRuntimeTagId node.
+        intptr_t tag_index = FindTagIndex(VMTag::kRuntimeTagId);
+        current = current->GetChild(tag_index);
+        // Give the tag a tick.
+        current->Tick();
+      }
       intptr_t tag_index = FindTagIndex(sample->vm_tag());
       current = current->GetChild(tag_index);
       // Give the tag a tick.
@@ -1131,7 +1189,13 @@
 
  private:
   intptr_t FindTagIndex(uword tag) const {
+    if (tag == 0) {
+      return -1;
+    }
     intptr_t index = tag_code_table_->FindIndex(tag);
+    if (index <= 0) {
+      return -1;
+    }
     ASSERT(index >= 0);
     ASSERT((tag_code_table_->At(index))->contains(tag));
     return tag_code_table_offset_ + index;
@@ -1406,6 +1470,45 @@
   return At(cursor);
 }
 
+class ProfilerDartStackWalker : public ValueObject {
+ public:
+  explicit ProfilerDartStackWalker(Sample* sample)
+      : sample_(sample),
+        frame_iterator_() {
+    ASSERT(sample_ != NULL);
+  }
+
+  ProfilerDartStackWalker(Sample* sample, uword pc, uword fp, uword sp)
+      : sample_(sample),
+        frame_iterator_(fp, sp, pc) {
+    ASSERT(sample_ != NULL);
+  }
+
+  ProfilerDartStackWalker(Sample* sample, uword fp)
+      : sample_(sample),
+        frame_iterator_(fp) {
+    ASSERT(sample_ != NULL);
+  }
+
+  int walk() {
+    intptr_t frame_index = 0;
+    StackFrame* frame = frame_iterator_.NextFrame();
+    while (frame != NULL) {
+      sample_->SetAt(frame_index, frame->pc());
+      frame_index++;
+      if (frame_index >= FLAG_profile_depth) {
+        break;
+      }
+      frame = frame_iterator_.NextFrame();
+    }
+    return frame_index;
+  }
+
+ private:
+  Sample* sample_;
+  DartFrameIterator frame_iterator_;
+};
+
 // Notes on stack frame walking:
 //
 // The sampling profiler will collect up to Sample::kNumStackFrames stack frames
@@ -1414,9 +1517,9 @@
 // recent GCC versions with optimizing enabled) the stack walking code may
 // fail (sometimes leading to a crash).
 //
-class ProfilerSampleStackWalker : public ValueObject {
+class ProfilerNativeStackWalker : public ValueObject {
  public:
-  ProfilerSampleStackWalker(Sample* sample,
+  ProfilerNativeStackWalker(Sample* sample,
                             uword stack_lower,
                             uword stack_upper,
                             uword pc,
@@ -1431,13 +1534,11 @@
     ASSERT(sample_ != NULL);
   }
 
-  int walk(Heap* heap, uword vm_tag) {
+  int walk(Heap* heap) {
     const intptr_t kMaxStep = 0x1000;  // 4K.
     const bool kWalkStack = true;  // Walk the stack.
     // Always store the exclusive PC.
     sample_->SetAt(0, original_pc_);
-    // Always store the vm tag.
-    sample_->set_vm_tag(vm_tag);
     if (!kWalkStack) {
       // Not walking the stack, only took exclusive sample.
       return 1;
@@ -1466,6 +1567,9 @@
         VerifyCodeAddress(heap, i, reinterpret_cast<uword>(pc));
       }
       sample_->SetAt(i, reinterpret_cast<uword>(pc));
+      if (fp == NULL) {
+        return i + 1;
+      }
       if (!ValidFramePointer(fp)) {
         return i + 1;
       }
@@ -1473,9 +1577,18 @@
       previous_fp = fp;
       fp = CallerFP(fp);
       intptr_t step = fp - previous_fp;
-      if ((step >= kMaxStep) || (fp <= previous_fp) || !ValidFramePointer(fp)) {
+      if (fp == NULL) {
+        return i + 1;
+      }
+      if ((step >= kMaxStep)) {
         // Frame pointer step is too large.
+        return i + 1;
+      }
+      if ((fp <= previous_fp)) {
         // Frame pointer did not move to a higher address.
+        return i + 1;
+      }
+      if (!ValidFramePointer(fp)) {
         // Frame pointer is outside of isolate stack bounds.
         return i + 1;
       }
@@ -1534,9 +1647,11 @@
     const InterruptedThreadState& state,
     void* data) {
   Isolate* isolate = reinterpret_cast<Isolate*>(data);
-  if (isolate == NULL) {
+  if ((isolate == NULL) || (Dart::vm_isolate() == NULL)) {
     return;
   }
+  ASSERT(isolate != Dart::vm_isolate());
+  ASSERT(isolate->stub_code() != NULL);
   VMTagCounters* counters = isolate->vm_tag_counters();
   ASSERT(counters != NULL);
   counters->Increment(isolate->vm_tag());
@@ -1550,16 +1665,29 @@
   }
   Sample* sample = sample_buffer->ReserveSample();
   sample->Init(isolate, OS::GetCurrentTimeMicros(), state.tid);
-  uword stack_lower = 0;
-  uword stack_upper = 0;
-  isolate->GetStackBounds(&stack_lower, &stack_upper);
-  if ((stack_lower == 0) || (stack_upper == 0)) {
-    stack_lower = 0;
-    stack_upper = 0;
+  sample->set_vm_tag(isolate->vm_tag());
+  sample->set_user_tag(isolate->user_tag());
+  if (FLAG_profile_native_stack) {
+    // Collect native and Dart frames.
+    uword stack_lower = 0;
+    uword stack_upper = 0;
+    isolate->GetStackBounds(&stack_lower, &stack_upper);
+    if ((stack_lower == 0) || (stack_upper == 0)) {
+      stack_lower = 0;
+      stack_upper = 0;
+    }
+    ProfilerNativeStackWalker stackWalker(sample, stack_lower, stack_upper,
+                                          state.pc, state.fp, state.sp);
+    stackWalker.walk(isolate->heap());
+  } else {
+    if (isolate->top_exit_frame_info() != 0) {
+      ProfilerDartStackWalker stackWalker(sample);
+      stackWalker.walk();
+    } else {
+      // TODO(johnmccutchan): Support collecting only Dart frames with
+      // ProfilerNativeStackWalker.
+    }
   }
-  ProfilerSampleStackWalker stackWalker(sample, stack_lower, stack_upper,
-                                        state.pc, state.fp, state.sp);
-  stackWalker.walk(isolate->heap(), isolate->vm_tag());
 }
 
 }  // namespace dart
diff --git a/runtime/vm/profiler.h b/runtime/vm/profiler.h
index 64c60c4..149f382 100644
--- a/runtime/vm/profiler.h
+++ b/runtime/vm/profiler.h
@@ -112,6 +112,7 @@
     tid_ = tid;
     isolate_ = isolate;
     vm_tag_ = VMTag::kInvalidTagId;
+    user_tag_ = UserTags::kNoUserTag;
     for (intptr_t i = 0; i < kSampleFramesSize; i++) {
       pcs_[i] = 0;
     }
@@ -149,11 +150,19 @@
     vm_tag_ = tag;
   }
 
+  uword user_tag() const {
+    return user_tag_;
+  }
+  void set_user_tag(uword tag) {
+    user_tag_ = tag;
+  }
+
  private:
   int64_t timestamp_;
   ThreadId tid_;
   Isolate* isolate_;
   uword vm_tag_;
+  uword user_tag_;
   uword pcs_[kSampleFramesSize];
 };
 
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index acf2554..986163b 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -372,6 +372,7 @@
   code = fn.CurrentCode();
 
   if (fn.HasCode() &&  // The function may not have code.
+      !fn.is_intrinsic() &&  // These may not increment the usage counter.
       !code.is_optimized() &&
       (fn.CurrentCode() == fn.unoptimized_code()) &&
       !code.HasBreakpoint() &&
@@ -835,4 +836,14 @@
   return MirrorReference::InstanceSize();
 }
 
+
+intptr_t RawUserTag::VisitUserTagPointers(
+    RawUserTag* raw_obj, ObjectPointerVisitor* visitor) {
+  // Make sure that we got here with the tagged pointer as this.
+  ASSERT(raw_obj->IsHeapObject());
+  visitor->VisitPointers(raw_obj->from(), raw_obj->to());
+  return UserTag::InstanceSize();
+}
+
+
 }  // namespace dart
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index c63b502..b16d1d5 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -69,6 +69,7 @@
     V(Float32x4)                                                               \
     V(Int32x4)                                                                 \
     V(Float64x2)                                                               \
+    V(UserTag)                                                                 \
 
 #define CLASS_LIST_ARRAYS(V)                                                   \
   V(Array)                                                                     \
@@ -774,6 +775,7 @@
   intptr_t num_imports_;         // Number of entries in imports_.
   intptr_t num_anonymous_;       // Number of entries in anonymous_classes_.
   Dart_NativeEntryResolver native_entry_resolver_;  // Resolves natives.
+  Dart_NativeEntrySymbol native_entry_symbol_resolver_;
   bool corelib_imported_;
   bool debuggable_;              // True if debugger can stop in library.
   int8_t load_state_;            // Of type LibraryState.
@@ -1580,6 +1582,29 @@
 };
 
 
+// UserTag are used by the profiler to track Dart script state.
+class RawUserTag : public RawInstance {
+  RAW_HEAP_OBJECT_IMPLEMENTATION(UserTag);
+
+  RawObject** from() {
+    return reinterpret_cast<RawObject**>(&ptr()->label_);
+  }
+
+  RawString* label_;
+
+  RawObject** to() {
+    return reinterpret_cast<RawObject**>(&ptr()->label_);
+  }
+
+  // Isolate unique tag.
+  uword tag_;
+
+  friend class SnapshotReader;
+
+ public:
+  uword tag() const { return tag_; }
+};
+
 // Class Id predicates.
 
 inline bool RawObject::IsErrorClassId(intptr_t index) {
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index c4f063e..08e0ecb 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -2693,4 +2693,27 @@
   }
 }
 
+
+RawUserTag* UserTag::ReadFrom(SnapshotReader* reader,
+                              intptr_t object_id,
+                              intptr_t tags,
+                              Snapshot::Kind kind) {
+  UNREACHABLE();
+  return UserTag::null();
+}
+
+
+void RawUserTag::WriteTo(SnapshotWriter* writer,
+                         intptr_t object_id,
+                         Snapshot::Kind kind) {
+  if (kind == Snapshot::kMessage) {
+    // We do not allow objects with native fields in an isolate message.
+    writer->SetWriteException(Exceptions::kArgument,
+                              "Illegal argument in isolate message"
+                              " : (object is a UserTag)");
+  } else {
+    UNREACHABLE();
+  }
+}
+
 }  // namespace dart
diff --git a/runtime/vm/runtime_entry.h b/runtime/vm/runtime_entry.h
index 3bb1dde..9f70b8d 100644
--- a/runtime/vm/runtime_entry.h
+++ b/runtime/vm/runtime_entry.h
@@ -9,6 +9,7 @@
 #include "vm/assembler.h"
 #include "vm/flags.h"
 #include "vm/native_arguments.h"
+#include "vm/tags.h"
 
 namespace dart {
 
@@ -28,7 +29,11 @@
         function_(function),
         argument_count_(argument_count),
         is_leaf_(is_leaf),
-        is_float_(is_float) { }
+        is_float_(is_float),
+        next_(NULL) {
+    // Strip off const for registration.
+    VMTag::RegisterRuntimeEntry(const_cast<RuntimeEntry*>(this));
+  }
   ~RuntimeEntry() {}
 
   const char* name() const { return name_; }
@@ -41,12 +46,16 @@
   // Generate code to call the runtime entry.
   void Call(Assembler* assembler, intptr_t argument_count) const;
 
+  void set_next(const RuntimeEntry* next) { next_ = next; }
+  const RuntimeEntry* next() const { return next_; }
+
  private:
   const char* name_;
   const RuntimeFunction function_;
   const intptr_t argument_count_;
   const bool is_leaf_;
   const bool is_float_;
+  const RuntimeEntry* next_;
 
   DISALLOW_COPY_AND_ASSIGN(RuntimeEntry);
 };
diff --git a/runtime/vm/runtime_entry_arm64.cc b/runtime/vm/runtime_entry_arm64.cc
index d771682..6147834 100644
--- a/runtime/vm/runtime_entry_arm64.cc
+++ b/runtime/vm/runtime_entry_arm64.cc
@@ -15,8 +15,39 @@
 
 #define __ assembler->
 
+// Generate code to call into the stub which will call the runtime
+// function. Input for the stub is as follows:
+//   SP : points to the arguments and return value array.
+//   R5 : address of the runtime function to call.
+//   R4 : number of arguments to the call.
 void RuntimeEntry::Call(Assembler* assembler, intptr_t argument_count) const {
-  UNIMPLEMENTED();
+  // Compute the effective address. When running under the simulator,
+  // this is a redirection address that forces the simulator to call
+  // into the runtime system.
+  uword entry = GetEntryPoint();
+#if defined(USING_SIMULATOR)
+  // Redirection to leaf runtime calls supports a maximum of 8 arguments passed
+  // in registers.
+  ASSERT(argument_count >= 0);
+  ASSERT(!is_leaf() || (argument_count <= 8));
+  Simulator::CallKind call_kind =
+      is_leaf() ? (is_float() ? Simulator::kLeafFloatRuntimeCall
+                              : Simulator::kLeafRuntimeCall)
+                : Simulator::kRuntimeCall;
+  entry =
+      Simulator::RedirectExternalReference(entry, call_kind, argument_count);
+#endif
+  if (is_leaf()) {
+    ASSERT(argument_count == this->argument_count());
+    ExternalLabel label(name(), entry);
+    __ BranchLink(&label, PP);
+  } else {
+    // Argument count is not checked here, but in the runtime entry for a more
+    // informative error message.
+    __ LoadImmediate(R5, entry, PP);
+    __ LoadImmediate(R4, argument_count, PP);
+    __ BranchLink(&StubCode::CallToRuntimeLabel(), PP);
+  }
 }
 
 }  // namespace dart
diff --git a/runtime/vm/runtime_entry_test.cc b/runtime/vm/runtime_entry_test.cc
index 6e5720c..b0b04c5 100644
--- a/runtime/vm/runtime_entry_test.cc
+++ b/runtime/vm/runtime_entry_test.cc
@@ -2,10 +2,6 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// TODO(zra): Remove when tests are ready to enable.
-#include "platform/globals.h"
-#if !defined(TARGET_ARCH_ARM64)
-
 #include "vm/runtime_entry.h"
 
 #include "vm/object.h"
@@ -63,5 +59,3 @@
 END_LEAF_RUNTIME_ENTRY
 
 }  // namespace dart
-
-#endif  // !defined(TARGET_ARCH_ARM64)
diff --git a/runtime/vm/scavenger.cc b/runtime/vm/scavenger.cc
index 0c43bec..ec28362 100644
--- a/runtime/vm/scavenger.cc
+++ b/runtime/vm/scavenger.cc
@@ -79,7 +79,6 @@
         visited_count_(0),
         handled_count_(0),
         delayed_weak_stack_(),
-        growth_policy_(PageSpace::kControlGrowth),
         bytes_promoted_(0),
         visiting_old_object_(NULL),
         in_scavenge_pointer_(false) { }
@@ -200,32 +199,15 @@
         //
         // This object is a survivor of a previous scavenge. Attempt to promote
         // the object.
-        new_addr = heap_->TryAllocate(size, Heap::kOld, growth_policy_);
+        new_addr =
+            heap_->TryAllocate(size, Heap::kOld, PageSpace::kForceGrowth);
         if (new_addr != 0) {
           // If promotion succeeded then we need to remember it so that it can
           // be traversed later.
           scavenger_->PushToPromotedStack(new_addr);
           bytes_promoted_ += size;
           class_table->UpdateAllocatedOld(cid, size);
-        } else if (!scavenger_->had_promotion_failure_) {
-          // Signal a promotion failure and set the growth policy for
-          // this, and all subsequent promotion allocations, to force
-          // growth.
-          scavenger_->had_promotion_failure_ = true;
-          growth_policy_ = PageSpace::kForceGrowth;
-          new_addr = heap_->TryAllocate(size, Heap::kOld, growth_policy_);
-          if (new_addr != 0) {
-            scavenger_->PushToPromotedStack(new_addr);
-            bytes_promoted_ += size;
-            class_table->UpdateAllocatedOld(cid, size);
-          } else {
-            // Promotion did not succeed. Copy into the to space
-            // instead.
-            new_addr = scavenger_->TryAllocate(size);
-            class_table->UpdateLiveNew(cid, size);
-          }
         } else {
-          ASSERT(growth_policy_ == PageSpace::kForceGrowth);
           // Promotion did not succeed. Copy into the to space instead.
           new_addr = scavenger_->TryAllocate(size);
           class_table->UpdateLiveNew(cid, size);
@@ -258,7 +240,6 @@
   typedef std::multimap<RawObject*, RawWeakProperty*> DelaySet;
   DelaySet delay_set_;
   GrowableArray<RawObject*> delayed_weak_stack_;
-  PageSpace::GrowthPolicy growth_policy_;
   // TODO(cshapiro): use this value to compute survival statistics for
   // new space growth policy.
   intptr_t bytes_promoted_;
@@ -518,16 +499,26 @@
     while (queue != NULL) {
       WeakReferenceSet* reference_set = WeakReferenceSet::Pop(&queue);
       ASSERT(reference_set != NULL);
+      intptr_t num_keys = reference_set->num_keys();
+      intptr_t num_values = reference_set->num_values();
+      if ((num_keys == 1) && (num_values == 1) &&
+          reference_set->SingletonKeyEqualsValue()) {
+        // We do not have to process sets that have just one key/value pair
+        // and the key and value are identical.
+        continue;
+      }
       bool is_unreachable = true;
       // Test each key object for reachability.  If a key object is
       // reachable, all value objects should be scavenged.
-      for (intptr_t k = 0; k < reference_set->num_keys(); ++k) {
+      for (intptr_t k = 0; k < num_keys; ++k) {
         if (!IsUnreachable(reference_set->get_key(k))) {
-          for (intptr_t v = 0; v < reference_set->num_values(); ++v) {
+          for (intptr_t v = 0; v < num_values; ++v) {
             visitor->VisitPointer(reference_set->get_value(v));
           }
           is_unreachable = false;
-          delete reference_set;
+          // Since we have found a key object that is reachable and all
+          // value objects have been marked we can break out of iterating
+          // this set and move on to the next set.
           break;
         }
       }
@@ -542,17 +533,16 @@
       ProcessToSpace(visitor);
     } else {
       // Break out of the loop if there has been no forward process.
+      // All key objects in the weak reference sets are unreachable
+      // so we reset the weak reference sets queue.
+      state->set_delayed_weak_reference_sets(NULL);
       break;
     }
   }
-  // Deallocate any unreachable references on the delay queue.
-  if (state->delayed_weak_reference_sets() != NULL) {
-    WeakReferenceSet* queue = state->delayed_weak_reference_sets();
-    state->set_delayed_weak_reference_sets(NULL);
-    while (queue != NULL) {
-      delete WeakReferenceSet::Pop(&queue);
-    }
-  }
+  ASSERT(state->delayed_weak_reference_sets() == NULL);
+  // All weak reference sets are zone allocated and unmarked references which
+  // were on the delay queue will be freed when the zone is released in the
+  // epilog callback.
 }
 
 
@@ -701,7 +691,6 @@
   // Scavenging is not reentrant. Make sure that is the case.
   ASSERT(!scavenging_);
   scavenging_ = true;
-  had_promotion_failure_ = false;
   Isolate* isolate = Isolate::Current();
   NoHandleScope no_handles(isolate);
 
diff --git a/runtime/vm/scavenger.h b/runtime/vm/scavenger.h
index 17d2857..f8d81e3 100644
--- a/runtime/vm/scavenger.h
+++ b/runtime/vm/scavenger.h
@@ -98,11 +98,6 @@
     *end = to_->end();
   }
 
-  // Returns true if the last scavenge had a promotion failure.
-  bool HadPromotionFailure() {
-    return had_promotion_failure_;
-  }
-
   void WriteProtect(bool read_only);
 
   void AddGCTime(int64_t micros) {
@@ -209,8 +204,6 @@
 
   // Keep track whether a scavenge is currently running.
   bool scavenging_;
-  // Keep track whether the scavenge had a promotion failure.
-  bool had_promotion_failure_;
 
   int64_t gc_time_micros_;
   intptr_t collections_;
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 5d5c246..11548e5 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -31,6 +31,8 @@
 namespace dart {
 
 DEFINE_FLAG(bool, trace_service, false, "Trace VM service requests.");
+DECLARE_FLAG(bool, enable_type_checks);
+DECLARE_FLAG(bool, enable_asserts);
 
 struct ResourcesEntry {
   const char* path_;
@@ -249,6 +251,81 @@
 }
 
 
+// These must be kept in sync with service/constants.dart
+#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1
+#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2
+
+
+static RawArray* MakeServiceControlMessage(Dart_Port port_id, intptr_t code,
+                                           const String& name) {
+  const Array& list = Array::Handle(Array::New(4));
+  ASSERT(!list.IsNull());
+  const Integer& code_int = Integer::Handle(Integer::New(code));
+  const Integer& port_int = Integer::Handle(Integer::New(port_id));
+  const Object& send_port = Object::Handle(
+      DartLibraryCalls::NewSendPort(port_id));
+  ASSERT(!send_port.IsNull());
+  list.SetAt(0, code_int);
+  list.SetAt(1, port_int);
+  list.SetAt(2, send_port);
+  list.SetAt(3, name);
+  return list.raw();
+}
+
+
+class RegisterRunningIsolatesVisitor : public IsolateVisitor {
+ public:
+  explicit RegisterRunningIsolatesVisitor(Isolate* service_isolate)
+      : IsolateVisitor(),
+        register_function_(Function::Handle(service_isolate)),
+        service_isolate_(service_isolate) {
+    ASSERT(Isolate::Current() == service_isolate_);
+    // Get library.
+    const String& library_url = Symbols::DartVMService();
+    ASSERT(!library_url.IsNull());
+    const Library& library =
+        Library::Handle(Library::LookupLibrary(library_url));
+    ASSERT(!library.IsNull());
+    // Get function.
+    const String& function_name =
+        String::Handle(String::New("_registerIsolate"));
+    ASSERT(!function_name.IsNull());
+    register_function_ = library.LookupFunctionAllowPrivate(function_name);
+    ASSERT(!register_function_.IsNull());
+  }
+
+  virtual void VisitIsolate(Isolate* isolate) {
+    ASSERT(Isolate::Current() == service_isolate_);
+    if ((isolate == service_isolate_) ||
+        (isolate == Dart::vm_isolate())) {
+      // We do not register the service or vm isolate.
+      return;
+    }
+    // Setup arguments for call.
+    intptr_t port_id = isolate->main_port();
+    const Integer& port_int = Integer::Handle(Integer::New(port_id));
+    ASSERT(!port_int.IsNull());
+    const Object& send_port = Object::Handle(
+        DartLibraryCalls::NewSendPort(port_id));
+    ASSERT(!send_port.IsNull());
+    const String& name = String::Handle(String::New(isolate->name()));
+    ASSERT(!name.IsNull());
+    const Array& args = Array::Handle(Array::New(3));
+    ASSERT(!args.IsNull());
+    args.SetAt(0, port_int);
+    args.SetAt(1, send_port);
+    args.SetAt(2, name);
+    Object& r = Object::Handle(service_isolate_);
+    r = DartEntry::InvokeFunction(register_function_, args);
+    ASSERT(!r.IsError());
+  }
+
+ private:
+  Function& register_function_;
+  Isolate* service_isolate_;
+};
+
+
 Isolate* Service::GetServiceIsolate(void* callback_data) {
   if (service_isolate_ != NULL) {
     // Already initialized, return service isolate.
@@ -326,34 +403,19 @@
     ASSERT(port_ != ILLEGAL_PORT);
     Dart_ExitScope();
   }
+  {
+    // Register existing isolates.
+    StackZone zone(isolate);
+    HANDLESCOPE(isolate);
+    RegisterRunningIsolatesVisitor register_isolates(isolate);
+    Isolate::VisitIsolates(&register_isolates);
+  }
   Isolate::SetCurrent(NULL);
   service_isolate_ = reinterpret_cast<Isolate*>(isolate);
   return service_isolate_;
 }
 
 
-// These must be kept in sync with service/constants.dart
-#define VM_SERVICE_ISOLATE_STARTUP_MESSAGE_ID 1
-#define VM_SERVICE_ISOLATE_SHUTDOWN_MESSAGE_ID 2
-
-
-static RawArray* MakeServiceControlMessage(Dart_Port port_id, intptr_t code,
-                                           const String& name) {
-  const Array& list = Array::Handle(Array::New(4));
-  ASSERT(!list.IsNull());
-  const Integer& code_int = Integer::Handle(Integer::New(code));
-  const Integer& port_int = Integer::Handle(Integer::New(port_id));
-  const Object& send_port = Object::Handle(
-      DartLibraryCalls::NewSendPort(port_id));
-  ASSERT(!send_port.IsNull());
-  list.SetAt(0, code_int);
-  list.SetAt(1, port_int);
-  list.SetAt(2, send_port);
-  list.SetAt(3, name);
-  return list.raw();
-}
-
-
 bool Service::SendIsolateStartupMessage() {
   if (!IsRunning()) {
     return false;
@@ -1250,6 +1312,7 @@
 static bool HandleScriptsEnumerate(Isolate* isolate, JSONStream* js) {
   JSONObject jsobj(js);
   jsobj.AddProperty("type", "ScriptList");
+  jsobj.AddProperty("id", "scripts");
   JSONArray members(&jsobj, "members");
   const GrowableObjectArray& libs =
       GrowableObjectArray::Handle(isolate->object_store()->libraries());
@@ -1768,7 +1831,8 @@
   jsobj.AddProperty("id", "vm");
   jsobj.AddProperty("architecture", CPU::Id());
   jsobj.AddProperty("version", Version::String());
-
+  jsobj.AddProperty("assertsEnabled", FLAG_enable_asserts);
+  jsobj.AddProperty("typeChecksEnabled", FLAG_enable_type_checks);
   int64_t start_time_micros = Dart::vm_isolate()->start_time();
   int64_t uptime_micros = (OS::GetCurrentTimeMicros() - start_time_micros);
   double seconds = (static_cast<double>(uptime_micros) /
diff --git a/runtime/vm/service/vmservice.dart b/runtime/vm/service/vmservice.dart
index 886268d..35fdb84 100644
--- a/runtime/vm/service/vmservice.dart
+++ b/runtime/vm/service/vmservice.dart
@@ -94,3 +94,8 @@
   // Return the port we expect isolate startup and shutdown messages on.
   return new VMService().receivePort;
 }
+
+void _registerIsolate(int port_id, SendPort sp, String name) {
+  var service = new VMService();
+  service.runningIsolates.isolateStartup(port_id, sp, name);
+}
diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc
index 5a462cf..b29af7f 100644
--- a/runtime/vm/simulator_arm64.cc
+++ b/runtime/vm/simulator_arm64.cc
@@ -35,6 +35,358 @@
 #define SScanF sscanf  // NOLINT
 
 
+// SimulatorSetjmpBuffer are linked together, and the last created one
+// is referenced by the Simulator. When an exception is thrown, the exception
+// runtime looks at where to jump and finds the corresponding
+// SimulatorSetjmpBuffer based on the stack pointer of the exception handler.
+// The runtime then does a Longjmp on that buffer to return to the simulator.
+class SimulatorSetjmpBuffer {
+ public:
+  int Setjmp() { return setjmp(buffer_); }
+  void Longjmp() {
+    // "This" is now the last setjmp buffer.
+    simulator_->set_last_setjmp_buffer(this);
+    longjmp(buffer_, 1);
+  }
+
+  explicit SimulatorSetjmpBuffer(Simulator* sim) {
+    simulator_ = sim;
+    link_ = sim->last_setjmp_buffer();
+    sim->set_last_setjmp_buffer(this);
+    sp_ = static_cast<uword>(sim->get_register(R31, R31IsSP));
+    native_sp_ = reinterpret_cast<uword>(&sim);  // Current C++ stack pointer.
+  }
+
+  ~SimulatorSetjmpBuffer() {
+    ASSERT(simulator_->last_setjmp_buffer() == this);
+    simulator_->set_last_setjmp_buffer(link_);
+  }
+
+  SimulatorSetjmpBuffer* link() { return link_; }
+
+  uword sp() { return sp_; }
+  uword native_sp() { return native_sp_; }
+
+ private:
+  uword sp_;
+  uword native_sp_;
+  Simulator* simulator_;
+  SimulatorSetjmpBuffer* link_;
+  jmp_buf buffer_;
+
+  friend class Simulator;
+};
+
+
+// The SimulatorDebugger class is used by the simulator while debugging
+// simulated ARM64 code.
+class SimulatorDebugger {
+ public:
+  explicit SimulatorDebugger(Simulator* sim);
+  ~SimulatorDebugger();
+
+  void Stop(Instr* instr, const char* message);
+  void Debug();
+  char* ReadLine(const char* prompt);
+
+ private:
+  Simulator* sim_;
+
+  bool GetValue(char* desc, int64_t* value);
+  // TODO(zra): GetVValue for doubles.
+  // TODO(zra): Breakpoints.
+};
+
+
+SimulatorDebugger::SimulatorDebugger(Simulator* sim) {
+  sim_ = sim;
+}
+
+
+SimulatorDebugger::~SimulatorDebugger() {
+}
+
+void SimulatorDebugger::Stop(Instr* instr, const char* message) {
+  OS::Print("Simulator hit %s\n", message);
+  Debug();
+}
+
+
+static Register LookupCpuRegisterByName(const char* name) {
+  static const char* kNames[] = {
+      "r0",  "r1",  "r2",  "r3",
+      "r4",  "r5",  "r6",  "r7",
+      "r8",  "r9",  "r10", "r11",
+      "r12", "r13", "r14", "r15",
+      "r16", "r17", "r18", "r19",
+      "r20", "r21", "r22", "r23",
+      "r24", "r25", "r26", "r27",
+      "r28", "r29", "r30",
+
+      "ip0", "ip1", "pp",  "ctx", "fp",  "lr",  "sp", "zr",
+  };
+  static const Register kRegisters[] = {
+      R0,  R1,  R2,  R3,  R4,  R5,  R6,  R7,
+      R8,  R9,  R10, R11, R12, R13, R14, R15,
+      R16, R17, R18, R19, R20, R21, R22, R23,
+      R24, R25, R26, R27, R28, R29, R30,
+
+      IP0, IP1, PP, CTX, FP, LR, SP, ZR,
+  };
+  ASSERT(ARRAY_SIZE(kNames) == ARRAY_SIZE(kRegisters));
+  for (unsigned i = 0; i < ARRAY_SIZE(kNames); i++) {
+    if (strcmp(kNames[i], name) == 0) {
+      return kRegisters[i];
+    }
+  }
+  return kNoRegister;
+}
+
+
+bool SimulatorDebugger::GetValue(char* desc, int64_t* value) {
+  Register reg = LookupCpuRegisterByName(desc);
+  if (reg != kNoRegister) {
+    *value = sim_->get_register(reg);
+    return true;
+  }
+  if (desc[0] == '*') {
+    int64_t addr;
+    if (GetValue(desc + 1, &addr)) {
+      if (Simulator::IsIllegalAddress(addr)) {
+        return false;
+      }
+      *value = *(reinterpret_cast<int64_t*>(addr));
+      return true;
+    }
+  }
+  if (strcmp("pc", desc) == 0) {
+    *value = sim_->get_pc();
+    return true;
+  }
+  bool retval = SScanF(desc, "0x%"Px64, value) == 1;
+  if (!retval) {
+    retval = SScanF(desc, "%"Px64, value) == 1;
+  }
+  return retval;
+}
+
+
+void SimulatorDebugger::Debug() {
+  intptr_t last_pc = -1;
+  bool done = false;
+
+#define COMMAND_SIZE 63
+#define ARG_SIZE 255
+
+#define STR(a) #a
+#define XSTR(a) STR(a)
+
+  char cmd[COMMAND_SIZE + 1];
+  char arg1[ARG_SIZE + 1];
+  char arg2[ARG_SIZE + 1];
+
+  // make sure to have a proper terminating character if reaching the limit
+  cmd[COMMAND_SIZE] = 0;
+  arg1[ARG_SIZE] = 0;
+  arg2[ARG_SIZE] = 0;
+
+  // TODO(zra): Undo all set breakpoints while running in the debugger shell.
+  // This will make them invisible to all commands.
+  // UndoBreakpoints();
+
+  while (!done) {
+    if (last_pc != sim_->get_pc()) {
+      last_pc = sim_->get_pc();
+      if (Simulator::IsIllegalAddress(last_pc)) {
+        OS::Print("pc is out of bounds: 0x%" Px "\n", last_pc);
+      } else {
+        Disassembler::Disassemble(last_pc, last_pc + Instr::kInstrSize);
+      }
+    }
+    char* line = ReadLine("sim> ");
+    if (line == NULL) {
+      FATAL("ReadLine failed");
+    } else {
+      // Use sscanf to parse the individual parts of the command line. At the
+      // moment no command expects more than two parameters.
+      int args = SScanF(line,
+                        "%" XSTR(COMMAND_SIZE) "s "
+                        "%" XSTR(ARG_SIZE) "s "
+                        "%" XSTR(ARG_SIZE) "s",
+                        cmd, arg1, arg2);
+      if ((strcmp(cmd, "h") == 0) || (strcmp(cmd, "help") == 0)) {
+        OS::Print("c/cont -- continue execution\n"
+                  "disasm -- disassemble instrs at current pc location\n"
+                  "  other variants are:\n"
+                  "    disasm <address>\n"
+                  "    disasm <address> <number_of_instructions>\n"
+                  "  by default 10 instrs are disassembled\n"
+                  "gdb -- transfer control to gdb\n"
+                  "h/help -- print this help string\n"
+                  "p/print <reg or value or *addr> -- print integer value\n"
+                  "po/printobject <*reg or *addr> -- print object\n"
+                  "si/stepi -- single step an instruction\n"
+                  "q/quit -- Quit the debugger and exit the program\n");
+      } else if ((strcmp(cmd, "quit") == 0) || (strcmp(cmd, "q") == 0)) {
+        OS::Print("Quitting\n");
+        OS::Exit(0);
+      } else if ((strcmp(cmd, "si") == 0) || (strcmp(cmd, "stepi") == 0)) {
+        sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc()));
+      } else if ((strcmp(cmd, "c") == 0) || (strcmp(cmd, "cont") == 0)) {
+        // Execute the one instruction we broke at with breakpoints disabled.
+        sim_->InstructionDecode(reinterpret_cast<Instr*>(sim_->get_pc()));
+        // Leave the debugger shell.
+        done = true;
+      } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
+        if (args == 2) {
+          int64_t value;
+          if (GetValue(arg1, &value)) {
+            OS::Print("%s: %"Pu64" 0x%"Px64"\n", arg1, value, value);
+          } else {
+            OS::Print("%s unrecognized\n", arg1);
+          }
+        } else {
+          OS::Print("print <reg or value or *addr>\n");
+        }
+      } else if ((strcmp(cmd, "po") == 0) ||
+                 (strcmp(cmd, "printobject") == 0)) {
+        if (args == 2) {
+          int64_t value;
+          // Make the dereferencing '*' optional.
+          if (((arg1[0] == '*') && GetValue(arg1 + 1, &value)) ||
+              GetValue(arg1, &value)) {
+            if (Isolate::Current()->heap()->Contains(value)) {
+              OS::Print("%s: \n", arg1);
+#if defined(DEBUG)
+              const Object& obj = Object::Handle(
+                  reinterpret_cast<RawObject*>(value));
+              obj.Print();
+#endif  // defined(DEBUG)
+            } else {
+              OS::Print("0x%"Px64" is not an object reference\n", value);
+            }
+          } else {
+            OS::Print("%s unrecognized\n", arg1);
+          }
+        } else {
+          OS::Print("printobject <*reg or *addr>\n");
+        }
+      } else if (strcmp(cmd, "disasm") == 0) {
+        int64_t start = 0;
+        int64_t end = 0;
+        if (args == 1) {
+          start = sim_->get_pc();
+          end = start + (10 * Instr::kInstrSize);
+        } else if (args == 2) {
+          if (GetValue(arg1, &start)) {
+            // no length parameter passed, assume 10 instructions
+            if (Simulator::IsIllegalAddress(start)) {
+              // If start isn't a valid address, warn and use PC instead
+              OS::Print("First argument yields invalid address: 0x%"Px64"\n",
+                        start);
+              OS::Print("Using PC instead");
+              start = sim_->get_pc();
+            }
+            end = start + (10 * Instr::kInstrSize);
+          }
+        } else {
+          int64_t length;
+          if (GetValue(arg1, &start) && GetValue(arg2, &length)) {
+            if (Simulator::IsIllegalAddress(start)) {
+              // If start isn't a valid address, warn and use PC instead
+              OS::Print("First argument yields invalid address: 0x%"Px64"\n",
+                        start);
+              OS::Print("Using PC instead\n");
+              start = sim_->get_pc();
+            }
+            end = start + (length * Instr::kInstrSize);
+          }
+        }
+        Disassembler::Disassemble(start, end);
+      } else if (strcmp(cmd, "gdb") == 0) {
+        OS::Print("relinquishing control to gdb\n");
+        OS::DebugBreak();
+        OS::Print("regaining control from gdb\n");
+      } else {
+        OS::Print("Unknown command: %s\n", cmd);
+      }
+    }
+    delete[] line;
+  }
+
+  // TODO(zra): Add all the breakpoints back to stop execution and enter the
+  // debugger shell when hit.
+  // RedoBreakpoints();
+
+#undef COMMAND_SIZE
+#undef ARG_SIZE
+
+#undef STR
+#undef XSTR
+}
+
+
+char* SimulatorDebugger::ReadLine(const char* prompt) {
+  char* result = NULL;
+  char line_buf[256];
+  intptr_t offset = 0;
+  bool keep_going = true;
+  OS::Print("%s", prompt);
+  while (keep_going) {
+    if (fgets(line_buf, sizeof(line_buf), stdin) == NULL) {
+      // fgets got an error. Just give up.
+      if (result != NULL) {
+        delete[] result;
+      }
+      return NULL;
+    }
+    intptr_t len = strlen(line_buf);
+    if (len > 1 &&
+        line_buf[len - 2] == '\\' &&
+        line_buf[len - 1] == '\n') {
+      // When we read a line that ends with a "\" we remove the escape and
+      // append the remainder.
+      line_buf[len - 2] = '\n';
+      line_buf[len - 1] = 0;
+      len -= 1;
+    } else if ((len > 0) && (line_buf[len - 1] == '\n')) {
+      // Since we read a new line we are done reading the line. This
+      // will exit the loop after copying this buffer into the result.
+      keep_going = false;
+    }
+    if (result == NULL) {
+      // Allocate the initial result and make room for the terminating '\0'
+      result = new char[len + 1];
+      if (result == NULL) {
+        // OOM, so cannot readline anymore.
+        return NULL;
+      }
+    } else {
+      // Allocate a new result with enough room for the new addition.
+      intptr_t new_len = offset + len + 1;
+      char* new_result = new char[new_len];
+      if (new_result == NULL) {
+        // OOM, free the buffer allocated so far and return NULL.
+        delete[] result;
+        return NULL;
+      } else {
+        // Copy the existing input into the new array and set the new
+        // array as the result.
+        memmove(new_result, result, offset);
+        delete[] result;
+        result = new_result;
+      }
+    }
+    // Copy the newly read line into the result.
+    memmove(result + offset, line_buf, len);
+    offset += len;
+  }
+  ASSERT(result != NULL);
+  result[offset] = '\0';
+  return result;
+}
+
+
 Simulator::Simulator() {
   // Setup simulator support first. Some of this information is needed to
   // setup the architecture state.
@@ -80,6 +432,76 @@
 }
 
 
+// When the generated code calls an external reference we need to catch that in
+// the simulator.  The external reference will be a function compiled for the
+// host architecture.  We need to call that function instead of trying to
+// execute it with the simulator.  We do that by redirecting the external
+// reference to a svc (supervisor call) instruction that is handled by
+// the simulator.  We write the original destination of the jump just at a known
+// offset from the svc instruction so the simulator knows what to call.
+class Redirection {
+ public:
+  uword address_of_hlt_instruction() {
+    return reinterpret_cast<uword>(&hlt_instruction_);
+  }
+
+  uword external_function() const { return external_function_; }
+
+  Simulator::CallKind call_kind() const { return call_kind_; }
+
+  int argument_count() const { return argument_count_; }
+
+  static Redirection* Get(uword external_function,
+                          Simulator::CallKind call_kind,
+                          int argument_count) {
+    Redirection* current;
+    for (current = list_; current != NULL; current = current->next_) {
+      if (current->external_function_ == external_function) return current;
+    }
+    return new Redirection(external_function, call_kind, argument_count);
+  }
+
+  static Redirection* FromHltInstruction(Instr* hlt_instruction) {
+    char* addr_of_hlt = reinterpret_cast<char*>(hlt_instruction);
+    char* addr_of_redirection =
+        addr_of_hlt - OFFSET_OF(Redirection, hlt_instruction_);
+    return reinterpret_cast<Redirection*>(addr_of_redirection);
+  }
+
+ private:
+  static const int32_t kRedirectInstruction = Instr::kRedirectInstruction;
+  Redirection(uword external_function,
+              Simulator::CallKind call_kind,
+              int argument_count)
+      : external_function_(external_function),
+        call_kind_(call_kind),
+        argument_count_(argument_count),
+        hlt_instruction_(kRedirectInstruction),
+        next_(list_) {
+    list_ = this;
+  }
+
+  uword external_function_;
+  Simulator::CallKind call_kind_;
+  int argument_count_;
+  uint32_t hlt_instruction_;
+  Redirection* next_;
+  static Redirection* list_;
+};
+
+
+Redirection* Redirection::list_ = NULL;
+
+
+uword Simulator::RedirectExternalReference(uword function,
+                                           CallKind call_kind,
+                                           int argument_count) {
+  Redirection* redirection =
+      Redirection::Get(function, call_kind, argument_count);
+  return redirection->address_of_hlt_instruction();
+}
+
+
 // Get the active Simulator for the current isolate.
 Simulator* Simulator::Current() {
   Simulator* simulator = Isolate::Current()->simulator();
@@ -95,7 +517,6 @@
 void Simulator::set_register(Register reg, int64_t value, R31Type r31t) {
   // register is in range, and if it is R31, a mode is specified.
   ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
-  ASSERT((reg != R31) || (r31t != R31IsUndef));
   if ((reg != R31) || (r31t != R31IsZR)) {
     registers_[reg] = value;
   }
@@ -105,7 +526,6 @@
 // Get the register from the architecture state.
 int64_t Simulator::get_register(Register reg, R31Type r31t) const {
   ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
-  ASSERT((reg != R31) || (r31t != R31IsUndef));
   if ((reg == R31) && (r31t == R31IsZR)) {
     return 0;
   } else {
@@ -116,7 +536,6 @@
 
 void Simulator::set_wregister(Register reg, int32_t value, R31Type r31t) {
   ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
-  ASSERT((reg != R31) || (r31t != R31IsUndef));
   // When setting in W mode, clear the high bits.
   if ((reg != R31) || (r31t != R31IsZR)) {
     registers_[reg] = Utils::LowHighTo64Bits(static_cast<uint32_t>(value), 0);
@@ -127,7 +546,6 @@
 // Get the register from the architecture state.
 int32_t Simulator::get_wregister(Register reg, R31Type r31t) const {
   ASSERT((reg >= 0) && (reg < kNumberOfCpuRegisters));
-  ASSERT((reg != R31) || (r31t != R31IsUndef));
   if ((reg == R31) && (r31t == R31IsZR)) {
     return 0;
   } else {
@@ -139,23 +557,32 @@
 // Raw access to the PC register.
 void Simulator::set_pc(int64_t value) {
   pc_modified_ = true;
+  last_pc_ = pc_;
   pc_ = value;
 }
 
 
-// Raw access to the PC register without the special adjustment when reading.
+// Raw access to the pc.
 int64_t Simulator::get_pc() const {
   return pc_;
 }
 
 
+int64_t Simulator::get_last_pc() const {
+  return last_pc_;
+}
+
+
 void Simulator::HandleIllegalAccess(uword addr, Instr* instr) {
   uword fault_pc = get_pc();
+  uword last_pc = get_last_pc();
   // TODO(zra): drop into debugger.
   char buffer[128];
   snprintf(buffer, sizeof(buffer),
-           "illegal memory access at 0x%" Px ", pc=0x%" Px "\n",
-           addr, fault_pc);
+      "illegal memory access at 0x%" Px ", pc=0x%" Px ", last_pc=0x%" Px"\n",
+      addr, fault_pc, last_pc);
+  SimulatorDebugger dbg(this);
+  dbg.Stop(instr, buffer);
   // The debugger will return control in non-interactive mode.
   FATAL("Cannot continue execution after illegal memory access.");
 }
@@ -168,7 +595,8 @@
   char buffer[64];
   snprintf(buffer, sizeof(buffer),
            "unaligned %s at 0x%" Px ", pc=%p\n", msg, addr, instr);
-  // TODO(zra): Drop into the simulator debugger when it exists.
+  SimulatorDebugger dbg(this);
+  dbg.Stop(instr, buffer);
   // The debugger will not be able to single step past this instruction, but
   // it will be possible to disassemble the code and inspect registers.
   FATAL("Cannot continue execution after unaligned access.");
@@ -178,7 +606,8 @@
 void Simulator::UnimplementedInstruction(Instr* instr) {
   char buffer[64];
   snprintf(buffer, sizeof(buffer), "Unimplemented instruction: pc=%p\n", instr);
-  // TODO(zra): drop into debugger.
+  SimulatorDebugger dbg(this);
+  dbg.Stop(instr, buffer);
   FATAL("Cannot continue execution after unimplemented instruction.");
 }
 
@@ -528,6 +957,22 @@
 }
 
 
+void Simulator::DecodePCRel(Instr* instr) {
+  const int op = instr->Bit(31);
+  if (op == 0) {
+    // Format(instr, "adr 'rd, 'pcrel")
+    const Register rd = instr->RdField();
+    const int64_t immhi = instr->SImm19Field();
+    const int64_t immlo = instr->Bits(29, 2);
+    const int64_t off = (immhi << 2) | immlo;
+    const int64_t dest = get_pc() + off;
+    set_register(rd, dest, instr->RdMode());
+  } else {
+    UnimplementedInstruction(instr);
+  }
+}
+
+
 void Simulator::DecodeDPImmediate(Instr* instr) {
   if (instr->IsMoveWideOp()) {
     DecodeMoveWide(instr);
@@ -535,14 +980,217 @@
     DecodeAddSubImm(instr);
   } else if (instr->IsLogicalImmOp()) {
     DecodeLogicalImm(instr);
+  } else if (instr->IsPCRelOp()) {
+    DecodePCRel(instr);
   } else {
     UnimplementedInstruction(instr);
   }
 }
 
 
+void Simulator::DecodeCompareAndBranch(Instr* instr) {
+  const int op = instr->Bit(24);
+  const Register rt = instr->RtField();
+  const int64_t imm19 = instr->SImm19Field();
+  const int64_t dest = get_pc() + (imm19 << 2);
+  const int64_t mask = instr->SFField() == 1 ? kXRegMask : kWRegMask;
+  const int64_t rt_val = get_register(rt, R31IsZR) & mask;
+  if (op == 0) {
+    // Format(instr, "cbz'sf 'rt, 'dest19");
+    if (rt_val == 0) {
+      set_pc(dest);
+    }
+  } else {
+    // Format(instr, "cbnz'sf 'rt, 'dest19");
+    if (rt_val != 0) {
+      set_pc(dest);
+    }
+  }
+}
+
+
+bool Simulator::ConditionallyExecute(Instr* instr) {
+  switch (instr->ConditionField()) {
+    case EQ: return z_flag_;
+    case NE: return !z_flag_;
+    case CS: return c_flag_;
+    case CC: return !c_flag_;
+    case MI: return n_flag_;
+    case PL: return !n_flag_;
+    case VS: return v_flag_;
+    case VC: return !v_flag_;
+    case HI: return c_flag_ && !z_flag_;
+    case LS: return !c_flag_ || z_flag_;
+    case GE: return n_flag_ == v_flag_;
+    case LT: return n_flag_ != v_flag_;
+    case GT: return !z_flag_ && (n_flag_ == v_flag_);
+    case LE: return z_flag_ || (n_flag_ != v_flag_);
+    case AL: return true;
+    default: UNREACHABLE();
+  }
+  return false;
+}
+
+
+void Simulator::DecodeConditionalBranch(Instr* instr) {
+  // Format(instr, "b'cond 'dest19");
+  if ((instr->Bit(24) != 0) || (instr->Bit(4) != 0)) {
+    UnimplementedInstruction(instr);
+  }
+  const int64_t imm19 = instr->SImm19Field();
+  const int64_t dest = get_pc() + (imm19 << 2);
+  if (ConditionallyExecute(instr)) {
+    set_pc(dest);
+  }
+}
+
+
+// Calls into the Dart runtime are based on this interface.
+typedef void (*SimulatorRuntimeCall)(NativeArguments arguments);
+
+// Calls to leaf Dart runtime functions are based on this interface.
+typedef int32_t (*SimulatorLeafRuntimeCall)(
+    int64_t r0, int64_t r1, int64_t r2, int64_t r3,
+    int64_t r4, int64_t r5, int64_t r6, int64_t r7);
+
+// Calls to leaf float Dart runtime functions are based on this interface.
+typedef double (*SimulatorLeafFloatRuntimeCall)(
+    double d0, double d1, double d2, double d3,
+    double d4, double d5, double d6, double d7);
+
+// Calls to native Dart functions are based on this interface.
+typedef void (*SimulatorBootstrapNativeCall)(NativeArguments* arguments);
+typedef void (*SimulatorNativeCall)(NativeArguments* arguments, uword target);
+
+
+void Simulator::DoRedirectedCall(Instr* instr) {
+  SimulatorSetjmpBuffer buffer(this);
+  if (!setjmp(buffer.buffer_)) {
+    int64_t saved_lr = get_register(LR);
+    Redirection* redirection = Redirection::FromHltInstruction(instr);
+    uword external = redirection->external_function();
+    if (FLAG_trace_sim) {
+      OS::Print("Call to host function at 0x%" Pd "\n", external);
+    }
+
+    if ((redirection->call_kind() == kRuntimeCall) ||
+        (redirection->call_kind() == kBootstrapNativeCall) ||
+        (redirection->call_kind() == kNativeCall)) {
+      // Set the top_exit_frame_info of this simulator to the native stack.
+      set_top_exit_frame_info(reinterpret_cast<uword>(&buffer));
+    }
+    if (redirection->call_kind() == kRuntimeCall) {
+      NativeArguments arguments;
+      ASSERT(sizeof(NativeArguments) == 4*kWordSize);
+      arguments.isolate_ = reinterpret_cast<Isolate*>(get_register(R0));
+      arguments.argc_tag_ = get_register(R1);
+      arguments.argv_ = reinterpret_cast<RawObject*(*)[]>(get_register(R2));
+      arguments.retval_ = reinterpret_cast<RawObject**>(get_register(R3));
+      SimulatorRuntimeCall target =
+          reinterpret_cast<SimulatorRuntimeCall>(external);
+      target(arguments);
+      set_register(R0, icount_);  // Zap result register from void function.
+      set_register(R1, icount_);
+    } else if (redirection->call_kind() == kLeafRuntimeCall) {
+      ASSERT((0 <= redirection->argument_count()) &&
+             (redirection->argument_count() <= 8));
+      int64_t r0 = get_register(R0);
+      int64_t r1 = get_register(R1);
+      int64_t r2 = get_register(R2);
+      int64_t r3 = get_register(R3);
+      int64_t r4 = get_register(R4);
+      int64_t r5 = get_register(R5);
+      int64_t r6 = get_register(R6);
+      int64_t r7 = get_register(R7);
+      SimulatorLeafRuntimeCall target =
+          reinterpret_cast<SimulatorLeafRuntimeCall>(external);
+      r0 = target(r0, r1, r2, r3, r4, r5, r6, r7);
+      set_register(R0, r0);  // Set returned result from function.
+      set_register(R1, icount_);  // Zap unused result register.
+    } else if (redirection->call_kind() == kLeafFloatRuntimeCall) {
+      // TODO(zra): leaf float runtime calls.
+      UNIMPLEMENTED();
+    } else if (redirection->call_kind() == kBootstrapNativeCall) {
+      NativeArguments* arguments;
+      arguments = reinterpret_cast<NativeArguments*>(get_register(R0));
+      SimulatorBootstrapNativeCall target =
+          reinterpret_cast<SimulatorBootstrapNativeCall>(external);
+      target(arguments);
+      set_register(R0, icount_);  // Zap result register from void function.
+    } else {
+      ASSERT(redirection->call_kind() == kNativeCall);
+      NativeArguments* arguments;
+      arguments = reinterpret_cast<NativeArguments*>(get_register(R0));
+      uword target_func = get_register(R1);
+      SimulatorNativeCall target =
+          reinterpret_cast<SimulatorNativeCall>(external);
+      target(arguments, target_func);
+      set_register(R0, icount_);  // Zap result register from void function.
+      set_register(R1, icount_);
+    }
+    set_top_exit_frame_info(0);
+
+    // Zap caller-saved registers, since the actual runtime call could have
+    // used them.
+    set_register(R2, icount_);
+    set_register(R3, icount_);
+    set_register(R4, icount_);
+    set_register(R5, icount_);
+    set_register(R6, icount_);
+    set_register(R7, icount_);
+    set_register(R8, icount_);
+    set_register(R9, icount_);
+    set_register(R10, icount_);
+    set_register(R11, icount_);
+    set_register(R12, icount_);
+    set_register(R13, icount_);
+    set_register(R14, icount_);
+    set_register(R15, icount_);
+    set_register(IP0, icount_);
+    set_register(IP1, icount_);
+    set_register(R18, icount_);
+    set_register(LR, icount_);
+
+    // TODO(zra): Zap caller-saved fpu registers.
+
+    // Return.
+    set_pc(saved_lr);
+  } else {
+    // Coming via long jump from a throw. Continue to exception handler.
+    set_top_exit_frame_info(0);
+  }
+}
+
+
 void Simulator::DecodeExceptionGen(Instr* instr) {
-  UnimplementedInstruction(instr);
+  if ((instr->Bits(0, 2) == 1) && (instr->Bits(2, 3) == 0) &&
+      (instr->Bits(21, 3) == 0)) {
+    // Format(instr, "svc 'imm16");
+    UnimplementedInstruction(instr);
+  } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) &&
+             (instr->Bits(21, 3) == 1)) {
+    // Format(instr, "brk 'imm16");
+    UnimplementedInstruction(instr);
+  } else if ((instr->Bits(0, 2) == 0) && (instr->Bits(2, 3) == 0) &&
+             (instr->Bits(21, 3) == 2)) {
+    // Format(instr, "hlt 'imm16");
+    uint16_t imm = static_cast<uint16_t>(instr->Imm16Field());
+    if (imm == kImmExceptionIsDebug) {
+      SimulatorDebugger dbg(this);
+      const char* message = *reinterpret_cast<const char**>(
+          reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize);
+      set_pc(get_pc() + Instr::kInstrSize);
+      dbg.Stop(instr, message);
+    } else if (imm == kImmExceptionIsPrintf) {
+      const char* message = *reinterpret_cast<const char**>(
+          reinterpret_cast<intptr_t>(instr) - 2 * Instr::kInstrSize);
+      OS::Print("Simulator hit: %s", message);
+    } else if (imm == kImmExceptionIsRedirectedCall) {
+      DoRedirectedCall(instr);
+    } else {
+      UnimplementedInstruction(instr);
+    }
+  }
 }
 
 
@@ -561,10 +1209,59 @@
 }
 
 
+void Simulator::DecodeTestAndBranch(Instr* instr) {
+  const int op = instr->Bit(24);
+  const int bitpos = instr->Bits(19, 4) | (instr->Bit(31) << 5);
+  const int64_t imm14 = instr->SImm14Field();
+  const int64_t dest = get_pc() + (imm14 << 2);
+  const Register rt = instr->RtField();
+  const int64_t rt_val = get_register(rt, R31IsZR);
+  if (op == 0) {
+    // Format(instr, "tbz'sf 'rt, 'bitpos, 'dest14");
+    if ((rt_val & (1 << bitpos)) == 0) {
+      set_pc(dest);
+    }
+  } else {
+    // Format(instr, "tbnz'sf 'rt, 'bitpos, 'dest14");
+    if ((rt_val & (1 << bitpos)) != 0) {
+      set_pc(dest);
+    }
+  }
+}
+
+
+void Simulator::DecodeUnconditionalBranch(Instr* instr) {
+  const bool link = instr->Bit(31) == 1;
+  const int64_t imm26 = instr->SImm26Field();
+  const int64_t dest = get_pc() + (imm26 << 2);
+  const int64_t ret = get_pc() + Instr::kInstrSize;
+  set_pc(dest);
+  if (link) {
+    set_register(LR, ret);
+  }
+}
+
+
 void Simulator::DecodeUnconditionalBranchReg(Instr* instr) {
   if ((instr->Bits(0, 5) == 0) && (instr->Bits(10, 6) == 0) &&
       (instr->Bits(16, 5) == 0x1f)) {
     switch (instr->Bits(21, 4)) {
+      case 0: {
+        // Format(instr, "br 'rn");
+        const Register rn = instr->RnField();
+        const int64_t dest = get_register(rn, instr->RnMode());
+        set_pc(dest);
+        break;
+      }
+      case 1: {
+        // Format(instr, "blr 'rn");
+        const Register rn = instr->RnField();
+        const int64_t dest = get_register(rn, instr->RnMode());
+        const int64_t ret = get_pc() + Instr::kInstrSize;
+        set_pc(dest);
+        set_register(LR, ret);
+        break;
+      }
       case 2: {
         // Format(instr, "ret 'rn");
         const Register rn = instr->RnField();
@@ -583,10 +1280,18 @@
 
 
 void Simulator::DecodeCompareBranch(Instr* instr) {
-  if (instr->IsExceptionGenOp()) {
+  if (instr->IsCompareAndBranchOp()) {
+    DecodeCompareAndBranch(instr);
+  } else if (instr->IsConditionalBranchOp()) {
+    DecodeConditionalBranch(instr);
+  } else if (instr->IsExceptionGenOp()) {
     DecodeExceptionGen(instr);
   } else if (instr->IsSystemOp()) {
     DecodeSystem(instr);
+  } else if (instr->IsTestAndBranchOp()) {
+    DecodeTestAndBranch(instr);
+  } else if (instr->IsUnconditionalBranchOp()) {
+    DecodeUnconditionalBranch(instr);
   } else if (instr->IsUnconditionalBranchRegOp()) {
     DecodeUnconditionalBranchReg(instr);
   } else {
@@ -616,6 +1321,12 @@
     const uint32_t imm12 = static_cast<uint32_t>(instr->Imm12Field());
     const uint32_t offset = imm12 << size;
     address = rn_val + offset;
+  }  else if (instr->Bits(10, 2) == 0) {
+    // addr = rn + signed 9-bit immediate offset.
+    wb = false;
+    const int64_t offset = static_cast<int64_t>(instr->SImm9Field());
+    address = rn_val + offset;
+    wb_address = rn_val;
   } else if (instr->Bit(10) == 1) {
     // addr = rn + signed 9-bit immediate offset.
     wb = true;
@@ -740,9 +1451,32 @@
 }
 
 
+void Simulator::DecodeLoadRegLiteral(Instr* instr) {
+  if ((instr->Bit(31) != 0) || (instr->Bit(29) != 0) ||
+      (instr->Bits(24, 3) != 0)) {
+    UnimplementedInstruction(instr);
+  }
+
+  const Register rt = instr->RtField();
+  const int64_t off = instr->SImm19Field() << 2;
+  const int64_t pc = reinterpret_cast<int64_t>(instr);
+  const int64_t address = pc + off;
+  const int64_t val = ReadX(address, instr);
+  if (instr->Bit(30)) {
+    // Format(instr, "ldrx 'rt, 'pcldr");
+    set_register(rt, val, R31IsZR);
+  } else {
+    // Format(instr, "ldrw 'rt, 'pcldr");
+    set_wregister(rt, static_cast<int32_t>(val), R31IsZR);
+  }
+}
+
+
 void Simulator::DecodeLoadStore(Instr* instr) {
   if (instr->IsLoadStoreRegOp()) {
     DecodeLoadStoreReg(instr);
+  } else if (instr->IsLoadRegLiteralOp()) {
+    DecodeLoadRegLiteral(instr);
   } else {
     UnimplementedInstruction(instr);
   }
@@ -811,6 +1545,7 @@
       break;
     default:
       UNREACHABLE();
+      break;
   }
   int64_t mask = (reg_size == kXRegSizeInBits) ? kXRegMask : kWRegMask;
   return (value << amount) & mask;
@@ -837,39 +1572,43 @@
 
 
 void Simulator::DecodeAddSubShiftExt(Instr* instr) {
-  switch (instr->Bit(30)) {
-    case 0: {
-      // Format(instr, "add'sf's 'rd, 'rn, 'shift_op");
-      const Register rd = instr->RdField();
-      const Register rn = instr->RnField();
-      const int64_t rm_val = DecodeShiftExtendOperand(instr);
-      if (instr->SFField()) {
-        // 64-bit add.
-        const int64_t rn_val = get_register(rn, instr->RnMode());
-        const int64_t alu_out = rn_val + rm_val;
-        set_register(rd, alu_out, instr->RdMode());
-        if (instr->HasS()) {
-          SetNZFlagsX(alu_out);
-          SetCFlag(CarryFromX(rn_val, rm_val));
-          SetVFlag(OverflowFromX(alu_out, rn_val, rm_val, true));
-        }
-      } else {
-        // 32-bit add.
-        const int32_t rn_val = get_wregister(rn, instr->RnMode());
-        const int32_t rm_val32 = static_cast<int32_t>(rm_val & kWRegMask);
-        const int32_t alu_out = rn_val + rm_val32;
-        set_wregister(rd, alu_out, instr->RdMode());
-        if (instr->HasS()) {
-          SetNZFlagsW(alu_out);
-          SetCFlag(CarryFromW(rn_val, rm_val32));
-          SetVFlag(OverflowFromW(alu_out, rn_val, rm_val32, true));
-        }
-      }
-      break;
+  // Format(instr, "add'sf's 'rd, 'rn, 'shift_op");
+  // also, sub, cmp, etc.
+  const bool subtract = instr->Bit(30) == 1;
+  const Register rd = instr->RdField();
+  const Register rn = instr->RnField();
+  const int64_t rm_val = DecodeShiftExtendOperand(instr);
+  if (instr->SFField()) {
+    // 64-bit add.
+    const int64_t rn_val = get_register(rn, instr->RnMode());
+    int64_t alu_out = 0;
+    if (subtract) {
+      alu_out = rn_val - rm_val;
+    } else {
+      alu_out = rn_val + rm_val;
     }
-    default:
-      UnimplementedInstruction(instr);
-      break;
+    set_register(rd, alu_out, instr->RdMode());
+    if (instr->HasS()) {
+      SetNZFlagsX(alu_out);
+      SetCFlag(CarryFromX(rn_val, rm_val));
+      SetVFlag(OverflowFromX(alu_out, rn_val, rm_val, !subtract));
+    }
+  } else {
+    // 32-bit add.
+    const int32_t rn_val = get_wregister(rn, instr->RnMode());
+    const int32_t rm_val32 = static_cast<int32_t>(rm_val & kWRegMask);
+    int32_t alu_out = 0;
+    if (subtract) {
+      alu_out = rn_val - rm_val32;
+    } else {
+      alu_out = rn_val + rm_val32;
+    }
+    set_wregister(rd, alu_out, instr->RdMode());
+    if (instr->HasS()) {
+      SetNZFlagsW(alu_out);
+      SetCFlag(CarryFromW(rn_val, rm_val32));
+      SetVFlag(OverflowFromW(alu_out, rn_val, rm_val32, !subtract));
+    }
   }
 }
 
@@ -938,11 +1677,156 @@
 }
 
 
+static int64_t divide64(int64_t top, int64_t bottom, bool signd) {
+  // ARM64 does not trap on integer division by zero. The destination register
+  // is instead set to 0.
+  if (bottom == 0) {
+    return 0;
+  }
+
+  if (signd) {
+    // INT_MIN / -1 = INT_MIN.
+    if ((top == static_cast<int64_t>(0x8000000000000000LL)) &&
+        (bottom == static_cast<int64_t>(0xffffffffffffffffLL))) {
+      return static_cast<int64_t>(0x8000000000000000LL);
+    } else {
+      return top / bottom;
+    }
+  } else {
+    const uint64_t utop = static_cast<uint64_t>(top);
+    const uint64_t ubottom = static_cast<uint64_t>(bottom);
+    return static_cast<int64_t>(utop / ubottom);
+  }
+}
+
+
+static int32_t divide32(int32_t top, int32_t bottom, bool signd) {
+  // ARM64 does not trap on integer division by zero. The destination register
+  // is instead set to 0.
+  if (bottom == 0) {
+    return 0;
+  }
+
+  if (signd) {
+    // INT_MIN / -1 = INT_MIN.
+    if ((top == static_cast<int32_t>(0x80000000)) &&
+        (bottom == static_cast<int32_t>(0xffffffff))) {
+      return static_cast<int32_t>(0x80000000);
+    } else {
+      return top / bottom;
+    }
+  } else {
+    const uint32_t utop = static_cast<uint32_t>(top);
+    const uint32_t ubottom = static_cast<uint32_t>(bottom);
+    return static_cast<int32_t>(utop / ubottom);
+  }
+}
+
+
+void Simulator::DecodeMiscDP2Source(Instr* instr) {
+  if (instr->Bit(29) != 0) {
+    UnimplementedInstruction(instr);
+  }
+
+  const Register rd = instr->RdField();
+  const Register rn = instr->RnField();
+  const Register rm = instr->RmField();
+  const int op = instr->Bits(10, 6);
+  const int64_t rn_val64 = get_register(rn, R31IsZR);
+  const int64_t rm_val64 = get_register(rm, R31IsZR);
+  const int32_t rn_val32 = get_wregister(rn, R31IsZR);
+  const int32_t rm_val32 = get_wregister(rm, R31IsZR);
+  switch (op) {
+    case 2:
+    case 3: {
+      // Format(instr, "udiv'sf 'rd, 'rn, 'rm");
+      // Format(instr, "sdiv'sf 'rd, 'rn, 'rm");
+      const bool signd = instr->Bit(10) == 1;
+      if (instr->SFField() == 1) {
+        set_register(rd, divide64(rn_val64, rm_val64, signd), R31IsZR);
+      } else {
+        set_wregister(rd, divide32(rn_val32, rm_val32, signd), R31IsZR);
+      }
+      break;
+    }
+    case 8: {
+      // Format(instr, "lsl'sf 'rd, 'rn, 'rm");
+      if (instr->SFField() == 1) {
+        const int64_t alu_out = rn_val64 << (rm_val64 & (kXRegSizeInBits - 1));
+        set_register(rd, alu_out, R31IsZR);
+      } else {
+        const int32_t alu_out = rn_val32 << (rm_val32 & (kXRegSizeInBits - 1));
+        set_wregister(rd, alu_out, R31IsZR);
+      }
+      break;
+    }
+    case 9: {
+      // Format(instr, "lsr'sf 'rd, 'rn, 'rm");
+      if (instr->SFField() == 1) {
+        const uint64_t rn_u64 = static_cast<uint64_t>(rn_val64);
+        const int64_t alu_out = rn_u64 >> (rm_val64 & (kXRegSizeInBits - 1));
+        set_register(rd, alu_out, R31IsZR);
+      } else {
+        const uint32_t rn_u32 = static_cast<uint32_t>(rn_val32);
+        const int32_t alu_out = rn_u32 >> (rm_val32 & (kXRegSizeInBits - 1));
+        set_wregister(rd, alu_out, R31IsZR);
+      }
+      break;
+    }
+    case 10: {
+      // Format(instr, "asr'sf 'rd, 'rn, 'rm");
+      if (instr->SFField() == 1) {
+        const int64_t alu_out = rn_val64 >> (rm_val64 & (kXRegSizeInBits - 1));
+        set_register(rd, alu_out, R31IsZR);
+      } else {
+        const int32_t alu_out = rn_val32 >> (rm_val32 & (kXRegSizeInBits - 1));
+        set_wregister(rd, alu_out, R31IsZR);
+      }
+      break;
+    }
+    default:
+      UnimplementedInstruction(instr);
+      break;
+  }
+}
+
+
+void Simulator::DecodeMiscDP3Source(Instr* instr) {
+  if ((instr->Bits(29, 2) == 0) && (instr->Bits(21, 3) == 0) &&
+      (instr->Bit(15) == 0)) {
+    // Format(instr, "madd'sf 'rd, 'rn, 'rm, 'ra");
+    const Register rd = instr->RdField();
+    const Register rn = instr->RnField();
+    const Register rm = instr->RmField();
+    const Register ra = instr->RaField();
+    if (instr->SFField() == 1) {
+      const int64_t rn_val = get_register(rn, R31IsZR);
+      const int64_t rm_val = get_register(rm, R31IsZR);
+      const int64_t ra_val = get_register(ra, R31IsZR);
+      const int64_t alu_out = ra_val + (rn_val * rm_val);
+      set_register(rd, alu_out, R31IsZR);
+    } else {
+      const int32_t rn_val = get_wregister(rn, R31IsZR);
+      const int32_t rm_val = get_wregister(rm, R31IsZR);
+      const int32_t ra_val = get_wregister(ra, R31IsZR);
+      const int32_t alu_out = ra_val + (rn_val * rm_val);
+      set_wregister(rd, alu_out, R31IsZR);
+    }
+  } else {
+    UnimplementedInstruction(instr);
+  }
+}
+
+
 void Simulator::DecodeDPRegister(Instr* instr) {
   if (instr->IsAddSubShiftExtOp()) {
     DecodeAddSubShiftExt(instr);
   } else if (instr->IsLogicalShiftOp()) {
     DecodeLogicalShift(instr);
+  } else if (instr->IsMiscDP2SourceOp()) {
+    DecodeMiscDP2Source(instr);
+  } else if (instr->IsMiscDP3SourceOp()) {
+    DecodeMiscDP3Source(instr);
   } else {
     UnimplementedInstruction(instr);
   }
@@ -1113,7 +1997,7 @@
   set_register(R28, r28_val);
   set_register(R29, r29_val);
 
-  // Restore the SP register and return R1:R0.
+  // Restore the SP register and return R0.
   set_register(R31, sp_before_call, R31IsSP);
   int64_t return_value;
   return_value = get_register(R0);
diff --git a/runtime/vm/simulator_arm64.h b/runtime/vm/simulator_arm64.h
index 6c32c1b..1f204f2 100644
--- a/runtime/vm/simulator_arm64.h
+++ b/runtime/vm/simulator_arm64.h
@@ -22,6 +22,7 @@
 namespace dart {
 
 class Isolate;
+class SimulatorSetjmpBuffer;
 
 class Simulator {
  public:
@@ -35,12 +36,18 @@
   static Simulator* Current();
 
   // Accessors for register state.
-  void set_register(Register reg, int64_t value, R31Type r31t = R31IsUndef);
-  int64_t get_register(Register reg, R31Type r31t = R31IsUndef) const;
-  void set_wregister(Register reg, int32_t value, R31Type r31t = R31IsUndef);
-  int32_t get_wregister(Register reg, R31Type r31t = R31IsUndef) const;
+  // The default value for R31Type has to be R31IsSP because get_register is
+  // accessed from architecture independent code through SPREG without
+  // specifying the type. We also can't translate a dummy value for SPREG into
+  // a real value because the architecture independent code expects SPREG to
+  // be a real register value.
+  void set_register(Register reg, int64_t value, R31Type r31t = R31IsSP);
+  int64_t get_register(Register reg, R31Type r31t = R31IsSP) const;
+  void set_wregister(Register reg, int32_t value, R31Type r31t = R31IsSP);
+  int32_t get_wregister(Register reg, R31Type r31t = R31IsSP) const;
 
   int64_t get_pc() const;
+  int64_t get_last_pc() const;
   void set_pc(int64_t pc);
 
   // Accessor to the internal simulator stack top.
@@ -65,6 +72,18 @@
                int64_t parameter2,
                int64_t parameter3);
 
+  // Runtime and native call support.
+  enum CallKind {
+    kRuntimeCall,
+    kLeafRuntimeCall,
+    kLeafFloatRuntimeCall,
+    kBootstrapNativeCall,
+    kNativeCall
+  };
+  static uword RedirectExternalReference(uword function,
+                                         CallKind call_kind,
+                                         int argument_count);
+
   void Longjmp(uword pc,
                uword sp,
                uword fp,
@@ -91,11 +110,13 @@
   bool v_flag_;
 
   // Simulator support.
+  int64_t last_pc_;
   int64_t pc_;
   char* stack_;
   bool pc_modified_;
   intptr_t icount_;
   static int64_t flag_stop_sim_at_;
+  SimulatorSetjmpBuffer* last_setjmp_buffer_;
   uword top_exit_frame_info_;
 
   // Registered breakpoints.
@@ -160,6 +181,10 @@
 
   int64_t DecodeShiftExtendOperand(Instr* instr);
 
+  bool ConditionallyExecute(Instr* instr);
+
+  void DoRedirectedCall(Instr* instr);
+
   // Decode instructions.
   void InstructionDecode(Instr* instr);
   #define DECODE_OP(op)                                                        \
@@ -170,6 +195,16 @@
   // Executes ARM64 instructions until the PC reaches kEndSimulatingPC.
   void Execute();
 
+  // Longjmp support for exceptions.
+  SimulatorSetjmpBuffer* last_setjmp_buffer() {
+    return last_setjmp_buffer_;
+  }
+  void set_last_setjmp_buffer(SimulatorSetjmpBuffer* buffer) {
+    last_setjmp_buffer_ = buffer;
+  }
+
+  friend class SimulatorDebugger;
+  friend class SimulatorSetjmpBuffer;
   DISALLOW_COPY_AND_ASSIGN(Simulator);
 };
 
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 9fef5bf..4f0a74f 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -32,7 +32,7 @@
 static bool IsObjectStoreClassId(intptr_t class_id) {
   // Check if this is a class which is stored in the object store.
   return (class_id == kObjectCid ||
-          (class_id >= kInstanceCid && class_id <= kFloat64x2Cid) ||
+          (class_id >= kInstanceCid && class_id <= kUserTagCid) ||
           class_id == kArrayCid ||
           class_id == kImmutableArrayCid ||
           RawObject::IsStringClassId(class_id) ||
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 327cbeb..22c96cc 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -554,6 +554,7 @@
   friend class RawTypeArguments;
   friend class RawMirrorReference;
   friend class SnapshotWriterVisitor;
+  friend class RawUserTag;
   DISALLOW_COPY_AND_ASSIGN(SnapshotWriter);
 };
 
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index 01e14b2..7fb5ab0 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -2,9 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-// TODO(zra): Remove when tests are ready to enable.
 #include "platform/globals.h"
-#if !defined(TARGET_ARCH_ARM64)
 
 #include "include/dart_debugger_api.h"
 #include "platform/assert.h"
@@ -836,6 +834,9 @@
 };
 
 
+// TODO(zra): Remove when tests are ready to enable.
+#if !defined(TARGET_ARCH_ARM64)
+
 static void GenerateSourceAndCheck(const Script& script) {
   // Check if we are able to generate the source from the token stream.
   // Rescan this source and compare the token stream to see if they are
@@ -2718,6 +2719,6 @@
   Dart_ExitScope();
 }
 
-}  // namespace dart
-
 #endif  // !defined(TARGET_ARCH_ARM64)
+
+}  // namespace dart
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index 582d4fa..3915563 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -26,8 +26,30 @@
 }
 
 
-void StackFrame::Print() const {
-  OS::Print("[%-8s : sp(%#" Px ") ]\n", GetName(), sp());
+const char* StackFrame::ToCString() const {
+  Zone* zone = Isolate::Current()->current_zone();
+  if (IsDartFrame()) {
+    const Code& code = Code::Handle(LookupDartCode());
+    ASSERT(!code.IsNull());
+    const Object& owner = Object::Handle(code.owner());
+    ASSERT(!owner.IsNull());
+    if (owner.IsFunction()) {
+      const Function& function = Function::Cast(owner);
+      return zone->PrintToString(
+          "[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px ") %s ]",
+          GetName(), sp(), fp(), pc(),
+          function.ToFullyQualifiedCString());
+    } else {
+      return zone->PrintToString(
+          "[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px ") %s ]",
+          GetName(), sp(), fp(), pc(),
+          owner.ToCString());
+    }
+  } else {
+    return zone->PrintToString(
+        "[%-8s : sp(%#" Px ") fp(%#" Px ") pc(%#" Px ")]",
+        GetName(), sp(), fp(), pc());
+  }
 }
 
 
diff --git a/runtime/vm/stack_frame.h b/runtime/vm/stack_frame.h
index 2727eee..5b4d6b4 100644
--- a/runtime/vm/stack_frame.h
+++ b/runtime/vm/stack_frame.h
@@ -56,8 +56,7 @@
   // Visit objects in the frame.
   virtual void VisitObjectPointers(ObjectPointerVisitor* visitor);
 
-  // Print a frame.
-  virtual void Print() const;
+  const char* ToCString() const;
 
   // Check validity of a frame, used for assertion purposes.
   virtual bool IsValid() const;
@@ -241,7 +240,9 @@
  public:
   DartFrameIterator() : frames_(StackFrameIterator::kDontValidateFrames) { }
   explicit DartFrameIterator(uword last_fp)
-      : frames_(last_fp, StackFrameIterator::kDontValidateFrames) {}
+      : frames_(last_fp, StackFrameIterator::kDontValidateFrames) { }
+  DartFrameIterator(uword fp, uword sp, uword pc)
+      : frames_(fp, sp, pc, StackFrameIterator::kDontValidateFrames) { }
   // Get next dart frame.
   StackFrame* NextFrame() {
     StackFrame* frame = frames_.NextFrame();
diff --git a/runtime/vm/stack_frame_arm64.h b/runtime/vm/stack_frame_arm64.h
index 49c7d29..b518746 100644
--- a/runtime/vm/stack_frame_arm64.h
+++ b/runtime/vm/stack_frame_arm64.h
@@ -7,27 +7,44 @@
 
 namespace dart {
 
-// TODO(zra):
-// These are the values for ARM. Fill in the values for ARM64 as they are
-// needed.
+/* ARM64 Dart Frame Layout
+               |                    | <- TOS
+Callee frame   | ...                |
+               | saved PP           |
+               | callee's PC marker |
+               | saved FP           |    (FP of current frame)
+               | saved PC           |    (PC of current frame)
+               +--------------------+
+Current frame  | ...               T| <- SP of current frame
+               | first local       T|
+               | caller's PP       T|
+               | PC marker          |    (current frame's code entry + offset)
+               | caller's FP        | <- FP of current frame
+               | caller's LR        |    (PC of caller frame)
+               +--------------------+
+Caller frame   | last parameter     | <- SP of caller frame
+               |  ...               |
+
+               T against a slot indicates it needs to be traversed during GC.
+*/
 
 static const int kDartFrameFixedSize = 4;  // PP, FP, LR, PC marker.
-static const int kSavedPcSlotFromSp = -2;
+static const int kSavedPcSlotFromSp = -1;
 
-static const int kFirstObjectSlotFromFp = -1;  // Used by GC to traverse stack.
+static const int kFirstObjectSlotFromFp = -2;  // Used by GC to traverse stack.
 
-static const int kFirstLocalSlotFromFp = -2;
-static const int kSavedCallerPpSlotFromFp = -1;
+static const int kFirstLocalSlotFromFp = -3;
+static const int kSavedCallerPpSlotFromFp = -2;
 static const int kSavedCallerFpSlotFromFp = 0;
 static const int kSavedCallerPcSlotFromFp = 1;
-static const int kPcMarkerSlotFromFp = 2;
-static const int kParamEndSlotFromFp = 2;  // One slot past last parameter.
-static const int kCallerSpSlotFromFp = 3;
+static const int kPcMarkerSlotFromFp = -1;
+static const int kParamEndSlotFromFp = 1;  // One slot past last parameter.
+static const int kCallerSpSlotFromFp = 2;
 
 // Entry and exit frame layout.
-static const int kSavedContextSlotFromEntryFp = -27;
-static const int kExitLinkSlotFromEntryFp = -26;
-static const int kSavedVMTagSlotFromEntryFp = -25;
+static const int kSavedContextSlotFromEntryFp = -14;
+static const int kExitLinkSlotFromEntryFp = -13;
+static const int kSavedVMTagSlotFromEntryFp = -12;
 
 }  // namespace dart
 
diff --git a/runtime/vm/stub_code.cc b/runtime/vm/stub_code.cc
index fd297de..3047ab5 100644
--- a/runtime/vm/stub_code.cc
+++ b/runtime/vm/stub_code.cc
@@ -51,12 +51,9 @@
 
 
 void StubCode::InitOnce() {
-  // TODO(zra): ifndef to be removed when ARM64 port is ready.
-#if !defined(TARGET_ARCH_ARM64)
   // Generate all the stubs.
   Code& code = Code::Handle();
   VM_STUB_CODE_LIST(STUB_CODE_GENERATE);
-#endif
 }
 
 
@@ -70,12 +67,9 @@
 
 
 void StubCode::Init(Isolate* isolate) {
-  // TODO(zra): ifndef to be removed when ARM64 port is ready.
-#if !defined(TARGET_ARCH_ARM64)
   StubCode* stubs = new StubCode();
   isolate->set_stub_code(stubs);
   stubs->GenerateFor(isolate);
-#endif
 }
 
 
diff --git a/runtime/vm/stub_code.h b/runtime/vm/stub_code.h
index 19f41a2..17acb6d 100644
--- a/runtime/vm/stub_code.h
+++ b/runtime/vm/stub_code.h
@@ -28,7 +28,6 @@
   V(AllocateArray)                                                             \
   V(CallNoSuchMethodFunction)                                                  \
   V(CallStaticFunction)                                                        \
-  V(CallClosureFunction)                                                       \
   V(FixCallersTarget)                                                          \
   V(Deoptimize)                                                                \
   V(DeoptimizeLazy)                                                            \
diff --git a/runtime/vm/stub_code_arm.cc b/runtime/vm/stub_code_arm.cc
index ae9785e..25d1270 100644
--- a/runtime/vm/stub_code_arm.cc
+++ b/runtime/vm/stub_code_arm.cc
@@ -70,8 +70,7 @@
 #endif
 
   // Mark that the isolate is executing VM code.
-  __ LoadImmediate(R6, VMTag::kVMTagId);
-  __ StoreToOffset(kWord, R6, CTX, Isolate::vm_tag_offset());
+  __ StoreToOffset(kWord, R5, CTX, Isolate::vm_tag_offset());
 
   // Reserve space for arguments and align frame before entering C++ world.
   // NativeArguments are passed in registers.
@@ -185,8 +184,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ LoadImmediate(R6, VMTag::kRuntimeNativeTagId);
-  __ StoreToOffset(kWord, R6, CTX, Isolate::vm_tag_offset());
+  __ StoreToOffset(kWord, R5, CTX, Isolate::vm_tag_offset());
 
   // Reserve space for the native arguments structure passed on the stack (the
   // outgoing pointer parameter to the native arguments structure is passed in
@@ -307,8 +305,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ LoadImmediate(R6, VMTag::kRuntimeNativeTagId);
-  __ StoreToOffset(kWord, R6, CTX, Isolate::vm_tag_offset());
+  __ StoreToOffset(kWord, R5, CTX, Isolate::vm_tag_offset());
 
   // Reserve space for the native arguments structure passed on the stack (the
   // outgoing pointer parameter to the native arguments structure is passed in
@@ -729,83 +726,6 @@
 }
 
 
-// Input parameters:
-//   LR: return address.
-//   SP: address of last argument.
-//   R4: arguments descriptor array.
-// Note: The closure object is the first argument to the function being
-//       called, the stub accesses the closure from this location directly
-//       when trying to resolve the call.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
-  // Load num_args.
-  __ ldr(R0, FieldAddress(R4, ArgumentsDescriptor::count_offset()));
-  __ sub(R0, R0, ShifterOperand(Smi::RawValue(1)));
-  // Load closure object in R1.
-  __ ldr(R1, Address(SP, R0, LSL, 1));  // R0 (num_args - 1) is a Smi.
-
-  // Verify that R1 is a closure by checking its class.
-  Label not_closure;
-  __ LoadImmediate(R8, reinterpret_cast<intptr_t>(Object::null()));
-  __ cmp(R1, ShifterOperand(R8));
-  // Not a closure, but null object.
-  __ b(&not_closure, EQ);
-  __ tst(R1, ShifterOperand(kSmiTagMask));
-  __ b(&not_closure, EQ);  // Not a closure, but a smi.
-  // Verify that the class of the object is a closure class by checking that
-  // class.signature_function() is not null.
-  __ LoadClass(R0, R1, R2);
-  __ ldr(R0, FieldAddress(R0, Class::signature_function_offset()));
-  __ cmp(R0, ShifterOperand(R8));  // R8 is raw null.
-  // Actual class is not a closure class.
-  __ b(&not_closure, EQ);
-
-  // R0 is just the signature function. Load the actual closure function.
-  __ ldr(R0, FieldAddress(R1, Closure::function_offset()));
-
-  // Load closure context in CTX; note that CTX has already been preserved.
-  __ ldr(CTX, FieldAddress(R1, Closure::context_offset()));
-
-  // R4: Arguments descriptor.
-  // R0: Function.
-  __ ldr(R2, FieldAddress(R0, Function::code_offset()));
-
-  // R2: code.
-  // R5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
-  __ LoadImmediate(R5, 0);
-  __ ldr(R2, FieldAddress(R2, Code::instructions_offset()));
-  __ AddImmediate(R2, Instructions::HeaderSize() - kHeapObjectTag);
-  __ bx(R2);
-
-  __ Bind(&not_closure);
-  // Call runtime to attempt to resolve and invoke a call method on a
-  // non-closure object, passing the non-closure object and its arguments array,
-  // returning here.
-  // If no call method exists, throw a NoSuchMethodError.
-  // R1: non-closure object.
-  // R4: arguments descriptor array.
-
-  // Create a stub frame as we are pushing some objects on the stack before
-  // calling into the runtime.
-  __ EnterStubFrame();
-
-  // Setup space on stack for result from error reporting.
-  __ PushList((1 << R4) | (1 << R8));  // Arguments descriptor and raw null.
-
-  // Load smi-tagged arguments array length, including the non-closure.
-  __ ldr(R2, FieldAddress(R4, ArgumentsDescriptor::count_offset()));
-  PushArgumentsArray(assembler);
-
-  __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
-  // Remove arguments.
-  __ Drop(2);
-  __ Pop(R0);  // Get result into R0.
-
-  // Remove the stub frame as we are about to return.
-  __ LeaveStubFrame();
-  __ Ret();
-}
-
-
 // Called when invoking Dart code from C++ (VM code).
 // Input parameters:
 //   LR : points to return address.
diff --git a/runtime/vm/stub_code_arm64.cc b/runtime/vm/stub_code_arm64.cc
index 168e5ea..7d601c4 100644
--- a/runtime/vm/stub_code_arm64.cc
+++ b/runtime/vm/stub_code_arm64.cc
@@ -21,95 +21,333 @@
 
 namespace dart {
 
+// Input parameters:
+//   LR : return address.
+//   SP : address of last argument in argument array.
+//   SP + 8*R4 - 8 : address of first argument in argument array.
+//   SP + 8*R4 : address of return value.
+//   R5 : address of the runtime function to call.
+//   R4 : number of arguments to the call.
 void StubCode::GenerateCallToRuntimeStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  const intptr_t isolate_offset = NativeArguments::isolate_offset();
+  const intptr_t argc_tag_offset = NativeArguments::argc_tag_offset();
+  const intptr_t argv_offset = NativeArguments::argv_offset();
+  const intptr_t retval_offset = NativeArguments::retval_offset();
+  const intptr_t exitframe_last_param_slot_from_fp = 1;
+
+  __ SetPrologueOffset();
+  __ Comment("CallToRuntimeStub");
+  __ EnterFrame(0);
+
+  // Load current Isolate pointer from Context structure into A0.
+  __ LoadFieldFromOffset(R0, CTX, Context::isolate_offset());
+
+  // Save exit frame information to enable stack walking as we are about
+  // to transition to Dart VM C++ code.
+  __ StoreToOffset(SP, R0, Isolate::top_exit_frame_info_offset());
+
+  // Save current Context pointer into Isolate structure.
+  __ StoreToOffset(CTX, R0, Isolate::top_context_offset());
+
+  // Cache Isolate pointer into CTX while executing runtime code.
+  __ mov(CTX, R0);
+
+#if defined(DEBUG)
+  { Label ok;
+    // Check that we are always entering from Dart code.
+    __ LoadFromOffset(R8, R0, Isolate::vm_tag_offset());
+    __ CompareImmediate(R8, VMTag::kScriptTagId, kNoRegister);
+    __ b(&ok, EQ);
+    __ Stop("Not coming from Dart code.");
+    __ Bind(&ok);
+  }
+#endif
+
+  // Mark that the isolate is executing VM code.
+  __ StoreToOffset(R5, R0, Isolate::vm_tag_offset());
+
+  // Reserve space for arguments and align frame before entering C++ world.
+  // NativeArguments are passed in registers.
+  __ Comment("align stack");
+  ASSERT(sizeof(NativeArguments) == 4 * kWordSize);
+  __ ReserveAlignedFrameSpace(4 * kWordSize);  // Reserve space for arguments.
+
+  // Pass NativeArguments structure by value and call runtime.
+  // Registers R0, R1, R2, and R3 are used.
+
+  ASSERT(isolate_offset == 0 * kWordSize);
+  // Set isolate in NativeArgs: R0 already contains CTX.
+
+  // There are no runtime calls to closures, so we do not need to set the tag
+  // bits kClosureFunctionBit and kInstanceFunctionBit in argc_tag_.
+  ASSERT(argc_tag_offset == 1 * kWordSize);
+  __ mov(R1, R4);  // Set argc in NativeArguments.
+
+  ASSERT(argv_offset == 2 * kWordSize);
+  __ add(R2, ZR, Operand(R4, LSL, 3));
+  __ add(R2, FP, Operand(R2));  // Compute argv.
+  // Set argv in NativeArguments.
+  __ AddImmediate(R2, R2, exitframe_last_param_slot_from_fp * kWordSize,
+                  kNoRegister);
+
+    ASSERT(retval_offset == 3 * kWordSize);
+  __ AddImmediate(R3, R2, kWordSize, kNoRegister);
+
+  // TODO(zra): Check that the ABI allows calling through this register.
+  __ blr(R5);
+
+  // Retval is next to 1st argument.
+  __ Comment("CallToRuntimeStub return");
+
+  // Mark that the isolate is executing Dart code.
+  __ LoadImmediate(R2, VMTag::kScriptTagId, kNoRegister);
+  __ StoreToOffset(R2, CTX, Isolate::vm_tag_offset());
+
+  // Reset exit frame information in Isolate structure.
+  __ StoreToOffset(ZR, CTX, Isolate::top_exit_frame_info_offset());
+
+  // Load Context pointer from Isolate structure into A2.
+  __ LoadFromOffset(R2, CTX, Isolate::top_context_offset());
+
+  // Load null.
+  __ LoadObject(TMP, Object::null_object(), PP);
+
+  // Reset Context pointer in Isolate structure.
+  __ StoreToOffset(TMP, CTX, Isolate::top_context_offset());
+
+  // Cache Context pointer into CTX while executing Dart code.
+  __ mov(CTX, R2);
+
+  __ LeaveFrame();
+  __ ret();
 }
 
 
 void StubCode::GeneratePrintStopMessageStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GeneratePrintStopMessageStub");
 }
 
 
 void StubCode::GenerateCallNativeCFunctionStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateCallNativeCFunctionStub");
 }
 
 
 void StubCode::GenerateCallBootstrapCFunctionStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateCallBootstrapCFunctionStub");
 }
 
 
 void StubCode::GenerateCallStaticFunctionStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateCallStaticFunctionStub");
 }
 
 
 void StubCode::GenerateFixCallersTargetStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateFixCallersTargetStub");
 }
 
 
 void StubCode::GenerateDeoptimizeLazyStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateDeoptimizeLazyStub");
 }
 
 
 void StubCode::GenerateDeoptimizeStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateDeoptimizeStub");
 }
 
 
 void StubCode::GenerateMegamorphicMissStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateMegamorphicMissStub");
 }
 
 
 void StubCode::GenerateAllocateArrayStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateAllocateArrayStub");
 }
 
 
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
-  UNIMPLEMENTED();
-}
-
-
+// Called when invoking Dart code from C++ (VM code).
+// Input parameters:
+//   LR : points to return address.
+//   R0 : entrypoint of the Dart function to call.
+//   R1 : arguments descriptor array.
+//   R2 : arguments array.
+//   R3 : new context containing the current isolate pointer.
 void StubCode::GenerateInvokeDartCodeStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Comment("InvokeDartCodeStub");
+  __ EnterFrame(0);
+
+  // The new context, saved vm tag, the top exit frame, and the old context.
+  // const intptr_t kPreservedContextSlots = 4;
+  const intptr_t kNewContextOffsetFromFp =
+      -(1 + kAbiPreservedCpuRegCount) * kWordSize;
+  // const intptr_t kPreservedRegSpace =
+  //     kWordSize * (kAbiPreservedCpuRegCount + kPreservedContextSlots);
+
+  // Save the callee-saved registers.
+  for (int i = R19; i <= R28; i++) {
+    const Register r = static_cast<Register>(i);
+    // We use str instead of the Push macro because we will be pushing the PP
+    // register when it is not holding a pool-pointer since we are coming from
+    // C++ code.
+    __ str(r, Address(SP, -1 * kWordSize, Address::PreIndex));
+  }
+
+  // TODO(zra): Save the bottom 64-bits of callee-saved floating point
+  // registers.
+
+  // Push new context.
+  __ Push(R3);
+
+  // We now load the pool pointer(PP) as we are about to invoke dart code and we
+  // could potentially invoke some intrinsic functions which need the PP to be
+  // set up.
+  __ LoadPoolPointer(PP);
+
+  // The new Context structure contains a pointer to the current Isolate
+  // structure. Cache the Context pointer in the CTX register so that it is
+  // available in generated code and calls to Isolate::Current() need not be
+  // done. The assumption is that this register will never be clobbered by
+  // compiled or runtime stub code.
+
+  // Cache the new Context pointer into CTX while executing Dart code.
+  __ LoadFromOffset(CTX, R3, VMHandles::kOffsetOfRawPtrInHandle);
+
+  // Load Isolate pointer from Context structure into temporary register R4.
+  __ LoadFieldFromOffset(R5, CTX, Context::isolate_offset());
+
+  // Save the current VMTag on the stack.
+  ASSERT(kSavedVMTagSlotFromEntryFp == -12);
+  __ LoadFromOffset(R4, R5, Isolate::vm_tag_offset());
+  __ Push(R4);
+
+  // Mark that the isolate is executing Dart code.
+  __ LoadImmediate(R6, VMTag::kScriptTagId, PP);
+  __ StoreToOffset(R6, R5, Isolate::vm_tag_offset());
+
+  // Save the top exit frame info. Use R6 as a temporary register.
+  // StackFrameIterator reads the top exit frame info saved in this frame.
+  __ LoadFromOffset(R6, R5, Isolate::top_exit_frame_info_offset());
+  __ StoreToOffset(ZR, R5, Isolate::top_exit_frame_info_offset());
+
+  // Save the old Context pointer. Use R4 as a temporary register.
+  // Note that VisitObjectPointers will find this saved Context pointer during
+  // GC marking, since it traverses any information between SP and
+  // FP - kExitLinkSlotFromEntryFp.
+  // EntryFrame::SavedContext reads the context saved in this frame.
+  __ LoadFromOffset(R4, R5, Isolate::top_context_offset());
+
+  // The constants kSavedContextSlotFromEntryFp and
+  // kExitLinkSlotFromEntryFp must be kept in sync with the code below.
+  ASSERT(kExitLinkSlotFromEntryFp == -13);
+  ASSERT(kSavedContextSlotFromEntryFp == -14);
+  __ Push(R6);
+  __ Push(R4);
+
+  // Load arguments descriptor array into R4, which is passed to Dart code.
+  __ LoadFromOffset(R4, R1, VMHandles::kOffsetOfRawPtrInHandle);
+
+  // Load number of arguments into S5.
+  __ LoadFieldFromOffset(R5, R4, ArgumentsDescriptor::count_offset());
+  __ SmiUntag(R5);
+
+  // Compute address of 'arguments array' data area into R2.
+  __ LoadFromOffset(R2, R2, VMHandles::kOffsetOfRawPtrInHandle);
+  __ AddImmediate(R2, R2, Array::data_offset() - kHeapObjectTag, PP);
+
+  // Set up arguments for the Dart call.
+  Label push_arguments;
+  Label done_push_arguments;
+  __ cmp(R5, Operand(0));
+  __ b(&done_push_arguments, EQ);  // check if there are arguments.
+  __ LoadImmediate(R1, 0, PP);
+  __ Bind(&push_arguments);
+  __ ldr(R3, Address(R2));
+  __ Push(R3);
+  __ add(R1, R1, Operand(1));
+  __ add(R2, R2, Operand(kWordSize));
+  __ cmp(R1, Operand(R5));
+  __ b(&push_arguments, LT);
+  __ Bind(&done_push_arguments);
+
+  // Call the Dart code entrypoint.
+  __ blr(R0);  // R4 is the arguments descriptor array.
+  __ Comment("InvokeDartCodeStub return");
+
+  // Read the saved new Context pointer.
+  __ LoadFromOffset(CTX, FP, kNewContextOffsetFromFp);
+  __ LoadFromOffset(CTX, CTX, VMHandles::kOffsetOfRawPtrInHandle);
+
+  // Get rid of arguments pushed on the stack.
+  __ AddImmediate(SP, FP, kSavedContextSlotFromEntryFp * kWordSize, PP);
+
+  // Load Isolate pointer from Context structure into CTX. Drop Context.
+  __ LoadFieldFromOffset(CTX, CTX, Context::isolate_offset());
+
+  // Restore the current VMTag from the stack.
+  __ ldr(R4, Address(SP, 2 * kWordSize));
+  __ StoreToOffset(R4, CTX, Isolate::vm_tag_offset());
+
+  // Restore the saved Context pointer into the Isolate structure.
+  // Uses R4 as a temporary register for this.
+  // Restore the saved top exit frame info back into the Isolate structure.
+  // Uses R6 as a temporary register for this.
+  __ Pop(R4);
+  __ Pop(R6);
+  __ StoreToOffset(R4, CTX, Isolate::top_context_offset());
+  __ StoreToOffset(R6, CTX, Isolate::top_exit_frame_info_offset());
+
+  __ Pop(R3);
+  __ Pop(R4);
+
+  // Restore C++ ABI callee-saved registers.
+  for (int i = R28; i >= R19; i--) {
+    Register r = static_cast<Register>(i);
+    // We use ldr instead of the Pop macro because we will be popping the PP
+    // register when it is not holding a pool-pointer since we are returning to
+    // C++ code.
+    __ ldr(r, Address(SP, 1 * kWordSize, Address::PostIndex));
+  }
+
+  // TODO(zra): Restore callee-saved fpu registers.
+
+  // Restore the frame pointer and return.
+  __ LeaveFrame();
+  __ ret();
 }
 
 
 void StubCode::GenerateAllocateContextStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateAllocateContextStub");
 }
 
 
 void StubCode::GenerateUpdateStoreBufferStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateUpdateStoreBufferStub");
 }
 
 
 void StubCode::GenerateAllocationStubForClass(Assembler* assembler,
                                               const Class& cls) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateAllocationStubForClass");
 }
 
 
 void StubCode::GenerateCallNoSuchMethodFunctionStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateCallNoSuchMethodFunctionStub");
 }
 
 
 void StubCode::GenerateOptimizedUsageCounterIncrement(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateOptimizedUsageCounterIncrement");
 }
 
 
 void StubCode::GenerateUsageCounterIncrement(Assembler* assembler,
                                              Register temp_reg) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateUsageCounterIncrement");
 }
 
 
@@ -117,105 +355,105 @@
     Assembler* assembler,
     intptr_t num_args,
     const RuntimeEntry& handle_ic_miss) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateNArgsCheckInlineCacheStub");
 }
 
 
 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateOneArgCheckInlineCacheStub");
 }
 
 
 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateTwoArgsCheckInlineCacheStub");
 }
 
 
 void StubCode::GenerateThreeArgsCheckInlineCacheStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateThreeArgsCheckInlineCacheStub");
 }
 
 
 void StubCode::GenerateOneArgOptimizedCheckInlineCacheStub(
     Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateOneArgOptimizedCheckInlineCacheStub");
 }
 
 
 void StubCode::GenerateTwoArgsOptimizedCheckInlineCacheStub(
     Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateTwoArgsOptimizedCheckInlineCacheStub");
 }
 
 
 void StubCode::GenerateThreeArgsOptimizedCheckInlineCacheStub(
     Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateThreeArgsOptimizedCheckInlineCacheStub");
 }
 
 
 void StubCode::GenerateClosureCallInlineCacheStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateClosureCallInlineCacheStub");
 }
 
 
 void StubCode::GenerateMegamorphicCallStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateMegamorphicCallStub");
 }
 
 
 void StubCode::GenerateZeroArgsUnoptimizedStaticCallStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateZeroArgsUnoptimizedStaticCallStub");
 }
 
 
 void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateTwoArgsUnoptimizedStaticCallStub");
 }
 
 
 void StubCode::GenerateLazyCompileStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateLazyCompileStub");
 }
 
 
 void StubCode::GenerateBreakpointRuntimeStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateBreakpointRuntimeStub");
 }
 
 
 void StubCode::GenerateDebugStepCheckStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateDebugStepCheckStub");
 }
 
 
 void StubCode::GenerateSubtype1TestCacheStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateSubtype1TestCacheStub");
 }
 
 
 void StubCode::GenerateSubtype2TestCacheStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateSubtype2TestCacheStub");
 }
 
 
 void StubCode::GenerateSubtype3TestCacheStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateSubtype3TestCacheStub");
 }
 
 
 void StubCode::GenerateGetStackPointerStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateGetStackPointerStub");
 }
 
 
 void StubCode::GenerateJumpToExceptionHandlerStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateJumpToExceptionHandlerStub");
 }
 
 
 void StubCode::GenerateOptimizeFunctionStub(Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateOptimizeFunctionStub");
 }
 
 
@@ -224,19 +462,19 @@
                                                     const Register right,
                                                     const Register temp,
                                                     const Register unused) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateIdenticalWithNumberCheckStub");
 }
 
 
 void StubCode::GenerateUnoptimizedIdenticalWithNumberCheckStub(
     Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateUnoptimizedIdenticalWithNumberCheckStub");
 }
 
 
 void StubCode::GenerateOptimizedIdenticalWithNumberCheckStub(
     Assembler* assembler) {
-  UNIMPLEMENTED();
+  __ Stop("GenerateOptimizedIdenticalWithNumberCheckStub");
 }
 
 }  // namespace dart
diff --git a/runtime/vm/stub_code_arm64_test.cc b/runtime/vm/stub_code_arm64_test.cc
index 7c2fe8f..fa12a56 100644
--- a/runtime/vm/stub_code_arm64_test.cc
+++ b/runtime/vm/stub_code_arm64_test.cc
@@ -5,6 +5,111 @@
 #include "vm/globals.h"
 #if defined(TARGET_ARCH_ARM64)
 
-// TODO(zra): Port these tests.
+#include "vm/isolate.h"
+#include "vm/dart_entry.h"
+#include "vm/native_entry.h"
+#include "vm/native_entry_test.h"
+#include "vm/object.h"
+#include "vm/runtime_entry.h"
+#include "vm/stub_code.h"
+#include "vm/symbols.h"
+#include "vm/unit_test.h"
+
+#define __ assembler->
+
+namespace dart {
+
+DECLARE_RUNTIME_ENTRY(TestSmiSub);
+DECLARE_LEAF_RUNTIME_ENTRY(RawObject*, TestLeafSmiAdd, RawObject*, RawObject*);
+
+
+static Function* CreateFunction(const char* name) {
+  const String& class_name = String::Handle(Symbols::New("ownerClass"));
+  const Script& script = Script::Handle();
+  const Class& owner_class =
+      Class::Handle(Class::New(class_name, script, Scanner::kNoSourcePos));
+  const Library& lib = Library::Handle(Library::New(class_name));
+  owner_class.set_library(lib);
+  const String& function_name = String::ZoneHandle(Symbols::New(name));
+  Function& function = Function::ZoneHandle(
+      Function::New(function_name, RawFunction::kRegularFunction,
+                    true, false, false, false, false, owner_class, 0));
+  return &function;
+}
+
+
+// Test calls to stub code which calls into the runtime.
+static void GenerateCallToCallRuntimeStub(Assembler* assembler,
+                                          int value1, int value2) {
+  const int argc = 2;
+  const Smi& smi1 = Smi::ZoneHandle(Smi::New(value1));
+  const Smi& smi2 = Smi::ZoneHandle(Smi::New(value2));
+  const Object& result = Object::ZoneHandle();
+  const Context& context = Context::ZoneHandle(Context::New(0, Heap::kOld));
+  ASSERT(context.isolate() == Isolate::Current());
+  __ EnterDartFrame(0);
+  __ LoadObject(CTX, context, PP);
+  __ PushObject(result, PP);  // Push Null object for return value.
+  __ PushObject(smi1, PP);  // Push argument 1 smi1.
+  __ PushObject(smi2, PP);  // Push argument 2 smi2.
+  ASSERT(kTestSmiSubRuntimeEntry.argument_count() == argc);
+  __ CallRuntime(kTestSmiSubRuntimeEntry, argc);  // Call SmiSub runtime func.
+  __ add(SP, SP, Operand(argc * kWordSize));
+  __ Pop(R0);  // Pop return value from return slot.
+  __ LeaveDartFrame();
+  __ ret();
+}
+
+
+TEST_CASE(CallRuntimeStubCode) {
+  extern const Function& RegisterFakeFunction(const char* name,
+                                              const Code& code);
+  const int value1 = 10;
+  const int value2 = 20;
+  const char* kName = "Test_CallRuntimeStubCode";
+  Assembler _assembler_;
+  GenerateCallToCallRuntimeStub(&_assembler_, value1, value2);
+  const Code& code = Code::Handle(Code::FinalizeCode(
+      *CreateFunction("Test_CallRuntimeStubCode"), &_assembler_));
+  const Function& function = RegisterFakeFunction(kName, code);
+  Smi& result = Smi::Handle();
+  result ^= DartEntry::InvokeFunction(function, Object::empty_array());
+  EXPECT_EQ((value1 - value2), result.Value());
+}
+
+
+// Test calls to stub code which calls into a leaf runtime entry.
+static void GenerateCallToCallLeafRuntimeStub(Assembler* assembler,
+                                              int value1,
+                                              int value2) {
+  const Smi& smi1 = Smi::ZoneHandle(Smi::New(value1));
+  const Smi& smi2 = Smi::ZoneHandle(Smi::New(value2));
+  __ EnterDartFrame(0);
+  __ ReserveAlignedFrameSpace(0);
+  __ LoadObject(R0, smi1, PP);  // Set up argument 1 smi1.
+  __ LoadObject(R1, smi2, PP);  // Set up argument 2 smi2.
+  __ CallRuntime(kTestLeafSmiAddRuntimeEntry, 2);  // Call SmiAdd runtime func.
+  __ LeaveDartFrame();
+  __ ret();  // Return value is in R0.
+}
+
+
+TEST_CASE(CallLeafRuntimeStubCode) {
+  extern const Function& RegisterFakeFunction(const char* name,
+                                              const Code& code);
+  const int value1 = 10;
+  const int value2 = 20;
+  const char* kName = "Test_CallLeafRuntimeStubCode";
+  Assembler _assembler_;
+  GenerateCallToCallLeafRuntimeStub(&_assembler_, value1, value2);
+  const Code& code = Code::Handle(Code::FinalizeCode(
+      *CreateFunction("Test_CallLeafRuntimeStubCode"), &_assembler_));
+  const Function& function = RegisterFakeFunction(kName, code);
+  Smi& result = Smi::Handle();
+  result ^= DartEntry::InvokeFunction(function, Object::empty_array());
+  EXPECT_EQ((value1 + value2), result.Value());
+}
+
+}  // namespace dart
 
 #endif  // defined TARGET_ARCH_ARM64
diff --git a/runtime/vm/stub_code_ia32.cc b/runtime/vm/stub_code_ia32.cc
index 2c1b247..82acef6 100644
--- a/runtime/vm/stub_code_ia32.cc
+++ b/runtime/vm/stub_code_ia32.cc
@@ -27,6 +27,7 @@
 DEFINE_FLAG(bool, use_slow_path, false,
     "Set to true for debugging & verifying the slow paths.");
 DECLARE_FLAG(bool, trace_optimized_ic_calls);
+DEFINE_FLAG(bool, verify_incoming_contexts, false, "");
 
 
 // Input parameters:
@@ -52,6 +53,19 @@
   // to transition to Dart VM C++ code.
   __ movl(Address(EAX, Isolate::top_exit_frame_info_offset()), ESP);
 
+#if defined(DEBUG)
+  if (FLAG_verify_incoming_contexts) {
+    Label ok;
+    // Check that the isolate's saved ctx is null.
+    const Immediate& raw_null =
+        Immediate(reinterpret_cast<intptr_t>(Object::null()));
+    __ cmpl(Address(EAX, Isolate::top_context_offset()), raw_null);
+    __ j(EQUAL, &ok, Assembler::kNearJump);
+    __ Stop("Found non-null incoming top context: call to runtime stub");
+    __ Bind(&ok);
+  }
+#endif
+
   // Save current Context pointer into Isolate structure.
   __ movl(Address(EAX, Isolate::top_context_offset()), CTX);
 
@@ -70,7 +84,7 @@
 #endif
 
   // Mark that the isolate is executing VM code.
-  __ movl(Address(CTX, Isolate::vm_tag_offset()), Immediate(VMTag::kVMTagId));
+  __ movl(Address(CTX, Isolate::vm_tag_offset()), ECX);
 
   // Reserve space for arguments and align frame before entering C++ world.
   __ AddImmediate(ESP, Immediate(-sizeof(NativeArguments)));
@@ -160,6 +174,20 @@
   // to transition to dart VM code.
   __ movl(Address(EDI, Isolate::top_exit_frame_info_offset()), ESP);
 
+#if defined(DEBUG)
+  if (FLAG_verify_incoming_contexts) {
+    Label ok;
+    // Check that the isolate's saved ctx is null.
+    const Immediate& raw_null =
+        Immediate(reinterpret_cast<intptr_t>(Object::null()));
+    __ cmpl(Address(EDI, Isolate::top_context_offset()), raw_null);
+    __ j(EQUAL, &ok, Assembler::kNearJump);
+    __ Stop("Found non-null incoming top context: "
+            "call to native c function stub");
+    __ Bind(&ok);
+  }
+#endif
+
   // Save current Context pointer into Isolate structure.
   __ movl(Address(EDI, Isolate::top_context_offset()), CTX);
 
@@ -178,8 +206,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ movl(Address(CTX, Isolate::vm_tag_offset()),
-          Immediate(VMTag::kRuntimeNativeTagId));
+  __ movl(Address(CTX, Isolate::vm_tag_offset()), ECX);
 
   // Reserve space for the native arguments structure, the outgoing parameters
   // (pointer to the native arguments structure, the C function entry point)
@@ -260,6 +287,20 @@
   // to transition to dart VM code.
   __ movl(Address(EDI, Isolate::top_exit_frame_info_offset()), ESP);
 
+#if defined(DEBUG)
+  if (FLAG_verify_incoming_contexts) {
+    Label ok;
+    // Check that the isolate's saved ctx is null.
+    const Immediate& raw_null =
+        Immediate(reinterpret_cast<intptr_t>(Object::null()));
+    __ cmpl(Address(EDI, Isolate::top_context_offset()), raw_null);
+    __ j(EQUAL, &ok, Assembler::kNearJump);
+    __ Stop("Found non-null incoming top context: "
+            "call to bootstrap c function stub");
+    __ Bind(&ok);
+  }
+#endif
+
   // Save current Context pointer into Isolate structure.
   __ movl(Address(EDI, Isolate::top_context_offset()), CTX);
 
@@ -278,8 +319,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ movl(Address(CTX, Isolate::vm_tag_offset()),
-          Immediate(VMTag::kRuntimeNativeTagId));
+  __ movl(Address(CTX, Isolate::vm_tag_offset()), ECX);
 
   // Reserve space for the native arguments structure, the outgoing parameter
   // (pointer to the native arguments structure) and align frame before
@@ -700,82 +740,6 @@
 }
 
 
-// Input parameters:
-//   EDX: Arguments descriptor array.
-// Note: The closure object is the first argument to the function being
-//       called, the stub accesses the closure from this location directly
-//       when trying to resolve the call.
-// Uses EDI.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
-  const Immediate& raw_null =
-      Immediate(reinterpret_cast<intptr_t>(Object::null()));
-
-  // Load num_args.
-  __ movl(EAX, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
-  // Load closure object in EDI.
-  __ movl(EDI, Address(ESP, EAX, TIMES_2, 0));  // EAX is a Smi.
-
-  // Verify that EDI is a closure by checking its class.
-  Label not_closure;
-  __ cmpl(EDI, raw_null);
-  // Not a closure, but null object.
-  __ j(EQUAL, &not_closure, Assembler::kNearJump);
-  __ testl(EDI, Immediate(kSmiTagMask));
-  __ j(ZERO, &not_closure, Assembler::kNearJump);  // Not a closure, but a smi.
-  // Verify that the class of the object is a closure class by checking that
-  // class.signature_function() is not null.
-  __ LoadClass(EAX, EDI, ECX);
-  __ movl(EAX, FieldAddress(EAX, Class::signature_function_offset()));
-  __ cmpl(EAX, raw_null);
-  // Actual class is not a closure class.
-  __ j(EQUAL, &not_closure, Assembler::kNearJump);
-
-  // EAX is just the signature function. Load the actual closure function.
-  __ movl(EAX, FieldAddress(EDI, Closure::function_offset()));
-
-  // Load closure context in CTX; note that CTX has already been preserved.
-  __ movl(CTX, FieldAddress(EDI, Closure::context_offset()));
-
-  // EBX: Code (compiled code or lazy compile stub).
-  __ movl(EBX, FieldAddress(EAX, Function::code_offset()));
-
-  // EAX: Function.
-  // EDX: Arguments descriptor array.
-  // ECX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
-  __ xorl(ECX, ECX);
-  __ movl(EBX, FieldAddress(EBX, Code::instructions_offset()));
-  __ addl(EBX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
-  __ jmp(EBX);
-
-  __ Bind(&not_closure);
-  // Call runtime to attempt to resolve and invoke a call method on a
-  // non-closure object, passing the non-closure object and its arguments array,
-  // returning here.
-  // If no call method exists, throw a NoSuchMethodError.
-  // EDI: non-closure object.
-  // EDX: arguments descriptor array.
-
-  // Create a stub frame as we are pushing some objects on the stack before
-  // calling into the runtime.
-  __ EnterStubFrame();
-
-  __ pushl(raw_null);  // Setup space on stack for result from error reporting.
-  __ pushl(EDX);  // Arguments descriptor.
-  // Load smi-tagged arguments array length, including the non-closure.
-  __ movl(EDX, FieldAddress(EDX, ArgumentsDescriptor::count_offset()));
-  PushArgumentsArray(assembler);
-
-  __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
-  // Remove arguments.
-  __ Drop(2);
-  __ popl(EAX);  // Get result into EAX.
-
-  // Remove the stub frame as we are about to return.
-  __ LeaveFrame();
-  __ ret();
-}
-
-
 // Called when invoking dart code from C++ (VM code).
 // Input parameters:
 //   ESP : points to return address.
@@ -840,6 +804,18 @@
   __ movl(ECX, Address(EDI, Isolate::top_context_offset()));
   __ pushl(ECX);
 
+  // TODO(turnidge): This code should probably be emitted all the time
+  // on all architectures but I am leaving it under DEBUG/flag for
+  // now.
+#if defined(DEBUG)
+  if (FLAG_verify_incoming_contexts) {
+    // Clear Context pointer in Isolate structure.
+    const Immediate& raw_null =
+        Immediate(reinterpret_cast<intptr_t>(Object::null()));
+    __ movl(Address(EDI, Isolate::top_context_offset()), raw_null);
+  }
+#endif
+
   // Load arguments descriptor array into EDX.
   __ movl(EDX, Address(EBP, kArgumentsDescOffset));
   __ movl(EDX, Address(EDX, VMHandles::kOffsetOfRawPtrInHandle));
diff --git a/runtime/vm/stub_code_mips.cc b/runtime/vm/stub_code_mips.cc
index aec9159..dfe0601 100644
--- a/runtime/vm/stub_code_mips.cc
+++ b/runtime/vm/stub_code_mips.cc
@@ -73,8 +73,7 @@
 #endif
 
   // Mark that the isolate is executing VM code.
-  __ LoadImmediate(T0, VMTag::kVMTagId);
-  __ sw(T0, Address(A0, Isolate::vm_tag_offset()));
+  __ sw(S5, Address(A0, Isolate::vm_tag_offset()));
 
   // Reserve space for arguments and align frame before entering C++ world.
   // NativeArguments are passed in registers.
@@ -200,8 +199,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ LoadImmediate(T0, VMTag::kRuntimeNativeTagId);
-  __ sw(T0, Address(A0, Isolate::vm_tag_offset()));
+  __ sw(T5, Address(A0, Isolate::vm_tag_offset()));
 
   // Initialize NativeArguments structure and call native function.
   // Registers A0, A1, A2, and A3 are used.
@@ -334,8 +332,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ LoadImmediate(T0, VMTag::kRuntimeNativeTagId);
-  __ sw(T0, Address(A0, Isolate::vm_tag_offset()));
+  __ sw(T5, Address(A0, Isolate::vm_tag_offset()));
 
   // Initialize NativeArguments structure and call native function.
   // Registers A0, A1, A2, and A3 are used.
@@ -826,100 +823,6 @@
 }
 
 
-// Input parameters:
-//   RA: return address.
-//   SP: address of last argument.
-//   S4: Arguments descriptor array.
-// Return: V0.
-// Note: The closure object is the first argument to the function being
-//       called, the stub accesses the closure from this location directly
-//       when trying to resolve the call.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
-  // Load num_args.
-  __ TraceSimMsg("GenerateCallClosureFunctionStub");
-  __ lw(T0, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
-  __ LoadImmediate(TMP, Smi::RawValue(1));
-  __ subu(T0, T0, TMP);
-
-  // Load closure object in T1.
-  __ sll(T1, T0, 1);  // T0 (num_args - 1) is a Smi.
-  __ addu(T1, SP, T1);
-  __ lw(T1, Address(T1));
-
-  // Verify that T1 is a closure by checking its class.
-  Label not_closure;
-
-  __ LoadImmediate(T7, reinterpret_cast<intptr_t>(Object::null()));
-
-  // See if it is not a closure, but null object.
-  __ beq(T1, T7, &not_closure);
-
-  __ andi(CMPRES1, T1, Immediate(kSmiTagMask));
-  __ beq(CMPRES1, ZR, &not_closure);  // Not a closure, but a smi.
-
-  // Verify that the class of the object is a closure class by checking that
-  // class.signature_function() is not null.
-  __ LoadClass(T0, T1);
-  __ lw(T0, FieldAddress(T0, Class::signature_function_offset()));
-
-  // See if actual class is not a closure class.
-  __ beq(T0, T7, &not_closure);
-
-  // T0 is just the signature function. Load the actual closure function.
-  __ lw(T0, FieldAddress(T1, Closure::function_offset()));
-
-  // Load closure context in CTX; note that CTX has already been preserved.
-  __ lw(CTX, FieldAddress(T1, Closure::context_offset()));
-
-  // Load closure function code in T2.
-  // S4: arguments descriptor array.
-  // S5: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
-  __ LoadImmediate(S5, 0);
-  __ lw(T2, FieldAddress(T0, Function::code_offset()));
-  __ lw(T2, FieldAddress(T2, Code::instructions_offset()));
-  __ AddImmediate(T2, Instructions::HeaderSize() - kHeapObjectTag);
-  __ jr(T2);
-
-  __ Bind(&not_closure);
-  // Call runtime to attempt to resolve and invoke a call method on a
-  // non-closure object, passing the non-closure object and its arguments array,
-  // returning here.
-  // If no call method exists, throw a NoSuchMethodError.
-  // T1: non-closure object.
-  // S4: arguments descriptor array.
-
-  // Create a stub frame as we are pushing some objects on the stack before
-  // calling into the runtime.
-  __ EnterStubFrame();
-
-  // Setup space on stack for result from error reporting.
-  __ addiu(SP, SP, Immediate(-2 * kWordSize));
-  // Arguments descriptor and raw null.
-  __ sw(T7, Address(SP, 1 * kWordSize));
-  __ sw(S4, Address(SP, 0 * kWordSize));
-
-  // Load smi-tagged arguments array length, including the non-closure.
-  __ lw(A1, FieldAddress(S4, ArgumentsDescriptor::count_offset()));
-  PushArgumentsArray(assembler);
-
-  // Stack:
-  // TOS + 0: Argument array.
-  // TOS + 1: Arguments descriptor array.
-  // TOS + 2: Place for result from the call.
-  // TOS + 3: Saved FP of previous frame.
-  // TOS + 4: Dart code return address.
-  // TOS + 5: PC marker (0 for stub).
-  // TOS + 6: Last argument of caller.
-  // ....
-  __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
-  __ lw(V0, Address(SP, 2 * kWordSize));  // Get result into V0.
-  __ addiu(SP, SP, Immediate(3 * kWordSize));  // Remove arguments.
-
-  // Remove the stub frame as we are about to return.
-  __ LeaveStubFrameAndReturn();
-}
-
-
 // Called when invoking Dart code from C++ (VM code).
 // Input parameters:
 //   RA : points to return address.
diff --git a/runtime/vm/stub_code_x64.cc b/runtime/vm/stub_code_x64.cc
index d914e54..cc7f4cc 100644
--- a/runtime/vm/stub_code_x64.cc
+++ b/runtime/vm/stub_code_x64.cc
@@ -70,7 +70,7 @@
 #endif
 
   // Mark that the isolate is executing VM code.
-  __ movq(Address(CTX, Isolate::vm_tag_offset()), Immediate(VMTag::kVMTagId));
+  __ movq(Address(CTX, Isolate::vm_tag_offset()), RBX);
 
   // Reserve space for arguments and align frame before entering C++ world.
   __ subq(RSP, Immediate(sizeof(NativeArguments)));
@@ -175,8 +175,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ movq(Address(CTX, Isolate::vm_tag_offset()),
-          Immediate(VMTag::kRuntimeNativeTagId));
+  __ movq(Address(CTX, Isolate::vm_tag_offset()), RBX);
 
   // Reserve space for the native arguments structure passed on the stack (the
   // outgoing pointer parameter to the native arguments structure is passed in
@@ -272,8 +271,7 @@
 #endif
 
   // Mark that the isolate is executing Native code.
-  __ movq(Address(CTX, Isolate::vm_tag_offset()),
-          Immediate(VMTag::kRuntimeNativeTagId));
+  __ movq(Address(CTX, Isolate::vm_tag_offset()), RBX);
 
   // Reserve space for the native arguments structure passed on the stack (the
   // outgoing pointer parameter to the native arguments structure is passed in
@@ -693,81 +691,6 @@
 }
 
 
-// Input parameters:
-//   R10: Arguments descriptor array.
-// Note: The closure object is the first argument to the function being
-//       called, the stub accesses the closure from this location directly
-//       when trying to resolve the call.
-void StubCode::GenerateCallClosureFunctionStub(Assembler* assembler) {
-  // Load num_args.
-  __ movq(RAX, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
-  // Load closure object in R13.
-  __ movq(R13, Address(RSP, RAX, TIMES_4, 0));  // RAX is a Smi.
-
-  __ LoadObject(R12, Object::null_object(), PP);
-
-  // Verify that R13 is a closure by checking its class.
-  Label not_closure;
-  __ cmpq(R13, R12);
-  // Not a closure, but null object.
-  __ j(EQUAL, &not_closure);
-  __ testq(R13, Immediate(kSmiTagMask));
-  __ j(ZERO, &not_closure);  // Not a closure, but a smi.
-  // Verify that the class of the object is a closure class by checking that
-  // class.signature_function() is not null.
-  __ LoadClass(RAX, R13);
-  __ movq(RAX, FieldAddress(RAX, Class::signature_function_offset()));
-  __ cmpq(RAX, R12);
-  // Actual class is not a closure class.
-  __ j(EQUAL, &not_closure, Assembler::kNearJump);
-
-  // RAX is just the signature function. Load the actual closure function.
-  __ movq(RAX, FieldAddress(R13, Closure::function_offset()));
-
-  // Load closure context in CTX; note that CTX has already been preserved.
-  __ movq(CTX, FieldAddress(R13, Closure::context_offset()));
-
-  // Load closure function code in RAX.
-  __ movq(RCX, FieldAddress(RAX, Function::code_offset()));
-
-  // RAX: Function.
-  // R10: Arguments descriptor array.
-  // RBX: Smi 0 (no IC data; the lazy-compile stub expects a GC-safe value).
-  __ xorq(RBX, RBX);
-  __ movq(RCX, FieldAddress(RCX, Code::instructions_offset()));
-  __ addq(RCX, Immediate(Instructions::HeaderSize() - kHeapObjectTag));
-  __ jmp(RCX);
-
-  __ Bind(&not_closure);
-  // Call runtime to attempt to resolve and invoke a call method on a
-  // non-closure object, passing the non-closure object and its arguments array,
-  // returning here.
-  // If no call method exists, throw a NoSuchMethodError.
-  // R13: non-closure object.
-  // R10: arguments descriptor array.
-
-  // Create a stub frame as we are pushing some objects on the stack before
-  // calling into the runtime.
-  __ EnterStubFrame();
-  // Setup space on stack for result from call.
-  __ pushq(R12);
-  __ pushq(R10);  // Arguments descriptor.
-  // Load smi-tagged arguments array length, including the non-closure.
-  __ movq(R10, FieldAddress(R10, ArgumentsDescriptor::count_offset()));
-  PushArgumentsArray(assembler);
-
-  __ CallRuntime(kInvokeNonClosureRuntimeEntry, 2);
-
-  // Remove arguments.
-  __ Drop(2);
-  __ popq(RAX);  // Get result into RAX.
-
-  // Remove the stub frame as we are about to return.
-  __ LeaveStubFrame();
-  __ ret();
-}
-
-
 // Called when invoking Dart code from C++ (VM code).
 // Input parameters:
 //   RSP : points to return address.
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 4d11dba..3d6d5b2 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -290,6 +290,7 @@
   V(DartMirrors, "dart:mirrors")                                               \
   V(DartTypedData, "dart:typed_data")                                          \
   V(DartVMService, "dart:vmservice")                                           \
+  V(DartProfiler, "dart:profiler")                                             \
   V(_Random, "_Random")                                                        \
   V(_state, "_state")                                                          \
   V(_A, "_A")                                                                  \
@@ -315,6 +316,8 @@
   V(ClassId, "get:_classId")                                                   \
   V(AllocationStubFor, "Allocation stub for ")                                 \
   V(TempParam, ":temp_param")                                                  \
+  V(UserTag, "UserTag")                                                        \
+  V(_UserTag, "_UserTag")                                                      \
 
 
 // Contains a list of frequently used strings in a canonicalized form. This
diff --git a/runtime/vm/tags.cc b/runtime/vm/tags.cc
index f05aa22..21e1d5a 100644
--- a/runtime/vm/tags.cc
+++ b/runtime/vm/tags.cc
@@ -6,10 +6,24 @@
 
 #include "vm/isolate.h"
 #include "vm/json_stream.h"
+#include "vm/native_entry.h"
+#include "vm/runtime_entry.h"
+#include "vm/object.h"
 
 namespace dart {
 
 const char* VMTag::TagName(uword tag) {
+  if (IsNativeEntryTag(tag)) {
+    const uint8_t* native_reverse_lookup = NativeEntry::ResolveSymbol(tag);
+    if (native_reverse_lookup != NULL) {
+      return reinterpret_cast<const char*>(native_reverse_lookup);
+    }
+    return "Unknown native entry";
+  } else if (IsRuntimeEntryTag(tag)) {
+    const char* runtime_entry_name = RuntimeEntryTagName(tag);
+    ASSERT(runtime_entry_name != NULL);
+    return runtime_entry_name;
+  }
   ASSERT(tag != kInvalidTagId);
   ASSERT(tag < kNumVMTags);
   const TagEntry& entry = entries_[tag];
@@ -18,6 +32,48 @@
 }
 
 
+bool VMTag::IsNativeEntryTag(uword tag) {
+  if (tag == 0) {
+    return false;
+  }
+  ASSERT(tag != kInvalidTagId);
+  ASSERT(tag != kNumVMTags);
+  return (tag > kNumVMTags) && !IsRuntimeEntryTag(tag);
+}
+
+static RuntimeEntry* runtime_entry_list = NULL;
+
+bool VMTag::IsRuntimeEntryTag(uword id) {
+  const RuntimeEntry* current = runtime_entry_list;
+  while (current != NULL) {
+    if (reinterpret_cast<uword>(current->function()) == id) {
+      return true;
+    }
+    current = current->next();
+  }
+  return false;
+}
+
+
+const char* VMTag::RuntimeEntryTagName(uword id) {
+  const RuntimeEntry* current = runtime_entry_list;
+  while (current != NULL) {
+    if (reinterpret_cast<uword>(current->function()) == id) {
+      return current->name();
+    }
+    current = current->next();
+  }
+  return NULL;
+}
+
+
+void VMTag::RegisterRuntimeEntry(RuntimeEntry* runtime_entry) {
+  ASSERT(runtime_entry != NULL);
+  runtime_entry->set_next(runtime_entry_list);
+  runtime_entry_list = runtime_entry;
+}
+
+
 VMTag::TagEntry VMTag::entries_[] = {
   { "InvalidTag", kInvalidTagId, },
 #define DEFINE_VM_TAG_ENTRY(tag)                                               \
@@ -50,6 +106,14 @@
 
 
 void VMTagCounters::Increment(uword tag) {
+  if (VMTag::IsRuntimeEntryTag(tag)) {
+    counters_[VMTag::kRuntimeTagId]++;
+    return;
+  } else if (tag > VMTag::kNumVMTags) {
+    // Assume native entry.
+    counters_[VMTag::kNativeTagId]++;
+    return;
+  }
   ASSERT(tag != VMTag::kInvalidTagId);
   ASSERT(tag < VMTag::kNumVMTags);
   counters_[tag]++;
@@ -78,4 +142,17 @@
   }
 }
 
+
+const char* UserTags::TagName(uword tag_id) {
+  ASSERT(tag_id >= kUserTagIdOffset);
+  ASSERT(tag_id < kUserTagIdOffset + kMaxUserTags);
+  Isolate* isolate = Isolate::Current();
+  const UserTag& tag =
+      UserTag::Handle(isolate, UserTag::FindTagById(tag_id));
+  ASSERT(!tag.IsNull());
+  const String& label = String::Handle(isolate, tag.label());
+  return label.ToCString();
+}
+
+
 }  // namespace dart
diff --git a/runtime/vm/tags.h b/runtime/vm/tags.h
index de0468f..f24f5a3 100644
--- a/runtime/vm/tags.h
+++ b/runtime/vm/tags.h
@@ -11,6 +11,7 @@
 
 class Isolate;
 class JSONObject;
+class RuntimeEntry;
 
 #define VM_TAG_LIST(V)                                                         \
   V(Idle)                                                                      \
@@ -19,8 +20,8 @@
   V(Script)                                                                    \
   V(GCNewSpace)                                                                \
   V(GCOldSpace)                                                                \
-  V(RuntimeNative)                                                             \
-
+  V(Runtime)                                                                   \
+  V(Native)                                                                    \
 
 class VMTag : public AllStatic {
  public:
@@ -33,7 +34,16 @@
     kNumVMTags,
   };
 
+  static bool IsVMTag(uword id) {
+    return (id != kInvalidTagId) && (id < kNumVMTags);
+  }
   static const char* TagName(uword id);
+  static bool IsNativeEntryTag(uword id);
+
+  static bool IsRuntimeEntryTag(uword id);
+  static const char* RuntimeEntryTagName(uword id);
+
+  static void RegisterRuntimeEntry(RuntimeEntry* runtime_entry);
 
  private:
   struct TagEntry {
@@ -70,6 +80,21 @@
   int64_t counters_[VMTag::kNumVMTags];
 };
 
+
+class UserTags : public AllStatic {
+ public:
+  static const uword kNoUserTag = 0;
+  // UserTag id space: [kUserTagIdOffset, kUserTagIdOffset + kMaxUserTags).
+  static const intptr_t kMaxUserTags = 64;
+  static const uword kUserTagIdOffset = 0x4096;
+  static const char* TagName(uword tag_id);
+  static bool IsUserTag(uword tag_id) {
+    return (tag_id >= kUserTagIdOffset) &&
+           (tag_id < kUserTagIdOffset + kMaxUserTags);
+  }
+};
+
+
 }  // namespace dart
 
 #endif  // VM_TAGS_H_
diff --git a/runtime/vm/unit_test.cc b/runtime/vm/unit_test.cc
index a800505..5d45453 100644
--- a/runtime/vm/unit_test.cc
+++ b/runtime/vm/unit_test.cc
@@ -120,7 +120,7 @@
   EXPECT_VALID(result);
   Dart_Handle lib = Dart_LoadScript(url, source, 0, 0);
   DART_CHECK_VALID(lib);
-  result = Dart_SetNativeResolver(lib, resolver);
+  result = Dart_SetNativeResolver(lib, resolver, NULL);
   DART_CHECK_VALID(result);
   return lib;
 }
diff --git a/runtime/vm/vm.gypi b/runtime/vm/vm.gypi
index ddb0db7..3bc5407 100644
--- a/runtime/vm/vm.gypi
+++ b/runtime/vm/vm.gypi
@@ -29,6 +29,8 @@
     'snapshot_test_dart_file': 'snapshot_test.dart',
     'typed_data_cc_file': '<(gen_source_dir)/typed_data_gen.cc',
     'typed_data_patch_cc_file': '<(gen_source_dir)/typed_data_patch_gen.cc',
+    'profiler_cc_file': '<(gen_source_dir)/profiler_gen.cc',
+    'profiler_patch_cc_file': '<(gen_source_dir)/profiler_patch_gen.cc',
   },
   'targets': [
     {
@@ -111,6 +113,8 @@
         'generate_mirrors_patch_cc_file#host',
         'generate_typed_data_cc_file#host',
         'generate_typed_data_patch_cc_file#host',
+        'generate_profiler_cc_file#host',
+        'generate_profiler_patch_cc_file#host',
       ],
       'includes': [
         '../lib/async_sources.gypi',
@@ -120,6 +124,7 @@
         '../lib/math_sources.gypi',
         '../lib/mirrors_sources.gypi',
         '../lib/typed_data_sources.gypi',
+        '../lib/profiler_sources.gypi',
       ],
       'sources': [
         'bootstrap.cc',
@@ -142,6 +147,8 @@
         '<(mirrors_patch_cc_file)',
         '<(typed_data_cc_file)',
         '<(typed_data_patch_cc_file)',
+        '<(profiler_cc_file)',
+        '<(profiler_patch_cc_file)',
       ],
       'include_dirs': [
         '..',
@@ -159,6 +166,7 @@
         '../lib/math_sources.gypi',
         '../lib/mirrors_sources.gypi',
         '../lib/typed_data_sources.gypi',
+        '../lib/profiler_sources.gypi',
       ],
       'sources': [
         'bootstrap_nocorelib.cc',
@@ -887,6 +895,86 @@
       ]
     },
     {
+      'target_name': 'generate_profiler_cc_file',
+      'type': 'none',
+      'toolsets':['host'],
+      'includes': [
+        # Load the shared library sources.
+        '../../sdk/lib/profiler/profiler_sources.gypi',
+      ],
+      'sources/': [
+        # Exclude all .[cc|h] files.
+        # This is only here for reference. Excludes happen after
+        # variable expansion, so the script has to do its own
+        # exclude processing of the sources being passed.
+        ['exclude', '\\.cc|h$'],
+      ],
+      'actions': [
+        {
+          'action_name': 'generate_profiler_cc',
+          'inputs': [
+            '../tools/gen_library_src_paths.py',
+            '<(libgen_in_cc_file)',
+            '<@(_sources)',
+          ],
+          'outputs': [
+            '<(profiler_cc_file)',
+          ],
+          'action': [
+            'python',
+            'tools/gen_library_src_paths.py',
+            '--output', '<(profiler_cc_file)',
+            '--input_cc', '<(libgen_in_cc_file)',
+            '--include', 'vm/bootstrap.h',
+            '--var_name', 'dart::Bootstrap::profiler_source_paths_',
+            '--library_name', 'dart:profiler',
+            '<@(_sources)',
+          ],
+          'message': 'Generating ''<(profiler_cc_file)'' file.'
+        },
+      ]
+    },
+    {
+      'target_name': 'generate_profiler_patch_cc_file',
+      'type': 'none',
+      'toolsets':['host'],
+      'includes': [
+        # Load the runtime implementation sources.
+        '../lib/profiler_sources.gypi',
+      ],
+      'sources/': [
+        # Exclude all .[cc|h] files.
+        # This is only here for reference. Excludes happen after
+        # variable expansion, so the script has to do its own
+        # exclude processing of the sources being passed.
+        ['exclude', '\\.cc|h$'],
+      ],
+      'actions': [
+        {
+          'action_name': 'generate_profiler_patch_cc',
+          'inputs': [
+            '../tools/gen_library_src_paths.py',
+            '<(libgen_in_cc_file)',
+            '<@(_sources)',
+          ],
+          'outputs': [
+            '<(profiler_patch_cc_file)',
+          ],
+          'action': [
+            'python',
+            'tools/gen_library_src_paths.py',
+            '--output', '<(profiler_patch_cc_file)',
+            '--input_cc', '<(libgen_in_cc_file)',
+            '--include', 'vm/bootstrap.h',
+            '--var_name', 'dart::Bootstrap::profiler_patch_paths_',
+            '--library_name', 'dart:profiler',
+            '<@(_sources)',
+          ],
+          'message': 'Generating ''<(profiler_patch_cc_file)'' file.'
+        },
+      ]
+    },
+    {
       'target_name': 'generate_snapshot_test_dat_file',
       'type': 'none',
       'toolsets':['host'],
diff --git a/sdk/lib/_internal/compiler/implementation/apiimpl.dart b/sdk/lib/_internal/compiler/implementation/apiimpl.dart
index bdfd4b4..d54dc9a 100644
--- a/sdk/lib/_internal/compiler/implementation/apiimpl.dart
+++ b/sdk/lib/_internal/compiler/implementation/apiimpl.dart
@@ -63,7 +63,8 @@
                 "build number could not be determined"),
             showPackageWarnings:
                 hasOption(options, '--show-package-warnings'),
-            useContentSecurityPolicy: hasOption(options, '--csp')) {
+            useContentSecurityPolicy: hasOption(options, '--csp'),
+            suppressWarnings: hasOption(options, '--suppress-warnings')) {
     if (!libraryRoot.path.endsWith("/")) {
       throw new ArgumentError("libraryRoot must end with a /");
     }
diff --git a/sdk/lib/_internal/compiler/implementation/compiler.dart b/sdk/lib/_internal/compiler/implementation/compiler.dart
index 4899ec7..96b2876 100644
--- a/sdk/lib/_internal/compiler/implementation/compiler.dart
+++ b/sdk/lib/_internal/compiler/implementation/compiler.dart
@@ -431,6 +431,8 @@
   /// suppressed for each library.
   Map<Uri, SuppressionInfo> suppressedWarnings = <Uri, SuppressionInfo>{};
 
+  final bool suppressWarnings;
+
   final api.CompilerOutputProvider outputProvider;
 
   bool disableInlining = false;
@@ -660,6 +662,7 @@
             this.dumpInfo: false,
             this.showPackageWarnings: false,
             this.useContentSecurityPolicy: false,
+            this.suppressWarnings: false,
             outputProvider,
             List<String> strips: const []})
       : this.analyzeOnly =
@@ -1116,7 +1119,7 @@
     enqueuer.resolution.logSummary(log);
 
     if (compilationFailed) return;
-    if (!showPackageWarnings) {
+    if (!showPackageWarnings && !suppressWarnings) {
       suppressedWarnings.forEach((Uri uri, SuppressionInfo info) {
         MessageKind kind = MessageKind.HIDDEN_WARNINGS_HINTS;
         if (info.warnings == 0) {
@@ -1312,7 +1315,8 @@
     assert(invariant(element, !element.isSynthesized || tree == null));
     if (tree != null) validator.validate(tree);
     elements = resolver.resolve(element);
-    if (tree != null && elements != null && !analyzeSignaturesOnly) {
+    if (tree != null && elements != null && !analyzeSignaturesOnly &&
+        !suppressWarnings) {
       // Only analyze nodes with a corresponding [TreeElements].
       checker.check(elements);
     }
diff --git a/sdk/lib/_internal/compiler/implementation/dart2js.dart b/sdk/lib/_internal/compiler/implementation/dart2js.dart
index 5d2349d..f62b083 100644
--- a/sdk/lib/_internal/compiler/implementation/dart2js.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart2js.dart
@@ -261,8 +261,10 @@
     new OptionHandler('-[chvm?]+', handleShortOptions),
     new OptionHandler('--throw-on-error',
                       (_) => diagnosticHandler.throwOnError = true),
-    new OptionHandler('--suppress-warnings',
-                      (_) => diagnosticHandler.showWarnings = false),
+    new OptionHandler('--suppress-warnings', (_) {
+      diagnosticHandler.showWarnings = false;
+      passThrough('--suppress-warnings');
+    }),
     new OptionHandler('--suppress-hints',
                       (_) => diagnosticHandler.showHints = false),
     new OptionHandler('--output-type=dart|--output-type=js', setOutputType),
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
index 2d66728..a4d1f1f 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/backend.dart
@@ -233,16 +233,17 @@
       if (!compiler.irBuilder.hasIr(element)) {
         node = element.parseNode(compiler);
       } else {
-        ir.Function function = compiler.irBuilder.getIr(element);
+        ir.FunctionDefinition function = compiler.irBuilder.getIr(element);
         tree.Builder builder = new tree.Builder(compiler);
-        tree.Expression expr = function.accept(builder);
-        compiler.tracer.traceGraph('Tree builder', expr);
+        tree.FunctionDefinition definition = builder.build(function);
+        compiler.tracer.traceCompilation(element.name, null, compiler);
+        compiler.tracer.traceGraph('Tree builder', definition);
         treeElements = new TreeElementMapping(element);
         tree.Unnamer unnamer = new tree.Unnamer();
-        expr = unnamer.unname(expr);
-        compiler.tracer.traceGraph('Unnamer', expr);
+        unnamer.unname(definition);
+        compiler.tracer.traceGraph('Unnamer', definition);
         tree.Emitter emitter = new tree.Emitter();
-        node = emitter.emit(element, treeElements, expr);
+        node = emitter.emit(element, treeElements, definition);
       }
       return new ElementAst(node, treeElements);
     }
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart
index 178f3bd..59d7a56 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/dart_tree.dart
@@ -7,7 +7,8 @@
 import '../dart2jslib.dart' as dart2js;
 import '../dart_types.dart';
 import '../util/util.dart';
-import '../elements/elements.dart' show FunctionElement, FunctionSignature;
+import '../elements/elements.dart'
+    show Element, FunctionElement, FunctionSignature, ParameterElement;
 import '../ir/ir_nodes.dart' as ir;
 import '../tree/tree.dart' as ast;
 import '../scanner/scannerlib.dart';
@@ -50,11 +51,15 @@
   static int counter = 0;
   static String _newName() => 'v${counter++}';
 
-  ast.Identifier identifier = null;
+  final Element element;
+  String name;
+  ast.Identifier identifier;
+
+  Variable(this.element);
 
   ast.Identifier assignIdentifier() {
     assert(identifier == null);
-    String name = _newName();
+    name = (element == null) ? _newName() : element.name;
     identifier = Emitter.makeIdentifier(name);
     return identifier;
   }
@@ -85,10 +90,11 @@
  */
 class LetVal extends Expression {
   final Variable variable;
-  final Expression definition;
-  final Expression body;
+  Expression definition;
+  Expression body;
+  final bool hasExactlyOneUse;
 
-  LetVal(this.variable, this.definition, this.body);
+  LetVal(this.variable, this.definition, this.body, this.hasExactlyOneUse);
 
   bool get isPure => definition.isPure && body.isPure;
 
@@ -140,9 +146,21 @@
   accept(Visitor visitor) => visitor.visitConstant(this);
 }
 
+class FunctionDefinition extends Node {
+  final List<Variable> parameters;
+  Expression body;
+
+  FunctionDefinition(this.parameters, this.body);
+
+  accept(Visitor visitor) => visitor.visitFunctionDefinition(this);
+}
+
 abstract class Visitor<T> {
+  T visit(Node node) => node.accept(this);
+
   // Abstract classes.
-  T visitNode(Node node) => node.accept(this);
+  T visitNode(Node node) => null;
+  T visitFunctionDefinition(FunctionDefinition node) => visitNode(node);
   T visitExpression(Expression node) => visitNode(node);
 
   // Concrete classes.
@@ -193,31 +211,47 @@
   // mapping from definitions to variables.
   final Map<ir.Definition, Variable> variables = {};
 
+  FunctionDefinition function;
   ir.Continuation returnContinuation;
 
   Builder(this.compiler);
 
+  FunctionDefinition build(ir.FunctionDefinition node) {
+    visit(node);
+    return function;
+  }
+
   List<Expression> translateArguments(List<ir.Reference> args) {
     return new List.generate(args.length,
          (int index) => variables[args[index].definition]);
   }
 
-  Expression visitFunction(ir.Function node) {
-    // Functions are simplistically translated to their bodies.  For now this
-    // is good enough.
+  Expression visitFunctionDefinition(ir.FunctionDefinition node) {
     returnContinuation = node.returnContinuation;
-    return node.body.accept(this);
+    List<Variable> parameters = <Variable>[];
+    for (ir.Parameter p in node.parameters) {
+      Variable parameter = new Variable(p.element);
+      parameters.add(parameter);
+      variables[p] = parameter;
+    }
+    function = new FunctionDefinition(parameters, visit(node.body));
+    return null;
   }
 
   Expression visitLetPrim(ir.LetPrim node) {
     // LetPrim is translated to LetVal.
-    Expression definition = node.primitive.accept(this);
+    Expression definition = visit(node.primitive);
     if (node.primitive.hasAtLeastOneUse) {
-      Variable variable = new Variable();
+      Variable variable = new Variable(null);
       variables[node.primitive] = variable;
-      return new LetVal(variable, definition, node.body.accept(this));
+      return new LetVal(variable, definition, node.body.accept(this),
+          node.primitive.hasExactlyOneUse);
+    } else if (node.primitive is ir.Constant) {
+      // TODO(kmillikin): Implement more systematic treatment of pure CPS
+      // values (e.g., as part of a shrinking reductions pass).
+      return visit(node.body);
     } else {
-      return new Sequence([definition, node.body.accept(this)]);
+      return new Sequence([definition, visit(node.body)]);
     }
   }
 
@@ -226,7 +260,7 @@
     // arise due to the representation of local control flow or due to
     // optimization.
     assert(node.continuation.hasAtMostOneUse);
-    return node.body.accept(this);
+    return visit(node.body);
   }
 
   Expression visitInvokeStatic(ir.InvokeStatic node) {
@@ -239,11 +273,12 @@
     } else {
       assert(cont.hasExactlyOneUse);
       if (cont.parameter.hasAtLeastOneUse) {
-        Variable variable = new Variable();
+        Variable variable = new Variable(null);
         variables[cont.parameter] = variable;
-        return new LetVal(variable, invoke, cont.body.accept(this));
+        return new LetVal(variable, invoke, cont.body.accept(this),
+            cont.parameter.hasExactlyOneUse);
       } else {
-        return new Sequence([invoke, cont.body.accept(this)]);
+        return new Sequence([invoke, visit(cont.body)]);
       }
     }
   }
@@ -298,15 +333,14 @@
   // enclosing binding.
   List<LetVal> environment;
 
-  Expression unname(Expression body) {
+  void unname(FunctionDefinition definition) {
     environment = <LetVal>[];
-    body = body.accept(this);
+    definition.body = visit(definition.body);
 
     // TODO(kmillikin):  Allow definitions that are not propagated.  Here,
     // this means rebuilding the binding with a recursively unnamed definition,
     // or else introducing a variable definition and an assignment.
     assert(environment.isEmpty);
-    return body;
   }
 
   Expression visitVariable(Variable node) {
@@ -326,10 +360,11 @@
     bool seenImpure = false;
     for (int i = environment.length - 1; i >= 0; --i) {
       if (environment[i].variable == node) {
-        if (!seenImpure || environment[i].definition.isPure) {
+        if ((!seenImpure || environment[i].definition.isPure)
+            && environment[i].hasExactlyOneUse) {
           // Use the definition if it is pure or if it is the first impure
           // definition (i.e., propagating past only pure expressions).
-          return environment.removeAt(i).definition.accept(this);
+          return visit(environment.removeAt(i).definition);
         }
         break;
       } else if (!environment[i].definition.isPure) {
@@ -344,18 +379,22 @@
 
   Expression visitSequence(Sequence node) {
     for (int i = 0; i < node.expressions.length; ++i) {
-      node.expressions[i] = node.expressions[i].accept(this);
+      node.expressions[i] = visit(node.expressions[i]);
     }
     return node;
   }
 
   Expression visitLetVal(LetVal node) {
     environment.add(node);
-    Expression body = node.body.accept(this);
+    Expression body = visit(node.body);
 
-    // TODO(kmillikin): Allow definitions that are not propagated.  Currently,
-    // the only bindings are anonymous intermediate values (which only have one
-    // use in the absence of optimizations) and they are not reordered.
+    if (!environment.isEmpty && environment.last == node) {
+      // The definition could not be propagated.  Residualize the let binding.
+      node.body = body;
+      environment.removeLast();
+      node.definition = visit(node.definition);
+      return node;
+    }
     assert(!environment.contains(node));
     return body;
   }
@@ -363,13 +402,13 @@
   Expression visitInvokeStatic(InvokeStatic node) {
     // Process arguments right-to-left, the opposite of evaluation order.
     for (int i = node.arguments.length - 1; i >= 0; --i) {
-      node.arguments[i] = node.arguments[i].accept(this);
+      node.arguments[i] = visit(node.arguments[i]);
     }
     return node;
   }
 
   Expression visitReturn(Return node) {
-    node.value = node.value.accept(this);
+    node.value = visit(node.value);
     return node;
   }
 
@@ -444,7 +483,7 @@
    */
   ast.FunctionExpression emit(FunctionElement element,
                               dart2js.TreeElementMapping treeElements,
-                              Expression expr) {
+                              FunctionDefinition definition) {
     // Reset the variable index.  This function is not reentrant.
     Variable.counter = 0;
     this.treeElements = treeElements;
@@ -455,27 +494,29 @@
     ast.TypeAnnotation returnType;
     if (!signature.type.returnType.isDynamic) {
       returnType =
-          signature.type.returnType.accept(typeEmitter, treeElements);
+          typeEmitter.visitType(signature.type.returnType, treeElements);
     }
 
     List<ast.VariableDefinitions> parameterList = <ast.VariableDefinitions>[];
-    signature.orderedForEachParameter((parameter) {
+    for (Variable parameter in definition.parameters) {
+      ParameterElement element = parameter.element;
+      parameter.assignIdentifier();
       ast.TypeAnnotation type;
-      if (!parameter.type.isDynamic) {
-        type = parameter.type.accept(typeEmitter, treeElements);
+      if (!element.type.isDynamic) {
+        type = typeEmitter.visitType(element.type, treeElements);
       }
       parameterList.add(new ast.VariableDefinitions(
           type,
           ast.Modifiers.EMPTY,
-          new ast.NodeList.singleton(makeIdentifier(parameter.name))));
-    });
+          new ast.NodeList.singleton(parameter.identifier)));
+    }
     ast.NodeList parameters =
         new ast.NodeList(openParen,
                          new Link<ast.Node>.fromList(parameterList),
                          closeParen,
                          ',');
 
-    ast.Node body = expr.accept(this);
+    ast.Node body = visit(definition.body);
 
     if (!variables.isEmpty) {
       // Introduce hoisted definitions for all variables.
@@ -512,7 +553,7 @@
       // Remove a final 'return null' that ends the body block.
       Link<ast.Node> nodes = (body as ast.Block).statements.nodes;
       ast.Node last;
-      for (var n in nodes) {
+      for (ast.Node n in nodes) {
         last = n;
       }
       if (last is ast.Return
@@ -532,8 +573,7 @@
    * Translate a list of arguments to an AST NodeList.
    */
   ast.NodeList translateArguments(List<Expression> args) {
-    List<ast.Expression> arguments =
-        args.map((e) => e.accept(this)).toList(growable: false);
+    List<ast.Expression> arguments = args.map(visit).toList(growable: false);
     return makeArgumentList(arguments);
   }
 
@@ -553,7 +593,7 @@
 
     addStatements(ast.Node node) {
       if (node is ast.Block) {
-        for (var n in node.statements.nodes) {
+        for (ast.Node n in node.statements.nodes) {
           statements.addLast(n);
         }
       } else if (node is ast.Expression) {
@@ -578,7 +618,7 @@
   }
 
   ast.Node visitSequence(Sequence node) {
-    return node.expressions.map((e) => e.accept(this)).reduce(concatenate);
+    return node.expressions.map(visit).reduce(concatenate);
   }
 
   ast.Node visitLetVal(LetVal node) {
@@ -586,10 +626,10 @@
     ast.Identifier identifier = node.variable.assignIdentifier();
     variables.add(identifier);
 
-    ast.Expression expression = node.definition.accept(this);
+    ast.Expression expression = visit(node.definition);
     ast.Expression assignment = makeAssignment(identifier, expression);
 
-    ast.Node rest = node.body.accept(this);
+    ast.Node rest = visit(node.body);
     return concatenate(assignment, rest);
   }
 
@@ -602,7 +642,7 @@
   }
 
   ast.Node visitReturn(Return node) {
-    ast.Expression expression = node.value.accept(this);
+    ast.Expression expression = visit(node.value);
     return new ast.Return(
         new KeywordToken(Keyword.keywords['return'], -1),
         semicolon,
@@ -632,7 +672,7 @@
 
   ast.TypeAnnotation visitType(DartType type,
                                dart2js.TreeElementMapping treeElements) {
-    return unimplemented();
+    return type.accept(this, treeElements);
   }
 
   ast.TypeAnnotation visitVoidType(VoidType type,
diff --git a/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart b/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart
index e50ca26..1730851 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_backend/tree_tracer.dart
@@ -3,26 +3,27 @@
 import 'dart:async' show EventSink;
 import '../tracer.dart';
 import 'dart_tree.dart';
-  
+
 class TreeTracer extends TracerUtil implements Visitor {
-  
   final EventSink<String> output;
-  
+
   TreeTracer(this.output);
-  
+
   Names names;
-  int stmtCounter;
-  
-  void traceGraph(String name, Expression exp) {
+  int statementCounter;
+
+  void visit(Node node) => node.accept(this);
+
+  void traceGraph(String name, FunctionDefinition function) {
     names = new Names();
-    stmtCounter = 0;
+    statementCounter = 0;
     tag("cfg", () {
       printProperty("name", name);
-      printBlock(exp);
+      printBlock(function.body);
     });
     names = null;
   }
-  
+
   void printBlock(Expression e) {
     tag("block", () {
       printProperty("name", "B0"); // Update when proper blocks exist
@@ -43,102 +44,105 @@
       });
     });
   }
-  
-  void printStmt(String def, String contents) {
+
+  void printStatement(String name, String contents) {
     int bci = 0;
     int uses = 0;
-    if (def == null) {
-      def = 'x${stmtCounter++}';
+    if (name == null) {
+      name = 'x${statementCounter++}';
     }
     addIndent();
-    add("$bci $uses $def $contents <|@\n");
+    add("$bci $uses $name $contents <|@\n");
   }
-  
+
+  visitFunctionDefinition(FunctionDefinition node) {
+  }
+
   visitVariable(Variable node) {
-    printStmt(null, "dead-use ${names.varName(node)}");
+    printStatement(null, "dead-use ${names.varName(node)}");
   }
-  
+
   visitSequence(Sequence node) {
     for (Expression e in node.expressions) {
       e.accept(this);
     }
   }
-  
+
   visitLetVal(LetVal node) {
     String name = names.varName(node.variable);
     String rhs = expr(node.definition);
-    printStmt(name, "let $name = $rhs");
+    printStatement(name, "let $name = $rhs");
     node.body.accept(this);
   }
-  
+
   visitInvokeStatic(InvokeStatic node) {
-    printStmt(null, expr(node));
+    printStatement(null, expr(node));
   }
-  
+
   visitReturn(Return node) {
-    printStmt(null, "return ${expr(node.value)}");
+    printStatement(null, "return ${expr(node.value)}");
   }
-  
+
   visitConstant(Constant node) {
-    printStmt(null, "dead-use ${node.value}");
+    printStatement(null, "dead-use ${node.value}");
   }
 
   visitNode(Node node) {}
   visitExpression(Expression node) {}
-  
+
   String expr(Expression e) {
-    return e.accept(new SubExprVisitor(names));
+    return e.accept(new ExpressionVisitor(names));
   }
-  
 }
 
-class SubExprVisitor implements Visitor<String> {
+class ExpressionVisitor extends Visitor<String> {
   Names names;
-  
-  SubExprVisitor(this.names);
-  
+
+  ExpressionVisitor(this.names);
+
   String visitVariable(Variable node) {
     return names.varName(node);
   }
+
   String visitSequence(Sequence node) {
     String exps = node.expressions.map((e) => e.accept(this)).join('; ');
     return "{ $exps }";
   }
+
   String visitLetVal(LetVal node) {
     String name = names.varName(node.variable);
     String def = node.definition.accept(this);
     String body = node.body.accept(this);
     return "(let $name = $def in $body)";
   }
+
   String visitInvokeStatic(InvokeStatic node) {
     String head = node.target.name;
     String args = node.arguments.map((e) => e.accept(this)).join(', ');
     return "$head($args)";
   }
+
   String visitReturn(Return node) {
     return "return ${node.value.accept(this)}";
   }
+
   String visitConstant(Constant node) {
     return "${node.value}";
   }
-  
-  visitNode(Node node) {}
-  visitExpression(Expression node) {}
 }
 
 /**
  * Invents (and remembers) names for Variables that do not have an associated
  * identifier.
- * 
+ *
  * In case a variable is named v0, v1, etc, it may be assigned a different
  * name to avoid clashing with a previously synthesized variable name.
  */
 class Names {
-  
   final Map<Variable, String> _names = {};
   final Set<String> _usedNames = new Set();
   int _counter = 0;
-  
+
   String varName(Variable v) {
     String name = _names[v];
     if (name == null) {
@@ -153,5 +157,4 @@
     }
     return name;
   }
-  
 }
\ No newline at end of file
diff --git a/sdk/lib/_internal/compiler/implementation/dart_types.dart b/sdk/lib/_internal/compiler/implementation/dart_types.dart
index 4177c5b..17794f9 100644
--- a/sdk/lib/_internal/compiler/implementation/dart_types.dart
+++ b/sdk/lib/_internal/compiler/implementation/dart_types.dart
@@ -1186,20 +1186,23 @@
     VoidType voidType = new VoidType(new VoidElementX(library));
     DynamicType dynamicType = new DynamicType(dynamicElement);
     dynamicElement.rawTypeCache = dynamicElement.thisTypeCache = dynamicType;
-    MoreSpecificVisitor moreSpecificVisitor =
-        new MoreSpecificVisitor(compiler, dynamicType, voidType);
-    SubtypeVisitor subtypeVisitor =
-        new SubtypeVisitor(compiler, dynamicType, voidType);
-    PotentialSubtypeVisitor potentialSubtypeVisitor =
-        new PotentialSubtypeVisitor(compiler, dynamicType, voidType);
-
-    return new Types.internal(compiler, voidType, dynamicType,
-        moreSpecificVisitor, subtypeVisitor, potentialSubtypeVisitor);
+    return new Types.internal(compiler, voidType, dynamicType);
   }
 
-  Types.internal(this.compiler, this.voidType, this.dynamicType,
-                 this.moreSpecificVisitor, this.subtypeVisitor,
-                 this.potentialSubtypeVisitor);
+  Types.internal(Compiler compiler, VoidType voidType, DynamicType dynamicType)
+      : this.compiler = compiler,
+        this.voidType = voidType,
+        this.dynamicType = dynamicType,
+        this.moreSpecificVisitor =
+          new MoreSpecificVisitor(compiler, dynamicType, voidType),
+        this.subtypeVisitor =
+          new SubtypeVisitor(compiler, dynamicType, voidType),
+        this.potentialSubtypeVisitor =
+          new PotentialSubtypeVisitor(compiler, dynamicType, voidType);
+
+  Types copy(Compiler compiler) {
+    return new Types.internal(compiler, voidType, dynamicType);
+  }
 
   /** Returns true if [t] is more specific than [s]. */
   bool isMoreSpecific(DartType t, DartType s) {
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart
index 1bcc733..74f2f1f 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_builder.dart
@@ -23,7 +23,7 @@
  * have an IR or not, depending on the language features that are used. For
  * elements that do have an IR, the tree [ast.Node]s and the [Token]s are not
  * used in the rest of the compilation. This is ensured by setting the element's
- * cached tree to [:null:] and also breaking the token stream to crash future
+ * cached tree to `null` and also breaking the token stream to crash future
  * attempts to parse.
  *
  * The type inferrer works on either IR nodes or tree nodes. The IR nodes are
@@ -32,7 +32,8 @@
  * re-implemented to work directly on the IR.
  */
 class IrBuilderTask extends CompilerTask {
-  final Map<Element, ir.Function> nodes = <Element, ir.Function>{};
+  final Map<Element, ir.FunctionDefinition> nodes =
+      <Element, ir.FunctionDefinition>{};
 
   IrBuilderTask(Compiler compiler) : super(compiler);
 
@@ -40,7 +41,7 @@
 
   bool hasIr(Element element) => nodes.containsKey(element.implementation);
 
-  ir.Function getIr(Element element) => nodes[element.implementation];
+  ir.FunctionDefinition getIr(Element element) => nodes[element.implementation];
 
   void buildNodes() {
     if (!irEnabled()) return;
@@ -54,7 +55,7 @@
           SourceFile sourceFile = elementSourceFile(element);
           IrBuilder builder =
               new IrBuilder(elementsMapping, compiler, sourceFile);
-          ir.Function function;
+          ir.FunctionDefinition function;
           ElementKind kind = element.kind;
           if (kind == ElementKind.GENERATIVE_CONSTRUCTOR) {
             // TODO(lry): build ir for constructors.
@@ -152,6 +153,7 @@
 class IrBuilder extends ResolvedVisitor<ir.Definition> {
   final SourceFile sourceFile;
   ir.Continuation returnContinuation = null;
+  List<ir.Parameter> parameters = <ir.Parameter>[];
 
   // The IR builder maintains a context, which is an expression with a hole in
   // it.  The hole represents the focus where new expressions can be added.
@@ -179,33 +181,42 @@
   ir.Expression root = null;
   ir.Expression current = null;
 
+  Map<Element, int> variableIndex = <Element, int>{};
+  List<ir.Definition> assignedVars = <ir.Definition>[];
+
   IrBuilder(TreeElements elements, Compiler compiler, this.sourceFile)
       : super(elements, compiler);
 
   /**
-   * Builds the [ir.Function] for a function element. In case the function
-   * uses features that cannot be expressed in the IR, this function returns
-   * [:null:].
+   * Builds the [ir.FunctionDefinition] for a function element. In case the
+   * function uses features that cannot be expressed in the IR, this function
+   * returns `null`.
    */
-  ir.Function buildFunction(FunctionElement functionElement) {
+  ir.FunctionDefinition buildFunction(FunctionElement functionElement) {
     return nullIfGiveup(() => buildFunctionInternal(functionElement));
   }
 
-  ir.Function buildFunctionInternal(FunctionElement functionElement) {
-    assert(invariant(functionElement, functionElement.isImplementation));
-    ast.FunctionExpression function = functionElement.parseNode(compiler);
+  ir.FunctionDefinition buildFunctionInternal(FunctionElement element) {
+    assert(invariant(element, element.isImplementation));
+    ast.FunctionExpression function = element.parseNode(compiler);
     assert(function != null);
     assert(!function.modifiers.isExternal());
     assert(elements[function] != null);
 
     returnContinuation = new ir.Continuation.retrn();
     root = current = null;
+
+    FunctionSignature signature = element.functionSignature;
+    signature.orderedForEachParameter((parameterElement) {
+      ir.Parameter parameter = new ir.Parameter(parameterElement);
+      parameters.add(parameter);
+      variableIndex[parameterElement] = assignedVars.length;
+      assignedVars.add(parameter);
+    });
+
     function.body.accept(this);
     ensureReturn(function);
-    int endPosition = function.getEndToken().charOffset;
-    int namePosition = elements[function].position().charOffset;
-    return
-        new ir.Function(endPosition, namePosition, returnContinuation, root);
+    return new ir.FunctionDefinition(returnContinuation, parameters, root);
   }
 
   ConstantSystem get constantSystem => compiler.backend.constantSystem;
@@ -226,9 +237,9 @@
   }
 
   /**
-   * Add an explicit [:return null:] for functions that don't have a return
+   * Add an explicit `return null` for functions that don't have a return
    * statement on each branch. This includes functions with an empty body,
-   * such as [:foo(){ }:].
+   * such as `foo(){ }`.
    */
   void ensureReturn(ast.FunctionExpression node) {
     if (!isOpen) return;
@@ -339,7 +350,9 @@
   }
 
   ir.Definition visitGetterSend(ast.Send node) {
-    return giveup();
+    Element element = elements[node];
+    if (!Elements.isLocal(element)) return giveup();
+    return assignedVars[variableIndex[element]];
   }
 
   ir.Definition visitOperatorSend(ast.Send node) {
@@ -385,7 +398,7 @@
       return giveup();
     }
     if (!isOpen) return null;
-    ir.Parameter v = new ir.Parameter();
+    ir.Parameter v = new ir.Parameter(null);
     ir.Continuation k = new ir.Continuation(v);
     ir.Expression invoke =
         new ir.InvokeStatic(element, selector, k, arguments);
@@ -401,11 +414,50 @@
     return giveup();
   }
 
+  ir.Definition visitSendSet(ast.SendSet node) {
+    Element element = elements[node];
+    if (!Elements.isLocal(element)) return giveup();
+    if (node.assignmentOperator.source != '=') return giveup();
+    // Exactly one argument expected for a simple assignment.
+    assert(!node.arguments.isEmpty);
+    assert(node.arguments.tail.isEmpty);
+    ir.Definition result = node.arguments.head.accept(this);
+    assignedVars[variableIndex[element]] = result;
+    return result;
+  }
+
+  ir.Definition visitVariableDefinitions(ast.VariableDefinitions node) {
+    assert(isOpen);
+    for (ast.Node definition in node.definitions.nodes) {
+      Element element = elements[definition];
+      // Definitions are either SendSets if there is an initializer, or
+      // Identifiers if there is no initializer.
+      if (definition is ast.SendSet) {
+        assert(!definition.arguments.isEmpty);
+        assert(definition.arguments.tail.isEmpty);
+        ir.Definition initialValue = definition.arguments.head.accept(this);
+        // Do not continue adding instructions if the initializer throws.
+        if (!isOpen) return null;
+        variableIndex[element] = assignedVars.length;
+        assignedVars.add(initialValue);
+      } else {
+        assert(definition is ast.Identifier);
+        // The initial value is null.
+        // TODO(kmillikin): Consider pooling constants.
+        ir.Constant constant = new ir.Constant(constantSystem.createNull());
+        add(new ir.LetPrim(constant));
+        variableIndex[element] = assignedVars.length;
+        assignedVars.add(constant);
+      }
+    }
+    return null;
+  }
+
   static final String ABORT_IRNODE_BUILDER = "IrNode builder aborted";
 
   ir.Definition giveup() => throw ABORT_IRNODE_BUILDER;
 
-  ir.Function nullIfGiveup(ir.Function action()) {
+  ir.FunctionDefinition nullIfGiveup(ir.FunctionDefinition action()) {
     try {
       return action();
     } catch(e) {
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart
index 7235ecd..78c2bb8 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_nodes.dart
@@ -7,10 +7,10 @@
 library dart2js.ir_nodes;
 
 import '../dart2jslib.dart' as dart2js show Constant;
-import '../elements/elements.dart' show FunctionElement, LibraryElement;
+import '../elements/elements.dart'
+    show FunctionElement, LibraryElement, ParameterElement;
 import 'ir_pickler.dart' show Pickler, IrConstantPool;
 import '../universe/universe.dart' show Selector, SelectorKind;
-import '../util/util.dart' show Spannable;
 
 abstract class Node {
   static int hashCount = 0;
@@ -133,7 +133,9 @@
 }
 
 class Parameter extends Primitive {
-  Parameter();
+  final ParameterElement element;
+
+  Parameter(this.element);
 
   accept(Visitor visitor) => visitor.visitParameter(this);
 }
@@ -154,32 +156,30 @@
 
 /// A function definition, consisting of parameters and a body.  The parameters
 /// include a distinguished continuation parameter.
-class Function extends Node {
-  final int endOffset;
-  final int namePosition;
-
+class FunctionDefinition extends Node {
   final Continuation returnContinuation;
+  final List<Parameter> parameters;
   final Expression body;
 
-  Function(this.endOffset, this.namePosition, this.returnContinuation,
-           this.body);
+  FunctionDefinition(this.returnContinuation, this.parameters, this.body);
 
   List<int> pickle(IrConstantPool constantPool) {
     return new Pickler(constantPool).pickle(this);
   }
 
-  accept(Visitor visitor) => visitor.visitFunction(this);
+  accept(Visitor visitor) => visitor.visitFunctionDefinition(this);
 }
 
 abstract class Visitor<T> {
+  T visit(Node node) => node.accept(this);
   // Abstract classes.
-  T visitNode(Node node) => node.accept(this);
+  T visitNode(Node node) => null;
   T visitExpression(Expression node) => visitNode(node);
   T visitDefinition(Definition node) => visitNode(node);
   T visitPrimitive(Primitive node) => visitDefinition(node);
 
   // Concrete classes.
-  T visitFunction(Function node) => visitNode(node);
+  T visitFunctionDefinition(FunctionDefinition node) => visitNode(node);
 
   T visitLetPrim(LetPrim node) => visitExpression(node);
   T visitLetCont(LetCont node) => visitExpression(node);
@@ -204,9 +204,16 @@
   String newValueName() => 'v${_valueCounter++}';
   String newContinuationName() => 'k${_continuationCounter++}';
 
-  String visitFunction(Function node) {
+  String visitFunctionDefinition(FunctionDefinition node) {
     names[node.returnContinuation] = 'return';
-    return '(Function ${node.body.accept(this)})';
+    String parameters = node.parameters
+        .map((p) {
+          String name = p.element.name;
+          names[p] = name;
+          return name;
+        })
+        .join(' ');
+    return '(FunctionDefinition ($parameters) ${node.body.accept(this)})';
   }
 
   String visitLetPrim(LetPrim expr) {
@@ -230,9 +237,8 @@
   String visitInvokeStatic(InvokeStatic expr) {
     String name = expr.target.name;
     String cont = names[expr.continuation.definition];
-    List<String> args =
-        expr.arguments.map((v) => names[v.definition]).toList(growable: false);
-    return '(InvokeStatic $name $cont ${args.join(' ')})';
+    String args = expr.arguments.map((v) => names[v.definition]).join(' ');
+    return '(InvokeStatic $name $cont $args)';
   }
 
   String visitInvokeContinuation(InvokeContinuation expr) {
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart
index 2a6c5ed..8a341a6 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_pickler.dart
@@ -28,7 +28,7 @@
  *
  * pickle   ::= int(entries) function
  *
- * function ::= int(endSourceOffset) int(namePosition) node(body)
+ * function ::= int(parameter count) {element(parameter)} node(body)
  *
  * int      ::= see [writeInt] for number encoding
  *
@@ -168,7 +168,7 @@
    */
   ByteData doubleData = new ByteData(8);
 
-  List<int> pickle(ir.Function function) {
+  List<int> pickle(ir.FunctionDefinition function) {
     data = new Uint8List(INITIAL_SIZE);
     offset = 0;
     emitted = <Object, int>{};
@@ -356,11 +356,14 @@
     }
   }
 
-  void visitFunction(ir.Function node) {
-    writeInt(node.endOffset);
-    writeInt(node.namePosition);
+  void visitFunctionDefinition(ir.FunctionDefinition node) {
     // The continuation parameter is bound in the body.
     recordForBackReference(node.returnContinuation);
+    writeInt(node.parameters.length);
+    for (var parameter in node.parameters) {
+      recordForBackReference(parameter);
+      writeElement(parameter.element);
+    }
     node.body.accept(this);
   }
 
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart
index 643d46a2..9b80f32 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_tracer.dart
@@ -2,8 +2,7 @@
 
 import 'dart:async' show EventSink;
 
-import 'ir_nodes.dart' as ir;
-import 'ir_nodes.dart' hide Function;
+import 'ir_nodes.dart' as ir hide Function;
 import '../tracer.dart';
 
 /**
@@ -11,23 +10,25 @@
  */
 const bool IR_TRACE_LET_CONT = false;
 
-class IRTracer extends TracerUtil implements Visitor {
+class IRTracer extends TracerUtil implements ir.Visitor {
   int indent = 0;
   EventSink<String> output;
 
   IRTracer(this.output);
 
-  void traceGraph(String name, ir.Function graph) {
+  visit(ir.Node node) => node.accept(this);
+
+  void traceGraph(String name, ir.FunctionDefinition graph) {
     tag("cfg", () {
       printProperty("name", name);
-      visitFunction(graph);
+      visitFunctionDefinition(graph);
     });
   }
 
   // Temporary field used during tree walk
   Names names;
 
-  visitFunction(ir.Function f) {
+  visitFunctionDefinition(ir.FunctionDefinition f) {
     names = new Names();
     BlockCollector builder = new BlockCollector(names);
     f.accept(builder);
@@ -69,13 +70,13 @@
     add("$bci $uses $resultVar $contents <|@\n");
   }
 
-  visitLetPrim(LetPrim node) {
+  visitLetPrim(ir.LetPrim node) {
     String id = names.name(node.primitive);
     printStmt(id, "LetPrim $id = ${formatPrimitive(node.primitive)}");
     node.body.accept(this);
   }
 
-  visitLetCont(LetCont node) {
+  visitLetCont(ir.LetCont node) {
     if (IR_TRACE_LET_CONT) {
       String dummy = names.name(node);
       String id = names.name(node.continuation);
@@ -84,7 +85,7 @@
     node.body.accept(this);
   }
 
-  visitInvokeStatic(InvokeStatic node) {
+  visitInvokeStatic(ir.InvokeStatic node) {
     String dummy = names.name(node);
     String callName = node.selector.name;
     String args = node.arguments.map(formatReference).join(', ');
@@ -92,23 +93,23 @@
     printStmt(dummy, "InvokeStatic $callName ($args) $kont");
   }
 
-  visitInvokeContinuation(InvokeContinuation node) {
+  visitInvokeContinuation(ir.InvokeContinuation node) {
     String dummy = names.name(node);
     String kont = formatReference(node.continuation);
     String arg = formatReference(node.argument);
     printStmt(dummy, "InvokeContinuation $kont ($arg)");
   }
 
-  String formatReference(Reference ref) {
-    Definition target = ref.definition;
-    if (target is Continuation && target.body == null) {
+  String formatReference(ir.Reference ref) {
+    ir.Definition target = ref.definition;
+    if (target is ir.Continuation && target.body == null) {
       return "return"; // Do not generate a name for the return continuation
     } else {
       return names.name(ref.definition);
     }
   }
 
-  String formatPrimitive(Primitive p) {
+  String formatPrimitive(ir.Primitive p) {
     return p.accept(this);
   }
 
@@ -116,23 +117,23 @@
     return "Constant ${node.value}";
   }
 
-  visitParameter(Parameter node) {
+  visitParameter(ir.Parameter node) {
     return "Parameter ${names.name(node)}";
   }
 
-  visitContinuation(Continuation node) {
+  visitContinuation(ir.Continuation node) {
     return "Continuation ${names.name(node)}";
   }
 
-  visitExpression(Expression e) {}
-  visitPrimitive(Primitive p) {}
-  visitDefinition(Definition d) {}
-  visitNode(Node n) {}
+  visitExpression(ir.Expression e) {}
+  visitPrimitive(ir.Primitive p) {}
+  visitDefinition(ir.Definition d) {}
+  visitNode(ir.Node n) {}
 }
 
-/** 
+/**
  * Invents (and remembers) names for Continuations, Parameters, etc.
- * The names must match the conventions used by IR Hydra, e.g. 
+ * The names must match the conventions used by IR Hydra, e.g.
  * Continuations and Functions must have names of form B### since they
  * are visualized as basic blocks.
  */
@@ -146,9 +147,9 @@
   };
 
   String prefix(x) {
-    if (x is Parameter) return 'r';
-    if (x is Continuation || x is ir.Function) return 'B';
-    if (x is Primitive) return 'v';
+    if (x is ir.Parameter) return 'r';
+    if (x is ir.Continuation || x is ir.FunctionDefinition) return 'B';
+    if (x is ir.Primitive) return 'v';
     return 'x';
   }
 
@@ -164,12 +165,12 @@
 }
 
 /**
- * A vertex in the graph visualization, used in place of basic blocks.  
+ * A vertex in the graph visualization, used in place of basic blocks.
  */
 class Block {
   String name;
-  final List<Parameter> parameters;
-  final Expression body;
+  final List<ir.Parameter> parameters;
+  final ir.Expression body;
   final List<Block> succ = <Block>[];
   final List<Block> pred = <Block>[];
 
@@ -181,15 +182,15 @@
   }
 }
 
-class BlockCollector implements Visitor {
+class BlockCollector extends ir.Visitor {
   Block entry;
-  final Map<Continuation, Block> cont2block = <Continuation, Block> {};
+  final Map<ir.Continuation, Block> cont2block = <ir.Continuation, Block> {};
   Block current_block;
 
   Names names;
   BlockCollector(this.names);
 
-  Block getBlock(Continuation c) {
+  Block getBlock(ir.Continuation c) {
     Block block = cont2block[c];
     if (block == null) {
       block = new Block(names.name(c), [c.parameter], c.body);
@@ -198,41 +199,42 @@
     return block;
   }
 
-  visitFunction(ir.Function f) {
+  visitFunctionDefinition(ir.FunctionDefinition f) {
     entry = current_block = new Block(names.name(f), [], f.body);
     f.body.accept(this);
   }
-  visitLetPrim(LetPrim exp) {
+
+  visitLetPrim(ir.LetPrim exp) {
     exp.body.accept(this);
   }
-  visitLetCont(LetCont exp) {
+
+  visitLetCont(ir.LetCont exp) {
     exp.continuation.accept(this);
     exp.body.accept(this);
   }
-  visitInvokeStatic(InvokeStatic exp) {
-    Definition target = exp.continuation.definition;
-    if (target is Continuation && target.body != null) {
+
+  visitInvokeStatic(ir.InvokeStatic exp) {
+    ir.Definition target = exp.continuation.definition;
+    if (target is ir.Continuation && target.body != null) {
       current_block.addEdgeTo(getBlock(target));
     }
   }
 
-  visitInvokeContinuation(InvokeContinuation exp) {
-    Definition target = exp.continuation.definition;
-    if (target is Continuation && target.body != null) {
+  visitInvokeContinuation(ir.InvokeContinuation exp) {
+    ir.Definition target = exp.continuation.definition;
+    if (target is ir.Continuation && target.body != null) {
       current_block.addEdgeTo(getBlock(target));
     }
   }
+
   visitConstant(ir.Constant constant) {}
-  visitParameter(Parameter p) {}
-  visitContinuation(Continuation c) {
+
+  visitParameter(ir.Parameter p) {}
+
+  visitContinuation(ir.Continuation c) {
     var old_node = current_block;
     current_block = getBlock(c);
     c.body.accept(this);
     current_block = old_node;
   }
-
-  visitPrimitive(Primitive p) {}
-  visitDefinition(Definition d) {}
-  visitExpression(Expression e) {}
-  visitNode(Node n) {}
 }
diff --git a/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart b/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart
index e560997..5cee8e8 100644
--- a/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart
+++ b/sdk/lib/_internal/compiler/implementation/ir/ir_unpickler.dart
@@ -22,8 +22,8 @@
   int index;
 
   /**
-   * This buffer is used in [readConstant] to reconstruct a double value from
-   * a sequence of bytes.
+   * This buffer is used in [readConstantValue] to reconstruct a double value
+   * from a sequence of bytes.
    */
   ByteData doubleData = new ByteData(8);
 
@@ -37,14 +37,14 @@
   ir.Expression root;
   ir.Expression current;
 
-  ir.Function unpickle(List<int> data) {
+  ir.FunctionDefinition unpickle(List<int> data) {
     this.data = data;
     offset = 0;
     int numEntries = readInt();
     unpickled = new List<Object>(numEntries);
     index = 0;
     root = current = null;
-    return readFunctionNode();
+    return readFunctionDefinition();
   }
 
   int readByte() {
@@ -122,12 +122,12 @@
     int tag = readByte();
     switch (tag) {
       case Pickles.NODE_CONSTANT:
-        ir.Definition constant = readConstantNode();
+        ir.Definition constant = readConstant();
         unpickled[index++] = constant;
         addExpression(new ir.LetPrim(constant));
         break;
       case Pickles.NODE_LET_CONT:
-        ir.Parameter parameter = new ir.Parameter();
+        ir.Parameter parameter = new ir.Parameter(null);
         ir.Continuation continuation = new ir.Continuation(parameter);
         unpickled[index++] = continuation;
         ir.Expression body = readDelimitedExpressionNode();
@@ -135,11 +135,11 @@
         addExpression(new ir.LetCont(continuation, body));
         break;
       case Pickles.NODE_INVOKE_STATIC:
-        addExpression(readInvokeStaticNode());
+        addExpression(readInvokeStatic());
         current = null;
         break;
       case Pickles.NODE_INVOKE_CONTINUATION:
-        addExpression(readInvokeContinuationNode());
+        addExpression(readInvokeContinuation());
         current = null;
         break;
       default:
@@ -174,30 +174,34 @@
   List<ir.Definition> readBackReferenceList() {
     int length = readInt();
     List<ir.Definition> result = new List<ir.Definition>(length);
-    for (int i = 0; i < length; i++) {
+    for (int i = 0; i < length; ++i) {
       result[i] = readBackReference();
     }
     return result;
   }
 
-  ir.Function readFunctionNode() {
-    int endOffset = readInt();
-    int namePosition = readInt();
+  ir.FunctionDefinition readFunctionDefinition() {
     // There is implicitly a return continuation which can be the target of
     // back references.
     ir.Continuation continuation = new ir.Continuation.retrn();
     unpickled[index++] = continuation;
 
+    int parameterCount = readInt();
+    List<ir.Parameter> parameters = new List<ir.Parameter>(parameterCount);
+    for (int i = 0; i < parameterCount; ++i) {
+      unpickled[index++] = parameters[i] = new ir.Parameter(readElement());
+    }
+
     ir.Expression body = readDelimitedExpressionNode();
-    return new ir.Function(endOffset, namePosition, continuation, body);
+    return new ir.FunctionDefinition(continuation, parameters, body);
   }
 
-  ir.Constant readConstantNode() {
-    Constant constant = readConstant();
+  ir.Constant readConstant() {
+    Constant constant = readConstantValue();
     return new ir.Constant(constant);
   }
 
-  ir.InvokeStatic readInvokeStaticNode() {
+  ir.InvokeStatic readInvokeStatic() {
     FunctionElement functionElement = readElement();
     Selector selector = readSelector();
     ir.Continuation continuation = readBackReference();
@@ -206,13 +210,13 @@
                                arguments);
   }
 
-  ir.InvokeContinuation readInvokeContinuationNode() {
+  ir.InvokeContinuation readInvokeContinuation() {
     ir.Continuation continuation = readBackReference();
     ir.Definition argument = readBackReference();
     return new ir.InvokeContinuation(continuation, argument);
   }
 
-  Constant readConstant() {
+  Constant readConstantValue() {
     int tag = readByte();
     switch(tag) {
       case Pickles.CONST_BOOL:
diff --git a/sdk/lib/_internal/compiler/implementation/js/builder.dart b/sdk/lib/_internal/compiler/implementation/js/builder.dart
index 5ae235e..31e88f1 100644
--- a/sdk/lib/_internal/compiler/implementation/js/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/builder.dart
@@ -10,30 +10,41 @@
 class JsBuilder {
   const JsBuilder();
 
-  // Parse a bit of JavaScript, and return an expression.
-  // See the MiniJsParser class.
-  // You can provide an expression or a list of expressions, which will be
-  // interpolated into the source at the '#' signs.
+  /**
+   * Parses a bit of JavaScript, and returns an expression.
+   *
+   * See the MiniJsParser class.
+   *
+   * [expression] can be an [Expression] or a list of [Expression]s, which will
+   * be interpolated into the source at the '#' signs.
+   */
   Expression call(String source, [var expression]) {
     var result = new MiniJsParser(source).expression();
     if (expression == null) return result;
 
-    List<Expression> expressions;
+    List<Node> nodes;
     if (expression is List) {
-      expressions = expression;
+      nodes = expression;
     } else {
-      expressions = <Expression>[expression];
+      nodes = <Node>[expression];
     }
-    if (expressions.length != result.interpolatedExpressions.length) {
-      throw "Unmatched number of interpolated expressions";
+    if (nodes.length != result.interpolatedNodes.length) {
+      throw 'Unmatched number of interpolated expressions given ${nodes.length}'
+          ' expected ${result.interpolatedNodes.length}';
     }
-    for (int i = 0; i < expressions.length; i++) {
-      result.interpolatedExpressions[i].value = expressions[i];
+    for (int i = 0; i < nodes.length; i++) {
+      result.interpolatedNodes[i].assign(nodes[i]);
     }
 
     return result.value;
   }
 
+  Statement statement(String source) {
+    var result = new MiniJsParser(source).statement();
+    // TODO(sra): Interpolation.
+    return result;
+  }
+
   // Parse JavaScript written in the JS foreign instruction.
   Expression parseForeignJS(String source, [var expression]) {
     // We can parse simple JS with the mini parser.  At the moment we can't
@@ -198,13 +209,27 @@
 class MiniJsParserError {
   MiniJsParserError(this.parser, this.message) { }
 
-  MiniJsParser parser;
-  String message;
+  final MiniJsParser parser;
+  final String message;
 
   String toString() {
-    var codes = new List.filled(parser.lastPosition, charCodes.$SPACE);
-    var spaces = new String.fromCharCodes(codes);
-    return "Error in MiniJsParser:\n${parser.src}\n$spaces^\n$spaces$message\n";
+    int pos = parser.lastPosition;
+
+    // Discard lines following the line containing lastPosition.
+    String src = parser.src;
+    int newlinePos = src.indexOf('\n', pos);
+    if (newlinePos >= pos) src = src.substring(0, newlinePos);
+
+    // Extract the prefix of the error line before lastPosition.
+    String line = src;
+    int lastLineStart = line.lastIndexOf('\n');
+    if (lastLineStart >= 0) line = line.substring(lastLineStart + 1);
+    String prefix = line.substring(0, pos - (src.length - line.length));
+
+    // Replace non-tabs with spaces, giving a print indent that matches the text
+    // for tabbing.
+    String spaces = prefix.replaceAll(new RegExp(r'[^\t]'), ' ');
+    return 'Error in MiniJsParser:\n${src}\n$spaces^\n$spaces$message\n';
   }
 }
 
@@ -241,13 +266,13 @@
     getToken();
   }
 
-  int lastCategory;
-  String lastToken;
-  int lastPosition;
-  int position;
-  String src;
-  final List<InterpolatedExpression> interpolatedValues =
-      <InterpolatedExpression>[];
+  int lastCategory = NONE;
+  String lastToken = null;
+  int lastPosition = 0;
+  int position = 0;
+  bool skippedNewline = false;  // skipped newline in last getToken?
+  final String src;
+  final List<InterpolatedNode> interpolatedValues = <InterpolatedNode>[];
 
   static const NONE = -1;
   static const ALPHA = 0;
@@ -265,9 +290,10 @@
   static const COMMA = 12;
   static const QUERY = 13;
   static const COLON = 14;
-  static const HASH = 15;
-  static const WHITESPACE = 16;
-  static const OTHER = 17;
+  static const SEMICOLON = 15;
+  static const HASH = 16;
+  static const WHITESPACE = 17;
+  static const OTHER = 18;
 
   // Make sure that ]] is two symbols.
   bool singleCharCategory(int category) => category >= DOT;
@@ -290,6 +316,7 @@
       case COMMA: return "COMMA";
       case QUERY: return "QUERY";
       case COLON: return "COLON";
+      case SEMICOLON: return "SEMICOLON";
       case HASH: return "HASH";
       case WHITESPACE: return "WHITESPACE";
       case OTHER: return "OTHER";
@@ -307,7 +334,7 @@
       LPAREN, RPAREN, SYMBOL, SYMBOL, COMMA, SYMBOL, DOT, SYMBOL,   // ()*+,-./
       NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC,                  // 01234
       NUMERIC, NUMERIC, NUMERIC, NUMERIC, NUMERIC,                  // 56789
-      COLON, OTHER, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER,           // :;<=>?@
+      COLON, SEMICOLON, SYMBOL, SYMBOL, SYMBOL, QUERY, OTHER,       // :;<=>?@
       ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,       // ABCDEFGH
       ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,       // IJKLMNOP
       ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA, ALPHA,       // QRSTUVWX
@@ -355,6 +382,7 @@
       position++;
       if (position >= src.length) error("Unterminated literal");
       currentCode = src.codeUnitAt(position);
+      if (currentCode == charCodes.$LF) error("Unterminated literal");
       if (currentCode == charCodes.$BACKSLASH) {
         if (++position >= src.length) error("Unterminated literal");
         int escaped = src.codeUnitAt(position);
@@ -370,10 +398,24 @@
   }
 
   void getToken() {
-    while (position < src.length &&
-           category(src.codeUnitAt(position)) == WHITESPACE) {
-      position++;
+    skippedNewline = false;
+    for (;;) {
+      if (position >= src.length) break;
+      int code = src.codeUnitAt(position);
+      //  Skip '//' style comment.
+      if (code == charCodes.$SLASH &&
+          position + 1 < src.length &&
+          src.codeUnitAt(position + 1) == charCodes.$SLASH) {
+        int nextPosition = src.indexOf('\n', position);
+        if (nextPosition == -1) nextPosition = src.length;
+        position = nextPosition;
+      } else {
+        if (category(code) != WHITESPACE) break;
+        if (code == charCodes.$LF) skippedNewline = true;
+        ++position;
+      }
     }
+
     if (position == src.length) {
       lastCategory = NONE;
       lastToken = null;
@@ -458,6 +500,21 @@
     return false;
   }
 
+  void expectSemicolon() {
+    if (acceptSemicolon()) return;
+    error('Expected SEMICOLON');
+  }
+
+  bool acceptSemicolon() {
+    // Accept semicolon or automatically inserted semicolon before close brace.
+    // Miniparser forbids other kinds of semicolon insertion.
+    if (RBRACE == lastCategory) return true;
+    if (skippedNewline) {
+      error('No automatic semicolon insertion at preceding newline');
+    }
+    return acceptCategory(SEMICOLON);
+  }
+
   bool acceptString(String string) {
     if (lastToken == string) {
       getToken();
@@ -479,6 +536,8 @@
         return new LiteralBool(false);
       } else if (last == "null") {
         return new LiteralNull();
+      } else if (last == "function") {
+        return parseFunctionExpression();
       } else {
         return new VariableUse(last);
       }
@@ -491,13 +550,12 @@
     } else if (acceptCategory(NUMERIC)) {
       return new LiteralNumber(last);
     } else if (acceptCategory(LBRACE)) {
-      expectCategory(RBRACE);
-      return new ObjectInitializer([]);
+      return parseObjectInitializer();
     } else if (acceptCategory(LSQUARE)) {
       var values = <ArrayElement>[];
       if (!acceptCategory(RSQUARE)) {
         do {
-          values.add(new ArrayElement(values.length, parseExpression()));
+          values.add(new ArrayElement(values.length, parseAssignment()));
         } while (acceptCategory(COMMA));
         expectCategory(RSQUARE);
       }
@@ -519,6 +577,57 @@
     }
   }
 
+  Expression parseFunctionExpression() {
+    String last = lastToken;
+    if (acceptCategory(ALPHA)) {
+      String functionName = last;
+      return new NamedFunction(new VariableDeclaration(functionName),
+          parseFun());
+    }
+    return parseFun();
+  }
+
+  Expression parseFun() {
+    List<Parameter> params = <Parameter>[];
+    expectCategory(LPAREN);
+    String argumentName = lastToken;
+    if (acceptCategory(ALPHA)) {
+      params.add(new Parameter(argumentName));
+      while (acceptCategory(COMMA)) {
+        argumentName = lastToken;
+        expectCategory(ALPHA);
+        params.add(new Parameter(argumentName));
+      }
+    }
+    expectCategory(RPAREN);
+    expectCategory(LBRACE);
+    Block block = parseBlock();
+    return new Fun(params, block);
+  }
+
+  Expression parseObjectInitializer() {
+    List<Property> properties = <Property>[];
+    for (;;) {
+      if (acceptCategory(RBRACE)) break;
+      // Limited subset: keys are identifiers, no 'get' or 'set' properties.
+      Literal propertyName;
+      String identifier = lastToken;
+      if (acceptCategory(ALPHA)) {
+        propertyName = new LiteralString('"$identifier"');
+      } else if (acceptCategory(STRING)) {
+        propertyName = new LiteralString(identifier);
+      } else {
+        error('Expected property name');
+      }
+      expectCategory(COLON);
+      Expression value = parseAssignment();
+      properties.add(new Property(propertyName, value));
+      if (acceptCategory(RBRACE)) break;
+      expectCategory(COMMA);
+    }
+    return new ObjectInitializer(properties);
+  }
+
   Expression parseMember() {
     Expression receiver = parsePrimary();
     while (true) {
@@ -543,7 +652,7 @@
         final arguments = <Expression>[];
         if (!acceptCategory(RPAREN)) {
           while (true) {
-            Expression argument = parseExpression();
+            Expression argument = parseAssignment();
             arguments.add(argument);
             if (acceptCategory(RPAREN)) break;
             expectCategory(COMMA);
@@ -585,9 +694,16 @@
   Expression parsePostfix() {
     Expression expression = parseCall();
     String operator = lastToken;
-    if (lastCategory == SYMBOL && (acceptString("++") || acceptString("--"))) {
+    // JavaScript grammar is:
+    //     LeftHandSideExpression [no LineTerminator here] ++
+    if (lastCategory == SYMBOL &&
+        !skippedNewline &&
+        (acceptString("++") || acceptString("--"))) {
       return new Postfix(operator, expression);
     }
+    // If we don't accept '++' or '--' due to skippedNewline a newline, no other
+    // part of the parser will accept the token and we will get an error at the
+    // whole expression level.
     return expression;
   }
 
@@ -664,23 +780,45 @@
     return lhs;
   }
 
-  Expression parseExpression() => parseAssignment();
+  Expression parseExpression() {
+    Expression expression = parseAssignment();
+    while (acceptCategory(COMMA)) {
+      Expression right = parseAssignment();
+      expression = new Binary(',', expression, right);
+    }
+    return expression;
+  }
+
+  VariableDeclarationList parseVariableDeclarationList() {
+    String firstVariable = lastToken;
+    expectCategory(ALPHA);
+    return finishVariableDeclarationList(firstVariable);
+  }
+
+  VariableDeclarationList finishVariableDeclarationList(String firstVariable) {
+    var initialization = [];
+
+    void declare(String variable) {
+      Expression initializer = null;
+      if (acceptString("=")) {
+        initializer = parseAssignment();
+      }
+      var declaration = new VariableDeclaration(variable);
+      initialization.add(new VariableInitialization(declaration, initializer));
+    }
+
+    declare(firstVariable);
+    while (acceptCategory(COMMA)) {
+      String variable = lastToken;
+      expectCategory(ALPHA);
+      declare(variable);
+    }
+    return new VariableDeclarationList(initialization);
+  }
 
   Expression parseVarDeclarationOrExpression() {
     if (acceptString("var")) {
-      var initialization = [];
-      do {
-        String variable = lastToken;
-        expectCategory(ALPHA);
-        Expression initializer = null;
-        if (acceptString("=")) {
-          initializer = parseExpression();
-        }
-        var declaration = new VariableDeclaration(variable);
-        initialization.add(
-            new VariableInitialization(declaration, initializer));
-      } while (acceptCategory(COMMA));
-      return new VariableDeclarationList(initialization);
+      return parseVariableDeclarationList();
     } else {
       return parseExpression();
     }
@@ -696,6 +834,167 @@
     }
     return expression;
   }
+
+  Statement statement() {
+    Statement statement = parseStatement();
+    if (lastCategory != NONE || position != src.length) {
+      error("Unparsed junk: ${categoryToString(lastCategory)}");
+    }
+    // TODO(sra): interpolated capture here?
+    return statement;
+  }
+
+  Block parseBlock() {
+    List<Statement> statements = <Statement>[];
+
+    while (!acceptCategory(RBRACE)) {
+      Statement statement = parseStatement();
+      statements.add(statement);
+    }
+    return new Block(statements);
+  }
+
+  Statement parseStatement() {
+    if (acceptCategory(LBRACE)) return parseBlock();
+
+    if (lastCategory == ALPHA) {
+      if (acceptString('return')) return parseReturn();
+
+      if (acceptString('throw')) return parseThrow();
+
+      if (acceptString('break')) {
+        return parseBreakOrContinue((label) => new Break(label));
+      }
+
+      if (acceptString('continue')) {
+        return parseBreakOrContinue((label) => new Continue(label));
+      }
+
+      if (acceptString('if')) return parseIfThenElse();
+
+      if (acceptString('for')) return parseFor();
+
+      if (acceptString('function')) return parseFunctionDeclaration();
+
+      if (acceptString('var')) {
+        Expression declarations = parseVariableDeclarationList();
+        expectSemicolon();
+        return new ExpressionStatement(declarations);
+      }
+
+      if (lastToken == 'case' ||
+          lastToken == 'do' ||
+          lastToken == 'while' ||
+          lastToken == 'switch' ||
+          lastToken == 'try' ||
+          lastToken == 'with') {
+        error('Not implemented in mini parser');
+      }
+    }
+
+    if (acceptCategory(HASH)) {
+      InterpolatedStatement statement = new InterpolatedStatement(null);
+      interpolatedValues.add(statement);
+      return statement;
+    }
+
+    // TODO:  label: statement
+
+    Expression expression = parseExpression();
+    expectSemicolon();
+    return new ExpressionStatement(expression);
+  }
+
+  Statement parseReturn() {
+    if (acceptSemicolon()) return new Return();
+    Expression expression = parseExpression();
+    expectSemicolon();
+    return new Return(expression);
+  }
+
+  Statement parseThrow() {
+    if (skippedNewline) error('throw expression must be on same line');
+    Expression expression = parseExpression();
+    expectSemicolon();
+    return new Throw(expression);
+  }
+
+  Statement parseBreakOrContinue(constructor) {
+    var identifier = lastToken;
+    if (!skippedNewline && acceptCategory(ALPHA)) {
+      expectSemicolon();
+      return constructor(identifier);
+    }
+    expectSemicolon();
+    return constructor(null);
+  }
+
+  Statement parseIfThenElse() {
+    expectCategory(LPAREN);
+    Expression condition = parseExpression();
+    expectCategory(RPAREN);
+    Statement thenStatement = parseStatement();
+    if (acceptString('else')) {
+      // Resolves dangling else by binding 'else' to closest 'if'.
+      Statement elseStatement = parseStatement();
+      return new If(condition, thenStatement, elseStatement);
+    } else {
+      return new If.noElse(condition, thenStatement);
+    }
+  }
+
+  Statement parseFor() {
+    // For-init-condition-increment style loops are fully supported.
+    //
+    // Only one for-in variant is currently implemented:
+    //
+    //     for (var variable in Expression) Statement
+    //
+    Statement finishFor(Expression init) {
+      Expression condition = null;
+      if (!acceptCategory(SEMICOLON)) {
+        condition = parseExpression();
+        expectCategory(SEMICOLON);
+      }
+      Expression update = null;
+      if (!acceptCategory(RPAREN)) {
+        update = parseExpression();
+        expectCategory(RPAREN);
+      }
+      Statement body = parseStatement();
+      return new For(init, condition, update, body);
+    }
+
+    expectCategory(LPAREN);
+    if (acceptCategory(SEMICOLON)) {
+      return finishFor(null);
+    }
+
+    if (acceptString('var')) {
+      String identifier = lastToken;
+      expectCategory(ALPHA);
+      if (acceptString('in')) {
+        Expression objectExpression = parseExpression();
+        expectCategory(RPAREN);
+        Statement body = parseStatement();
+        return new ForIn(js.defineVar(identifier), objectExpression, body);
+      }
+      Expression declarations = finishVariableDeclarationList(identifier);
+      expectCategory(SEMICOLON);
+      return finishFor(declarations);
+    }
+
+    Expression init = parseExpression();
+    expectCategory(SEMICOLON);
+    return finishFor(init);
+  }
+
+  Statement parseFunctionDeclaration() {
+    String name = lastToken;
+    expectCategory(ALPHA);
+    Expression fun = parseFun();
+    return new FunctionDeclaration(new VariableDeclaration(name), fun);
+  }
 }
 
 /**
@@ -754,6 +1053,10 @@
     return arguments[argumentIndex++];
   }
 
+  Node visitInterpolatedStatement(InterpolatedStatement statement) {
+    return arguments[argumentIndex++];
+  }
+
   Node visitJSExpression(JSExpression expression) {
     assert(argumentIndex == 0);
     Node result = visit(expression.value);
diff --git a/sdk/lib/_internal/compiler/implementation/js/nodes.dart b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
index 95191f0..b48c4ae 100644
--- a/sdk/lib/_internal/compiler/implementation/js/nodes.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/nodes.dart
@@ -64,6 +64,7 @@
   T visitComment(Comment node);
 
   T visitInterpolatedExpression(InterpolatedExpression node);
+  T visitInterpolatedStatement(InterpolatedStatement node);
   T visitJSExpression(JSExpression node);
 }
 
@@ -151,6 +152,8 @@
 
   T visitInterpolatedExpression(InterpolatedExpression node)
       => visitExpression(node);
+  T visitInterpolatedStatement(InterpolatedStatement node)
+      => visitStatement(node);
   T visitJSExpression(JSExpression node) => visitExpression(node);
 
   // Ignore comments by default.
@@ -702,6 +705,8 @@
         return LOGICAL_AND;
       case "||":
         return LOGICAL_OR;
+      case ',':
+        return EXPRESSION;
       default:
         throw new leg.CompilerCancelledException(
             "Internal Error: Unhandled binary operator: $op");
@@ -947,11 +952,18 @@
   }
 }
 
-class InterpolatedExpression extends Expression {
+/// Tag class for all interpolated positions.
+abstract class InterpolatedNode implements Node {}
+
+class InterpolatedExpression extends Expression implements InterpolatedNode {
   Expression value;
 
   InterpolatedExpression(this.value);
 
+  void assign(Expression newValue) {
+    value = newValue;
+  }
+
   accept(NodeVisitor visitor) => visitor.visitInterpolatedExpression(this);
 
   void visitChildren(NodeVisitor visitor) {
@@ -961,24 +973,45 @@
   int get precedenceLevel => value.precedenceLevel;
 }
 
+class InterpolatedStatement extends Statement implements InterpolatedNode {
+  Statement value;
+
+  InterpolatedStatement(this.value);
+
+  void assign(Node newValue) {
+    if (newValue is Expression)
+      value = new ExpressionStatement(newValue);
+    else
+      value = newValue;
+  }
+
+  accept(NodeVisitor visitor) => visitor.visitInterpolatedStatement(this);
+
+  void visitChildren(NodeVisitor visitor) {
+    if (value != null) value.accept(visitor);
+  }
+}
+
 class JSExpression extends Expression {
   Expression value;
-  List<InterpolatedExpression> interpolatedExpressions;
+  List<InterpolatedNode> interpolatedNodes;
 
-  JSExpression(this.value, this.interpolatedExpressions);
+  JSExpression(this.value, this.interpolatedNodes);
 
   accept(NodeVisitor visitor) => visitor.visitJSExpression(this);
 
   void visitChildren(NodeVisitor visitor) {
     value.accept(visitor);
-    for (InterpolatedExpression expression in interpolatedExpressions) {
-      expression.accept(visitor);
+    for (InterpolatedNode node in interpolatedNodes) {
+      node.accept(visitor);
     }
   }
 
   int get precedenceLevel => value.precedenceLevel;
 }
 
+// TODO(sra): JSStatement like JSExpression.
+
 /**
  * [RegExpLiteral]s, despite being called "Literal", do not inherit from
  * [Literal]. Indeed, regular expressions in JavaScript have a side-effect and
diff --git a/sdk/lib/_internal/compiler/implementation/js/printer.dart b/sdk/lib/_internal/compiler/implementation/js/printer.dart
index 855eb1d..975cbf6 100644
--- a/sdk/lib/_internal/compiler/implementation/js/printer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js/printer.dart
@@ -553,7 +553,13 @@
     String op = binary.op;
     int leftPrecedenceRequirement;
     int rightPrecedenceRequirement;
+    bool leftSpace = true;   // left<HERE>op right
     switch (op) {
+      case ',':
+        leftPrecedenceRequirement = EXPRESSION;
+        rightPrecedenceRequirement = LOGICAL_OR;
+        leftSpace = false;
+        break;
       case "||":
         leftPrecedenceRequirement = LOGICAL_OR;
         // x || (y || z) <=> (x || y) || z.
@@ -633,7 +639,7 @@
       out(op);
       out(" ");
     } else {
-      spaceOut();
+      if (leftSpace) spaceOut();
       out(op);
       spaceOut();
     }
@@ -881,6 +887,10 @@
     visit(node.value);
   }
 
+  visitInterpolatedStatement(InterpolatedStatement node) {
+    visit(node.value);
+  }
+
   void visitComment(Comment node) {
     if (shouldCompressOutput) return;
     String comment = node.comment.trim();
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
index ad0e1ff..6866b25 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/native_emitter.dart
@@ -74,25 +74,6 @@
     return backend.namer.isolateAccess(element);
   }
 
-  // The tags string contains comma-separated 'words' which are either dispatch
-  // tags (having JavaScript identifier syntax) and directives that begin with
-  // `!`.
-  List<String> nativeTagsOfClassRaw(ClassElement cls) {
-    String quotedName = cls.nativeTagInfo;
-    return quotedName.substring(1, quotedName.length - 1).split(',');
-  }
-
-  List<String> nativeTagsOfClass(ClassElement cls) {
-    return nativeTagsOfClassRaw(cls).where((s) => !s.startsWith('!')).toList();
-  }
-
-  bool nativeHasTagsMarker(ClassElement cls, String marker) {
-    return nativeTagsOfClassRaw(cls).contains(marker);
-  }
-
-  bool nativeForcedNonLeaf(ClassElement cls) =>
-      nativeHasTagsMarker(cls, '!nonleaf');
-
   /**
    * Writes the class definitions for the interceptors to [mainBuffer].
    * Writes code to associate dispatch tags with interceptors to [nativeBuffer].
@@ -185,7 +166,8 @@
       } else if (extensionPoints.containsKey(classElement)) {
         needed = true;
       }
-      if (classElement.isNative() && nativeForcedNonLeaf(classElement)) {
+      if (classElement.isNative() &&
+          native.nativeTagsForcedNonLeaf(classElement)) {
         needed = true;
         nonleafClasses.add(classElement);
       }
@@ -206,7 +188,7 @@
 
     for (ClassElement classElement in classes) {
       if (!classElement.isNative()) continue;
-      List<String> nativeTags = nativeTagsOfClass(classElement);
+      List<String> nativeTags = native.nativeTagsOfClass(classElement);
 
       if (nonleafClasses.contains(classElement) ||
           extensionPoints.containsKey(classElement)) {
diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart
index 9721322..c5dd94a 100644
--- a/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_emitter/code_emitter_task.dart
@@ -233,32 +233,32 @@
     //  },
     // });
 
-    var defineClass = js.fun(['name', 'cls', 'fields'], [
-      js('var accessors = []'),
+    var defineClass = js('''function(name, cls, fields) {
+      var accessors = [];
 
-      js('var str = "function " + cls + "("'),
-      js('var body = ""'),
+      var str = "function " + cls + "(";
+      var body = "";
 
-      js.for_('var i = 0', 'i < fields.length', 'i++', [
-        js.if_('i != 0', js('str += ", "')),
+      for (var i = 0; i < fields.length; i++) {
+        if(i != 0) str += ", ";
 
-        js('var field = generateAccessor(fields[i], accessors, cls)'),
-        js('var parameter = "parameter_" + field'),
-        js('str += parameter'),
-        js('body += ("this." + field + " = " + parameter + ";\\n")')
-      ]),
-      js('str += ") {\\n" + body + "}\\n"'),
-      js('str += cls + ".builtin\$cls=\\"" + name + "\\";\\n"'),
-      js('str += "\$desc=\$collectedClasses." + cls + ";\\n"'),
-      js('str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n"'),
-      js('str += cls + ".prototype = \$desc;\\n"'),
-      js.if_(
-          'typeof defineClass.name != "string"',
-          [js('str += cls + ".name=\\"" + cls + "\\";\\n"')]),
-      js('str += accessors.join("")'),
+        var field = generateAccessor(fields[i], accessors, cls);
+        var parameter = "parameter_" + field;
+        str += parameter;
+        body += ("this." + field + " = " + parameter + ";\\n");
+      }
+      str += ") {\\n" + body + "}\\n";
+      str += cls + ".builtin\$cls=\\"" + name + "\\";\\n";
+      str += "\$desc=\$collectedClasses." + cls + ";\\n";
+      str += "if(\$desc instanceof Array) \$desc = \$desc[1];\\n";
+      str += cls + ".prototype = \$desc;\\n";
+      if (typeof defineClass.name != "string") {
+        str += cls + ".name=\\"" + cls + "\\";\\n";
+      }
+      str += accessors.join("");
 
-      js.return_('str')
-    ]);
+      return str;
+    }''');
     // Declare a function called "generateAccessor".  This is used in
     // defineClassFunction (it's a local declaration in init()).
     return [
@@ -270,23 +270,23 @@
 
   /** Needs defineClass to be defined. */
   List buildInheritFrom() {
-    return [
-      js('var inheritFrom = #',
-          js.fun([], [
-              new jsAst.FunctionDeclaration(
-                  new jsAst.VariableDeclaration('tmp'), js.fun([], [])),
-              js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
-              js.return_(js.fun(['constructor', 'superConstructor'], [
-                  js('tmp.prototype = superConstructor.prototype'),
-                  js('var object = new tmp()'),
-                  js('var properties = constructor.prototype'),
-                  js.forIn('member', 'properties',
-                    js.if_('hasOwnProperty.call(properties, member)',
-                           js('object[member] = properties[member]'))),
-                  js('object.constructor = constructor'),
-                  js('constructor.prototype = object'),
-                  js.return_('object')
-              ]))])())];
+    return [js(r'''
+        var inheritFrom = function() {
+          function tmp() {}
+          var hasOwnProperty = Object.prototype.hasOwnProperty;
+          return function (constructor, superConstructor) {
+            tmp.prototype = superConstructor.prototype;
+            var object = new tmp();
+            var properties = constructor.prototype;
+            for (var member in properties)
+              if (hasOwnProperty.call(properties, member))
+                object[member] = properties[member];
+            object.constructor = constructor;
+            constructor.prototype = object;
+            return object;
+          };
+        }()
+      ''')];
   }
 
   jsAst.Fun get finishClassesFunction {
@@ -385,13 +385,15 @@
           js.if_('supr', js('pendingClasses[cls] = supr'))
         ])
       ]),
-      js.if_('typeof dart_precompiled != "function"',
-          [js('combinedConstructorFunction +='
-              ' "return [\\n  " + constructorsList.join(",\\n  ") + "\\n]"'),
-           js('var constructors ='
-              ' new Function("\$collectedClasses", combinedConstructorFunction)'
-              '(collectedClasses)'),
-           js('combinedConstructorFunction = null')]),
+      js.statement('''
+          if (typeof dart_precompiled != "function") {
+            combinedConstructorFunction +=
+               "return [\\n  " + constructorsList.join(",\\n  ") + "\\n]";
+             var constructors =
+              new Function("\$collectedClasses", combinedConstructorFunction)
+              (collectedClasses);
+            combinedConstructorFunction = null;
+          }'''),
       js.for_('var i = 0', 'i < constructors.length', 'i++', [
         js('var constructor = constructors[i]'),
         js('var cls = constructor.name'),
@@ -418,8 +420,9 @@
     nsmEmitter.addTrivialNsmHandlers(statements);
 
     statements.add(
-      js.forIn('cls', 'pendingClasses', js('finishClass(cls)'))
+        js.statement('for (var cls in pendingClasses) finishClass(cls);')
     );
+
     // function(collectedClasses,
     //          isolateProperties,
     //          existingIsolateProperties)
@@ -519,32 +522,34 @@
     //
     // We also copy over old values like the prototype, and the
     // isolateProperties themselves.
-    return js.fun('oldIsolate', [
-      js('var isolateProperties = oldIsolate.${namer.isolatePropertiesName}'),
-      new jsAst.FunctionDeclaration(
-        new jsAst.VariableDeclaration('Isolate'),
-          js.fun([], [
-            js('var hasOwnProperty = Object.prototype.hasOwnProperty'),
-            js.forIn('staticName', 'isolateProperties',
-              js.if_('hasOwnProperty.call(isolateProperties, staticName)',
-                js('this[staticName] = isolateProperties[staticName]'))),
-            // Use the newly created object as prototype. In Chrome,
-            // this creates a hidden class for the object and makes
-            // sure it is fast to access.
-            new jsAst.FunctionDeclaration(
-              new jsAst.VariableDeclaration('ForceEfficientMap'),
-              js.fun([], [])),
-            js('ForceEfficientMap.prototype = this'),
-            js('new ForceEfficientMap()')])),
-      js('Isolate.prototype = oldIsolate.prototype'),
-      js('Isolate.prototype.constructor = Isolate'),
-      js('Isolate.${namer.isolatePropertiesName} = isolateProperties'),
-      optional(needsDefineClass,
-               js('Isolate.$finishClassesProperty ='
-                  ' oldIsolate.$finishClassesProperty')),
-      optional(hasMakeConstantList,
-               js('Isolate.makeConstantList = oldIsolate.makeConstantList')),
-      js.return_('Isolate')]);
+    return js('''
+      function (oldIsolate) {
+        var isolateProperties = oldIsolate.${namer.isolatePropertiesName};
+        function Isolate() {
+          var hasOwnProperty = Object.prototype.hasOwnProperty;
+          for (var staticName in isolateProperties)
+            if (hasOwnProperty.call(isolateProperties, staticName))
+              this[staticName] = isolateProperties[staticName];
+          // Use the newly created object as prototype. In Chrome,
+          // this creates a hidden class for the object and makes
+          // sure it is fast to access.
+          function ForceEfficientMap() {}
+          ForceEfficientMap.prototype = this;
+          new ForceEfficientMap();
+        }
+        Isolate.prototype = oldIsolate.prototype;
+        Isolate.prototype.constructor = Isolate;
+        Isolate.${namer.isolatePropertiesName} = isolateProperties;
+        #
+        #
+        return Isolate;
+      }''',
+      [ optional(needsDefineClass,
+            js('Isolate.$finishClassesProperty ='
+               ' oldIsolate.$finishClassesProperty')),
+        optional(hasMakeConstantList,
+            js('Isolate.makeConstantList = oldIsolate.makeConstantList'))
+      ]);
   }
 
   jsAst.Fun get lazyInitializerFunction {
@@ -949,8 +954,8 @@
     int cmp2 = isConstantInlinedOrAlreadyEmitted(b) ? 0 : 1;
     if (cmp1 + cmp2 < 2) return cmp1 - cmp2;
 
-    // Emit constant interceptors first. Constant intercpetors for primitives
-    // might be used by code that builds other constants.  See Issue 19183.
+    // Emit constant interceptors first. Constant interceptors for primitives
+    // might be used by code that builds other constants.  See Issue 18173.
     if (a.isInterceptor != b.isInterceptor) {
       return a.isInterceptor ? -1 : 1;
     }
@@ -992,37 +997,38 @@
    * Emits code that sets `init.isolateTag` to a unique string.
    */
   jsAst.Expression generateIsolateAffinityTagInitialization() {
-    return js('!#', js.fun([], [
-
+    return js('''
+      !function() {
         // On V8, the 'intern' function converts a string to a symbol, which
         // makes property access much faster.
-        new jsAst.FunctionDeclaration(new jsAst.VariableDeclaration('intern'),
-            js.fun(['s'], [
-                js('var o = {}'),
-                js('o[s] = 1'),
-                js.return_(js('Object.keys(convertToFastObject(o))[0]'))])),
+        function intern(s) {
+          var o = {};
+          o[s] = 1;
+          return Object.keys(convertToFastObject(o))[0];
+        }
 
-
-        js('init.getIsolateTag = #',
-            js.fun(['name'],
-                js.return_('intern("___dart_" + name + init.isolateTag)'))),
+        init.getIsolateTag = function(name) {
+          return intern("___dart_" + name + init.isolateTag);
+        };
 
         // To ensure that different programs loaded into the same context (page)
         // use distinct dispatch properies, we place an object on `Object` to
         // contain the names already in use.
-        js('var tableProperty = "___dart_isolate_tags_"'),
-        js('var usedProperties = Object[tableProperty] ||'
-            '(Object[tableProperty] = Object.create(null))'),
+        var tableProperty = "___dart_isolate_tags_";
+        var usedProperties = Object[tableProperty] ||
+            (Object[tableProperty] = Object.create(null));
 
-        js('var rootProperty = "_${generateIsolateTagRoot()}"'),
-        js.for_('var i = 0', null, 'i++', [
-            js('var property = intern(rootProperty + "_" + i + "_")'),
-            js.if_('!(property in usedProperties)', [
-                js('usedProperties[property] = 1'),
-                js('init.isolateTag = property'),
-                new jsAst.Break(null)])])])
-        ());
-
+        var rootProperty = "_${generateIsolateTagRoot()}";
+        for (var i = 0; ; i++) {
+          var property = intern(rootProperty + "_" + i + "_");
+          if (!(property in usedProperties)) {
+            usedProperties[property] = 1;
+            init.isolateTag = property;
+            break;
+          }
+        }
+      }()
+    ''');
   }
 
   jsAst.Expression generateDispatchPropertyNameInitialization() {
@@ -1406,7 +1412,12 @@
           }
         }
         mainBuffer
-            ..write(getReflectionDataParser(classesCollector, backend))
+            ..write('(')
+            ..write(
+                jsAst.prettyPrint(
+                    getReflectionDataParser(classesCollector, backend),
+                    compiler))
+            ..write(')')
             ..write('([$n');
 
         List<Element> sortedElements =
@@ -1664,7 +1675,12 @@
                 '$_${namer.isolateName}.prototype$N$n'
                 // The classesCollector object ($$).
                 '$classesCollector$_=$_{};$n')
-        ..write(getReflectionDataParser(classesCollector, backend))
+        ..write('(')
+        ..write(
+            jsAst.prettyPrint(
+                getReflectionDataParser(classesCollector, backend),
+                compiler))
+        ..write(')')
         ..write('([$n')
         ..addBuffer(outputBuffer)
         ..write('])$N');
diff --git a/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart b/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
index ffe3b52..59eb43e 100644
--- a/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_emitter/reflection_data_parser.dart
@@ -15,8 +15,15 @@
 const bool VALIDATE_DATA = false;
 
 // TODO(ahe): This code should be integrated in CodeEmitterTask.finishClasses.
-String getReflectionDataParser(String classesCollector,
-                               JavaScriptBackend backend) {
+jsAst.Expression getReflectionDataParser(String classesCollector,
+                                        JavaScriptBackend backend) {
+  var text = getReflectionDataParserAsString(classesCollector, backend);
+  return js(text);
+}
+
+
+String getReflectionDataParserAsString(String classesCollector,
+                                       JavaScriptBackend backend) {
   Namer namer = backend.namer;
   Compiler compiler = backend.compiler;
   Element closureFromTearOff = compiler.findHelper('closureFromTearOff');
@@ -30,7 +37,7 @@
   } else {
     // Default values for mocked-up test libraries.
     tearOffAccess =
-        'function() { throw "Helper \'closureFromTearOff\' missing." }';
+        r'''function() { throw 'Helper \'closureFromTearOff\' missing.' }''';
     tearOffGlobalObjectName = 'MissingHelperFunction';
     tearOffGlobalObject = '($tearOffAccess())';
   }
@@ -47,31 +54,107 @@
   String methodsWithOptionalArgumentsField =
       namer.methodsWithOptionalArgumentsField;
 
-  String header = '''
-(function (reflectionData) {
-  "use strict";
-'''
-// [map] returns an object literal that V8 shouldn't try to optimize with a
-// hidden class. This prevents a potential performance problem where V8 tries
-// to build a hidden class for an object used as a hashMap.
-'''
-  function map(x){x={x:x};delete x.x;return x}
-''';
-
   String unmangledNameIndex = backend.mustRetainMetadata
       ? ' 3 * optionalParameterCount + 2 * requiredParameterCount + 3'
       : ' 2 * optionalParameterCount + requiredParameterCount + 3';
 
+
+  String header = '''
+(function (reflectionData) {
+  "use strict";
+
+// [map] returns an object literal that V8 shouldn't try to optimize with a
+// hidden class. This prevents a potential performance problem where V8 tries
+// to build a hidden class for an object used as a hashMap.
+
+  function map(x){x={x:x};delete x.x;return x}
+''';
+
+  String processStatics = '''
+    function processStatics(descriptor) {
+      for (var property in descriptor) {
+        if (!hasOwnProperty.call(descriptor, property)) continue;
+        if (property === "${namer.classDescriptorProperty}") continue;
+        var element = descriptor[property];
+        var firstChar = property.substring(0, 1);
+        var previousProperty;
+        if (firstChar === "+") {
+          mangledGlobalNames[previousProperty] = property.substring(1);
+          var flag = descriptor[property];
+          if (flag > 0)
+            descriptor[previousProperty].$reflectableField = flag;
+          if (element && element.length)
+            init.typeInformation[previousProperty] = element;
+        } else if (firstChar === "@") {
+          property = property.substring(1);
+          ${namer.currentIsolate}[property][$metadataField] = element;
+        } else if (firstChar === "*") {
+          globalObject[previousProperty].$defaultValuesField = element;
+          var optionalMethods = descriptor.$methodsWithOptionalArgumentsField;
+          if (!optionalMethods) {
+            descriptor.$methodsWithOptionalArgumentsField = optionalMethods = {}
+          }
+          optionalMethods[property] = previousProperty;
+        } else if (typeof element === "function") {
+          globalObject[previousProperty = property] = element;
+          functions.push(property);
+          init.globalFunctions[property] = element;
+        } else if (element.constructor === Array) {
+          addStubs(globalObject, element, property,
+                   true, descriptor, functions);
+        } else {
+          previousProperty = property;
+          var newDesc = {};
+          var previousProp;
+          for (var prop in element) {
+            if (!hasOwnProperty.call(element, prop)) continue;
+            firstChar = prop.substring(0, 1);
+            if (prop === "static") {
+              processStatics(init.statics[property] = element[prop]);
+            } else if (firstChar === "+") {
+              mangledNames[previousProp] = prop.substring(1);
+              var flag = element[prop];
+              if (flag > 0)
+                element[previousProp].$reflectableField = flag;
+            } else if (firstChar === "@" && prop !== "@") {
+              newDesc[prop.substring(1)][$metadataField] = element[prop];
+            } else if (firstChar === "*") {
+              newDesc[previousProp].$defaultValuesField = element[prop];
+              var optionalMethods = newDesc.$methodsWithOptionalArgumentsField;
+              if (!optionalMethods) {
+                newDesc.$methodsWithOptionalArgumentsField = optionalMethods={}
+              }
+              optionalMethods[prop] = previousProp;
+            } else {
+              var elem = element[prop];
+              if (prop !== "${namer.classDescriptorProperty}" &&
+                  elem != null &&
+                  elem.constructor === Array &&
+                  prop !== "<>") {
+                addStubs(newDesc, elem, prop, false, element, []);
+              } else {
+                newDesc[previousProp = prop] = elem;
+              }
+            }
+          }
+          $classesCollector[property] = [globalObject, newDesc];
+          classes.push(property);
+        }
+      }
+    }
+''';
+
+
   /**
    * See [dart2js.js_emitter.ContainerBuilder.addMemberMethod] for format of
    * [array].
    */
   String addStubs = '''
-  function addStubs(descriptor, array, name, isStatic,''' // Break long line.
-                ''' originalDescriptor, functions) {
-    var f, funcs =''' // Break long line.
-    ''' [originalDescriptor[name] =''' // Break long line.
-    ''' descriptor[name] = f = ${readFunction("array", "$FUNCTION_INDEX")}];
+  function addStubs(descriptor, array, name, isStatic,
+                    originalDescriptor, functions) {
+    var f, funcs =
+        [originalDescriptor[name] =
+        descriptor[name] = f = ${readFunction("array", "$FUNCTION_INDEX")}];
     f.\$stubName = name;
     functions.push(name);
     for (var index = $FUNCTION_INDEX; index < array.length; index += 2) {
@@ -97,8 +180,8 @@
     var optionalParameterInfo = ${readInt("array", "1")};
     var optionalParameterCount = optionalParameterInfo >> 1;
     var optionalParametersAreNamed = (optionalParameterInfo & 1) === 1;
-    var isIntercepted =''' // Break long line.
-       ''' requiredParameterCount + optionalParameterCount != funcs[0].length;
+    var isIntercepted =
+           requiredParameterCount + optionalParameterCount != funcs[0].length;
     var functionTypeIndex = ${readFunctionType("array", "2")};
     var unmangledNameIndex = $unmangledNameIndex;
     var isReflectable = array.length > unmangledNameIndex;
@@ -107,9 +190,7 @@
       f = tearOff(funcs, array, isStatic, name, isIntercepted);
       descriptor[name].\$getter = f;
       f.\$getterStub = true;
-'''
-      /* Used to create an isolate using spawnFunction.*/
-'''
+      // Used to create an isolate using spawnFunction.
       if (isStatic) init.globalFunctions[name] = f;
       originalDescriptor[getterStubName] = descriptor[getterStubName] = f;
       funcs.push(f);
@@ -125,18 +206,16 @@
       }
       var mangledNames = isStatic ? init.mangledGlobalNames : init.mangledNames;
       var unmangledName = ${readString("array", "unmangledNameIndex")};
-'''
       // The function is either a getter, a setter, or a method.
       // If it is a method, it might also have a tear-off closure.
       // The unmangledName is the same as the getter-name.
-'''
       var reflectionName = unmangledName;
       if (getterStubName) mangledNames[getterStubName] = reflectionName;
       if (isSetter) {
         reflectionName += "=";
       } else if (!isGetter) {
-        reflectionName += ":" + requiredParameterCount +''' // Break long line.
-      ''' ":" + optionalParameterCount;
+        reflectionName += ":" + requiredParameterCount +
+          ":" + optionalParameterCount;
       }
       mangledNames[name] = reflectionName;
       funcs[0].$reflectionNameField = reflectionName;
@@ -149,41 +228,41 @@
   String tearOff = '''
   function tearOffGetterNoCsp(funcs, reflectionInfo, name, isIntercepted) {
     return isIntercepted
-        ? new Function("funcs", "reflectionInfo", "name",''' // Break long line.
-                   ''' "$tearOffGlobalObjectName", "c",
+        ? new Function("funcs", "reflectionInfo", "name",
+                       "$tearOffGlobalObjectName", "c",
             "return function tearOff_" + name + (functionCounter++)+ "(x) {" +
               "if (c === null) c = $tearOffAccess(" +
                   "this, funcs, reflectionInfo, false, [x], name);" +
               "return new c(this, funcs[0], x, name);" +
             "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null)
-        : new Function("funcs", "reflectionInfo", "name",''' // Break long line.
-                   ''' "$tearOffGlobalObjectName", "c",
+        : new Function("funcs", "reflectionInfo", "name",
+                       "$tearOffGlobalObjectName", "c",
             "return function tearOff_" + name + (functionCounter++)+ "() {" +
               "if (c === null) c = $tearOffAccess(" +
                   "this, funcs, reflectionInfo, false, [], name);" +
               "return new c(this, funcs[0], null, name);" +
-            "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null)
+            "}")(funcs, reflectionInfo, name, $tearOffGlobalObject, null);
   }
   function tearOffGetterCsp(funcs, reflectionInfo, name, isIntercepted) {
     var cache = null;
     return isIntercepted
         ? function(x) {
-            if (cache === null) cache = $tearOffAccess(''' // Break long line.
-             '''this, funcs, reflectionInfo, false, [x], name);
-            return new cache(this, funcs[0], x, name)
+            if (cache === null) cache = $tearOffAccess(
+                this, funcs, reflectionInfo, false, [x], name);
+            return new cache(this, funcs[0], x, name);
           }
         : function() {
-            if (cache === null) cache = $tearOffAccess(''' // Break long line.
-             '''this, funcs, reflectionInfo, false, [], name);
-            return new cache(this, funcs[0], null, name)
-          }
+            if (cache === null) cache = $tearOffAccess(
+                this, funcs, reflectionInfo, false, [], name);
+            return new cache(this, funcs[0], null, name);
+          };
   }
   function tearOff(funcs, reflectionInfo, isStatic, name, isIntercepted) {
     var cache;
     return isStatic
         ? function() {
-            if (cache === void 0) cache = $tearOffAccess(''' // Break long line.
-             '''this, funcs, reflectionInfo, true, [], name).prototype;
+            if (cache === void 0) cache = $tearOffAccess(
+                this, funcs, reflectionInfo, true, [], name).prototype;
             return cache;
           }
         : tearOffGetter(funcs, reflectionInfo, name, isIntercepted);
@@ -211,7 +290,7 @@
   var length = reflectionData.length;
   for (var i = 0; i < length; i++) {
     var data = reflectionData[i];
-'''
+
 // [data] contains these elements:
 // 0. The library name (not unique).
 // 1. The library URI (unique).
@@ -222,7 +301,7 @@
 // library is the root library (see dart:mirrors IsolateMirror.rootLibrary).
 //
 // The entries of [data] are built in [assembleProgram] above.
-'''
+
     var name = data[0];
     var uri = data[1];
     var metadata = data[2];
@@ -234,80 +313,6 @@
     var functions = [];
 ''';
 
-  String processStatics = '''
-    function processStatics(descriptor) {
-      for (var property in descriptor) {
-        if (!hasOwnProperty.call(descriptor, property)) continue;
-        if (property === "${namer.classDescriptorProperty}") continue;
-        var element = descriptor[property];
-        var firstChar = property.substring(0, 1);
-        var previousProperty;
-        if (firstChar === "+") {
-          mangledGlobalNames[previousProperty] = property.substring(1);
-          var flag = descriptor[property];
-          if (flag > 0) ''' // Break long line.
-         '''descriptor[previousProperty].$reflectableField = flag;
-          if (element && element.length) ''' // Break long line.
-         '''init.typeInformation[previousProperty] = element;
-        } else if (firstChar === "@") {
-          property = property.substring(1);
-          ${namer.currentIsolate}[property][$metadataField] = element;
-        } else if (firstChar === "*") {
-          globalObject[previousProperty].$defaultValuesField = element;
-          var optionalMethods = descriptor.$methodsWithOptionalArgumentsField;
-          if (!optionalMethods) {
-            descriptor.$methodsWithOptionalArgumentsField = optionalMethods = {}
-          }
-          optionalMethods[property] = previousProperty;
-        } else if (typeof element === "function") {
-          globalObject[previousProperty = property] = element;
-          functions.push(property);
-          init.globalFunctions[property] = element;
-        } else if (element.constructor === Array) {
-          addStubs(globalObject, element, property, ''' // Break long line.
-                '''true, descriptor, functions);
-        } else {
-          previousProperty = property;
-          var newDesc = {};
-          var previousProp;
-          for (var prop in element) {
-            if (!hasOwnProperty.call(element, prop)) continue;
-            firstChar = prop.substring(0, 1);
-            if (prop === "static") {
-              processStatics(init.statics[property] = element[prop]);
-            } else if (firstChar === "+") {
-              mangledNames[previousProp] = prop.substring(1);
-              var flag = element[prop];
-              if (flag > 0) ''' // Break long line.
-             '''element[previousProp].$reflectableField = flag;
-            } else if (firstChar === "@" && prop !== "@") {
-              newDesc[prop.substring(1)][$metadataField] = element[prop];
-            } else if (firstChar === "*") {
-              newDesc[previousProp].$defaultValuesField = element[prop];
-              var optionalMethods = newDesc.$methodsWithOptionalArgumentsField;
-              if (!optionalMethods) {
-                newDesc.$methodsWithOptionalArgumentsField = optionalMethods={}
-              }
-              optionalMethods[prop] = previousProp;
-            } else {
-              var elem = element[prop];
-              if (prop !== "${namer.classDescriptorProperty}" &&'''
-              ''' elem != null &&''' // Break long line.
-              ''' elem.constructor === Array &&''' // Break long line.
-              ''' prop !== "<>") {
-                addStubs(newDesc, elem, prop, false, element, []);
-              } else {
-                newDesc[previousProp = prop] = elem;
-              }
-            }
-          }
-          $classesCollector[property] = [globalObject, newDesc];
-          classes.push(property);
-        }
-      }
-    }
-''';
-
 String footer = '''
     processStatics(descriptor);
     libraries.push([name, uri, classes, functions, metadata, fields, isRoot,
diff --git a/sdk/lib/_internal/compiler/implementation/native_handler.dart b/sdk/lib/_internal/compiler/implementation/native_handler.dart
index 38577c9..f5caa61 100644
--- a/sdk/lib/_internal/compiler/implementation/native_handler.dart
+++ b/sdk/lib/_internal/compiler/implementation/native_handler.dart
@@ -537,9 +537,28 @@
 
 class NativeResolutionEnqueuer extends NativeEnqueuerBase {
 
+  Map<String, ClassElement> tagOwner = new Map<String, ClassElement>();
+
   NativeResolutionEnqueuer(Enqueuer world, Compiler compiler)
     : super(world, compiler, compiler.enableNativeLiveTypeAnalysis);
 
+  void processNativeClass(ClassElement classElement) {
+    super.processNativeClass(classElement);
+
+    // Since we map from dispatch tags to classes, a dispatch tag must be used
+    // on only one native class.
+    for (String tag in nativeTagsOfClass(classElement)) {
+      ClassElement owner = tagOwner[tag];
+      if (owner != null) {
+        compiler.reportError(classElement,
+            MessageKind.GENERIC,
+            {'text': "Tag '$tag' already in use by '${owner.name}'"});
+      } else {
+        tagOwner[tag] = classElement;
+      }
+    }
+  }
+
   void logSummary(log(message)) {
     log('Resolved ${registeredClasses.length} native elements used, '
         '${unusedClasses.length} native elements dead.');
@@ -1048,6 +1067,22 @@
   return nativeTagInfo;
 }
 
+// The tags string contains comma-separated 'words' which are either dispatch
+// tags (having JavaScript identifier syntax) and directives that begin with
+// `!`.
+List<String> nativeTagsOfClassRaw(ClassElement cls) {
+  String quotedName = cls.nativeTagInfo;
+  return quotedName.substring(1, quotedName.length - 1).split(',');
+}
+
+List<String> nativeTagsOfClass(ClassElement cls) {
+  return nativeTagsOfClassRaw(cls).where((s) => !s.startsWith('!')).toList();
+}
+
+bool nativeTagsForcedNonLeaf(ClassElement cls) =>
+    nativeTagsOfClassRaw(cls).contains('!nonleaf');
+
+
 final RegExp nativeRedirectionRegExp = new RegExp(r'^[a-zA-Z][a-zA-Z_$0-9]*$');
 
 void handleSsaNative(SsaBuilder builder, Expression nativeBody) {
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart b/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart
index 8e218ae..ba3fd64 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/class_members.dart
@@ -35,7 +35,7 @@
   Map<dynamic/* Member | Element */, Set<MessageKind>> reportedMessages =
       new Map<dynamic, Set<MessageKind>>();
 
-  MembersCreator(Compiler this.compiler, ClassElement this.cls) {
+  MembersCreator(this.compiler, this.cls) {
     assert(invariant(cls, cls.isDeclaration,
         message: "Members may only be computed on declarations."));
   }
@@ -171,7 +171,7 @@
           declaredMembers[name] = new DeclaredMember(
               name, element, thisType, type, type);
         }
-      };
+      }
 
       cls.forEachLocalMember(createMember);
       if (cls.isPatched) {
diff --git a/sdk/lib/_internal/compiler/implementation/resolution/members.dart b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
index d81eb38..b687c2a 100644
--- a/sdk/lib/_internal/compiler/implementation/resolution/members.dart
+++ b/sdk/lib/_internal/compiler/implementation/resolution/members.dart
@@ -472,7 +472,7 @@
         }
       }
       element.parseNode(compiler);
-      element.computeSignature(compiler);
+      element.computeType(compiler);
       if (element.isPatched) {
         FunctionElementX patch = element.patch;
         compiler.withCurrentElement(patch, () {
diff --git a/sdk/lib/_internal/compiler/implementation/scanner/parser.dart b/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
index e28b110..2b82c5e 100644
--- a/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
+++ b/sdk/lib/_internal/compiler/implementation/scanner/parser.dart
@@ -1941,6 +1941,8 @@
   }
 
   Token parseLiteralString(Token token) {
+    bool old = mayParseFunctionExpressions;
+    mayParseFunctionExpressions = true;
     token = parseSingleLiteralString(token);
     int count = 1;
     while (identical(token.kind, STRING_TOKEN)) {
@@ -1950,6 +1952,7 @@
     if (count > 1) {
       listener.handleStringJuxtaposition(count);
     }
+    mayParseFunctionExpressions = old;
     return token;
   }
 
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart b/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart
index ecfd632..f3c3098 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/interceptor_simplifier.dart
@@ -64,29 +64,24 @@
     return false;
   }
 
-  bool canUseSelfForInterceptor(HInstruction instruction,
+  bool canUseSelfForInterceptor(HInstruction receiver,
                                 Set<ClassElement> interceptedClasses) {
     JavaScriptBackend backend = compiler.backend;
-    if (instruction.canBePrimitive(compiler)) {
+    if (receiver.canBePrimitive(compiler)) {
       // Primitives always need interceptors.
       return false;
     }
-    if (instruction.canBeNull()
-        && interceptedClasses.contains(backend.jsNullClass)) {
+    if (receiver.canBeNull() &&
+        interceptedClasses.contains(backend.jsNullClass)) {
       // Need the JSNull interceptor.
       return false;
     }
 
-    // [interceptedClasses] is sparse - it is just the classes that define some
-    // intercepted method.  Their subclasses (that inherit the method) are
-    // implicit, so we have to extend them.
-    TypeMask receiverType = instruction.instructionType;
-    return interceptedClasses
-        .where((cls) => cls != compiler.objectClass)
-        .map((cls) => backend.classesMixedIntoInterceptedClasses.contains(cls)
-            ? new TypeMask.subtype(cls)
-            : new TypeMask.subclass(cls))
-        .every((mask) => receiverType.intersection(mask, compiler).isEmpty);
+    // All intercepted classes extend `Interceptor`, so if the receiver can't be
+    // a class extending `Interceptor` then it can be called directly.
+    return new TypeMask.nonNullSubclass(backend.jsInterceptorClass)
+        .intersection(receiver.instructionType, compiler)
+        .isEmpty;
   }
 
   HInstruction tryComputeConstantInterceptor(
@@ -115,9 +110,9 @@
       constantInterceptor = backend.jsStringClass;
     } else if (input.isArray(compiler)) {
       constantInterceptor = backend.jsArrayClass;
-    } else if (input.isNumber(compiler)
-        && !interceptedClasses.contains(backend.jsIntClass)
-        && !interceptedClasses.contains(backend.jsDoubleClass)) {
+    } else if (input.isNumber(compiler) &&
+        !interceptedClasses.contains(backend.jsIntClass) &&
+        !interceptedClasses.contains(backend.jsDoubleClass)) {
       // If the method being intercepted is not defined in [int] or [double] we
       // can safely use the number interceptor.  This is because none of the
       // [int] or [double] methods are called from a method defined on [num].
@@ -144,8 +139,8 @@
 
     // If we just happen to be in an instance method of the constant
     // interceptor, `this` is a shorter alias.
-    if (constantInterceptor == work.element.getEnclosingClass()
-        && graph.thisInstruction != null) {
+    if (constantInterceptor == work.element.getEnclosingClass() &&
+        graph.thisInstruction != null) {
       return graph.thisInstruction;
     }
 
@@ -183,9 +178,9 @@
 
       // If we found that we need number, we must still go through all
       // uses to check if they require int, or double.
-      if (interceptedClasses.contains(backend.jsNumberClass)
-          && !(interceptedClasses.contains(backend.jsDoubleClass)
-               || interceptedClasses.contains(backend.jsIntClass))) {
+      if (interceptedClasses.contains(backend.jsNumberClass) &&
+          !(interceptedClasses.contains(backend.jsDoubleClass) ||
+            interceptedClasses.contains(backend.jsIntClass))) {
         for (HInstruction user in node.usedBy) {
           if (user is! HInvoke) continue;
           Set<ClassElement> intercepted =
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
index 3cdfa57..633a2db 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/optimize.dart
@@ -839,6 +839,14 @@
     if (input.isConstant()) {
       HConstant constant = input;
       if (!constant.constant.isPrimitive) return node;
+      if (constant.constant.isInt) {
+        // Only constant-fold int.toString() when Dart and JS results the same.
+        // TODO(18103): We should be able to remove this work-around when issue
+        // 18103 is resolved by providing the correct string.
+        IntConstant intConstant = constant.constant;
+        // Very conservative range.
+        if (!intConstant.isUInt32()) return node;
+      }
       PrimitiveConstant primitive = constant.constant;
       return graph.addConstant(constantSystem.createString(
           primitive.toDartString()), compiler);
diff --git a/sdk/lib/_internal/compiler/implementation/tracer.dart b/sdk/lib/_internal/compiler/implementation/tracer.dart
index 0ffc3d6..65603c9 100644
--- a/sdk/lib/_internal/compiler/implementation/tracer.dart
+++ b/sdk/lib/_internal/compiler/implementation/tracer.dart
@@ -22,7 +22,7 @@
 
 /**
  * Dumps the intermediate representation after each phase in a format
- * readable by Hydra IR. 
+ * readable by IR Hydra.
  */
 class Tracer extends TracerUtil {
   Compiler compiler;
@@ -30,10 +30,10 @@
   bool traceActive = false;
   final EventSink<String> output;
   final bool enabled = GENERATE_TRACE;
-  
+
   Tracer(api.CompilerOutputProvider outputProvider) :
     output = GENERATE_TRACE ? outputProvider('dart', 'cfg') : null;
-  
+
   void traceCompilation(String methodName,
                         ItemCompilationContext compilationContext,
                         Compiler compiler) {
@@ -55,14 +55,14 @@
     if (irObject is ssa.HGraph) {
       new HTracer(output, compiler, context).traceGraph(name, irObject);
     }
-    else if (irObject is ir.Function) {
+    else if (irObject is ir.FunctionDefinition) {
       new IRTracer(output).traceGraph(name, irObject);
     }
-    else if (irObject is tree.Expression) {
+    else if (irObject is tree.FunctionDefinition) {
       new TreeTracer(output).traceGraph(name, irObject);
     }
   }
-  
+
   void close() {
     if (output != null) {
       output.close();
@@ -74,7 +74,7 @@
 abstract class TracerUtil {
   int indent = 0;
   EventSink<String> get output;
-  
+
 
   void tag(String tagName, Function f) {
     println("begin_$tagName");
@@ -89,11 +89,11 @@
     add(string);
     add("\n");
   }
-  
+
   void printEmptyProperty(String propertyName) {
     println(propertyName);
   }
-  
+
   String formatPrty(x) {
     if (x is num) {
       return '${x}';
@@ -105,7 +105,7 @@
       throw "invalid property type: ${x}";
     }
   }
-  
+
   void printProperty(String propertyName, value) {
     println("$propertyName ${formatPrty(value)}");
   }
diff --git a/sdk/lib/_internal/compiler/implementation/use_unused_api.dart b/sdk/lib/_internal/compiler/implementation/use_unused_api.dart
index aea9845..ea458d7 100644
--- a/sdk/lib/_internal/compiler/implementation/use_unused_api.dart
+++ b/sdk/lib/_internal/compiler/implementation/use_unused_api.dart
@@ -196,6 +196,7 @@
   compiler.importHelperLibrary(null);
   typeGraphInferrer.getCallersOf(null);
   dart_types.Types.sorted(null);
+  new dart_types.Types(compiler, null).copy(compiler);
   new universe.TypedSelector.subclass(null, null);
   new universe.TypedSelector.subtype(null, null);
   new universe.TypedSelector.exact(null, null);
@@ -218,7 +219,7 @@
     ..visitContinuation(null)
     ..visitDefinition(null)
     ..visitExpression(null)
-    ..visitFunction(null)
+    ..visitFunctionDefinition(null)
     ..visitInvokeStatic(null)
     ..visitLetCont(null)
     ..visitNode(null)
diff --git a/sdk/lib/_internal/lib/async_patch.dart b/sdk/lib/_internal/lib/async_patch.dart
index aa1c342..c67b3ff 100644
--- a/sdk/lib/_internal/lib/async_patch.dart
+++ b/sdk/lib/_internal/lib/async_patch.dart
@@ -8,7 +8,13 @@
     Primitives,
     convertDartClosureToJS,
     loadDeferredLibrary;
-import 'dart:_isolate_helper' show TimerImpl;
+import 'dart:_isolate_helper' show
+    IsolateNatives,
+    TimerImpl,
+    leaveJsAsync,
+    enterJsAsync,
+    isWorker,
+    globalThis;
 
 import 'dart:_foreign_helper' show JS;
 
@@ -27,7 +33,34 @@
 
 patch class _AsyncRun {
   patch static void _scheduleImmediate(void callback()) {
+    scheduleImmediateClosure(callback);
+  }
+
+  // Lazily initialized.
+  static final Function scheduleImmediateClosure =
+      _initializeScheduleImmediate();
+
+  static Function _initializeScheduleImmediate() {
+    if (JS('', '#.scheduleImmediate', globalThis) != null) {
+      return _scheduleImmediateJsOverride;
+    }
     // TODO(9002): don't use the Timer to enqueue the immediate callback.
+    // Also check for other JS options like mutation observer or runImmediate.
+    return _scheduleImmediateWithTimer;
+  }
+
+  static void _scheduleImmediateJsOverride(void callback()) {
+    internalCallback() {
+      leaveJsAsync();
+      callback();
+    };
+    enterJsAsync();
+    JS('void', '#.scheduleImmediate(#)',
+       globalThis,
+       convertDartClosureToJS(internalCallback, 0));
+  }
+
+  static void _scheduleImmediateWithTimer(void callback()) {
     _createTimer(Duration.ZERO, callback);
   }
 }
diff --git a/sdk/lib/_internal/lib/isolate_helper.dart b/sdk/lib/_internal/lib/isolate_helper.dart
index 8a19dc68..9245d07 100644
--- a/sdk/lib/_internal/lib/isolate_helper.dart
+++ b/sdk/lib/_internal/lib/isolate_helper.dart
@@ -280,6 +280,16 @@
   // Container with the "on exit" handler send-ports.
   var doneHandlers;
 
+  /**
+   * Queue of functions to call when the current event is complete.
+   *
+   * These events are not just put at the front of the event queue, because
+   * they represent control messages, and should be handled even if the
+   * event queue is paused.
+   */
+  var _scheduledControlEvents;
+  bool _isExecutingEvent = false;
+
   /** Whether errors are considered fatal. */
   // This doesn't do anything yet. We need to be able to catch uncaught errors
   // (oxymoronically) in order to take lethal action. This is waiting for the
@@ -332,15 +342,41 @@
   }
 
   void handlePing(SendPort responsePort, int pingType) {
-    if (pingType == Isolate.PING_EVENT) {
-      _globalState.topEventLoop.enqueue(this, () {
-        responsePort.send(null);
-      }, "ping");
-    } else {
-      // There is no difference between PING_ALIVE and PING_CONTROL
-      // since we don't handle it before the control event queue.
+    if (pingType == Isolate.IMMEDIATE ||
+        (pingType == Isolate.BEFORE_NEXT_EVENT &&
+         !_isExecutingEvent)) {
       responsePort.send(null);
+      return;
     }
+    void respond() { responsePort.send(null); }
+    if (pingType == Isolate.AS_EVENT) {
+      _globalState.topEventLoop.enqueue(this, respond, "ping");
+      return;
+    }
+    assert(pingType == Isolate.BEFORE_NEXT_EVENT);
+    if (_scheduledControlEvents == null) {
+      _scheduledControlEvents = new Queue();
+    }
+    _scheduledControlEvents.addLast(respond);
+  }
+
+  void handleKill(Capability authentification, int priority) {
+    if (this.terminateCapability != authentification) return;
+    if (priority == Isolate.IMMEDIATE ||
+        (priority == Isolate.BEFORE_NEXT_EVENT &&
+         !_isExecutingEvent)) {
+      kill();
+      return;
+    }
+    if (priority == Isolate.AS_EVENT) {
+      _globalState.topEventLoop.enqueue(this, kill, "kill");
+      return;
+    }
+    assert(priority == Isolate.BEFORE_NEXT_EVENT);
+    if (_scheduledControlEvents == null) {
+      _scheduledControlEvents = new Queue();
+    }
+    _scheduledControlEvents.addLast(kill);
   }
 
   /**
@@ -351,11 +387,18 @@
     _globalState.currentContext = this;
     this._setGlobals();
     var result = null;
+    _isExecutingEvent = true;
     try {
       result = code();
     } finally {
+      _isExecutingEvent = false;
       _globalState.currentContext = old;
       if (old != null) old._setGlobals();
+      if (_scheduledControlEvents != null) {
+        while (_scheduledControlEvents.isNotEmpty) {
+          (_scheduledControlEvents.removeFirst())();
+        }
+      }
     }
     return result;
   }
@@ -364,6 +407,13 @@
     JS_SET_CURRENT_ISOLATE(isolateStatics);
   }
 
+  /**
+   * Handle messages comming in on the control port.
+   *
+   * These events do not go through the event queue.
+   * The `_globalState.currentContext` context is not set to this context
+   * during the handling.
+   */
   void handleControlMessage(message) {
     switch (message[0]) {
       case "pause":
@@ -384,8 +434,10 @@
       case "ping":
         handlePing(message[1], message[2]);
         break;
+      case "kill":
+        handleKill(message[1], message[2]);
+        break;
       default:
-        print("UNKNOWN MESSAGE: $message");
     }
   }
 
@@ -419,18 +471,30 @@
     if (ports.length - weakPorts.length > 0 || isPaused) {
       _globalState.isolates[id] = this; // indicate this isolate is active
     } else {
-      _shutdown();
+      kill();
     }
   }
 
-  void _shutdown() {
+  void kill() {
+    if (_scheduledControlEvents != null) {
+      // Kill all pending events.
+      _scheduledControlEvents.clear();
+    }
+    // Stop listening on all ports.
+    // This should happen before sending events to done handlers, in case
+    // we are listening on ourselves.
+    // Closes all ports, including control port.
+    for (var port in ports.values) {
+      port._close();
+    }
+    ports.clear();
+    weakPorts.clear();
     _globalState.isolates.remove(id); // indicate this isolate is not active
-    // Send "done" event to all listeners. This must be done after deactivating
-    // the current isolate, or it may get events if listening to itself.
     if (doneHandlers != null) {
       for (SendPort port in doneHandlers) {
         port.send(null);
       }
+      doneHandlers = null;
     }
   }
 
@@ -1046,6 +1110,13 @@
     _handler = newHandler;
   }
 
+  // Close the port without unregistering it.
+  // Used by an isolate context to close all ports when shutting down.
+  void _close() {
+    _isClosed = true;
+    _handler = null;
+  }
+
   void close() {
     if (_isClosed) return;
     _isClosed = true;
diff --git a/sdk/lib/_internal/lib/native_helper.dart b/sdk/lib/_internal/lib/native_helper.dart
index 6608481..3d7e17b 100644
--- a/sdk/lib/_internal/lib/native_helper.dart
+++ b/sdk/lib/_internal/lib/native_helper.dart
@@ -608,6 +608,7 @@
     "BeforeUnloadEvent": "Event",
     "DataTransfer": "Clipboard",
     "GeoGeolocation": "Geolocation",
+    "Location": "!Location",               // Fixes issue 18151
     "WorkerMessageEvent": "MessageEvent",
     "XMLDocument": "!Document"};
 
diff --git a/sdk/lib/_internal/lib/preambles/d8.js b/sdk/lib/_internal/lib/preambles/d8.js
index 2b20e49..e965083 100644
--- a/sdk/lib/_internal/lib/preambles/d8.js
+++ b/sdk/lib/_internal/lib/preambles/d8.js
@@ -3,3 +3,258 @@
 // BSD-style license that can be found in the LICENSE file.
 
 // Javascript preamble, that lets the output of dart2js run on V8's d8 shell.
+
+var setTimeout;
+var clearTimeout;
+var setInterval;
+var clearInterval;
+var dartMainRunner;
+var scheduleImmediate;
+
+(function() {
+  // Event loop.
+
+  // Task queue as cyclic list queue.
+  var taskQueue = new Array(8);  // Length is power of 2.
+  var head = 0;
+  var tail = 0;
+  var mask = taskQueue.length - 1;
+  function addTask(elem) {
+    taskQueue[head] = elem;
+    head = (head + 1) & mask;
+    if (head == tail) _growTaskQueue();
+  }
+  function removeTask() {
+    if (head == tail) return;
+    var result = taskQueue[tail];
+    taskQueue[tail] = undefined;
+    tail = (tail + 1) & mask;
+    return result;
+  }
+  function _growTaskQueue() {
+    // head == tail.
+    var length = taskQueue.length;
+    var split = head;
+    taskQueue.length = length * 2;
+    if (split * 2 < length) {  // split < length / 2
+      for (var i = 0; i < split; i++) {
+        taskQueue[length + i] = taskQueue[i];
+        taskQueue[i] = undefined;
+      }
+      head += length;
+    } else {
+      for (var i = split; i < length; i++) {
+        taskQueue[length + i] = taskQueue[i];
+        taskQueue[i] = undefined;
+      }
+      tail += length;
+    }
+    mask = taskQueue.length - 1;
+  }
+
+  // Mapping from timer id to timer function.
+  // The timer id is written on the function as .$timerId.
+  // That field is cleared when the timer is cancelled, but it is not returned
+  // from the queue until its time comes.
+  var timerIds = {};
+  var timerIdCounter = 1;  // Counter used to assing ids.
+
+  // Zero-timer queue as simple array queue using push/shift.
+  var zeroTimerQueue = [];
+
+  function addTimer(f, ms) {
+    var id = timerIdCounter++;
+    f.$timerId = id;
+    timerIds[id] = f;
+    if (ms == 0) {
+      zeroTimerQueue.push(f);
+    } else {
+      addDelayedTimer(f, ms);
+    }
+    return id;
+  }
+
+  function nextZeroTimer() {
+    while (zeroTimerQueue.length > 0) {
+      var action = zeroTimerQueue.shift();
+      if (action.$timerId !== undefined) return action;
+    }
+  }
+
+  function nextEvent() {
+    var action = removeTask();
+    if (action) {
+      return action;
+    }
+    do {
+      action = nextZeroTimer();
+      if (action) break;
+      var nextList = nextDelayedTimerQueue();
+      if (!nextList) {
+        return;
+      }
+      var newTime = nextList.shift();
+      advanceTimeTo(newTime);
+      zeroTimerQueue = nextList;
+    } while (true)
+    var id = action.$timerId;
+    clearTimerId(action, id);
+    return action;
+  }
+
+  // Mocking time.
+  var timeOffset = 0;
+  var now = function() {
+    // Install the mock Date object only once.
+    // Following calls to "now" will just use the new (mocked) Date.now
+    // method directly.
+    installMockDate();
+    now = Date.now;
+    return Date.now();
+  };
+  var originalDate = Date;
+  var originalNow = originalDate.now;
+  function advanceTimeTo(time) {
+    timeOffset = time - originalNow();
+  }
+  function installMockDate() {
+    var NewDate = function Date(Y, M, D, h, m, s, ms) {
+      if (this instanceof Date) {
+        // Assume a construct call.
+        switch (arguments.length) {
+          case 0:  return new originalDate(originalNow() + timeOffset);
+          case 1:  return new originalDate(Y);
+          case 2:  return new originalDate(Y, M);
+          case 3:  return new originalDate(Y, M, D);
+          case 4:  return new originalDate(Y, M, D, h);
+          case 5:  return new originalDate(Y, M, D, h, m);
+          case 6:  return new originalDate(Y, M, D, h, m, s);
+          default: return new originalDate(Y, M, D, h, m, s, ms);
+        }
+      }
+      return new originalDate(originalNow() + timeOffset).toString();
+    };
+    NewDate.UTC = originalDate.UTC;
+    NewDate.parse = originalDate.parse;
+    NewDate.now = function now() { return originalNow() + timeOffset; };
+    NewDate.prototype = originalDate.prototype;
+    originalDate.prototype.constructor = NewDate;
+    Date = NewDate;
+  }
+
+  // Heap priority queue with key index.
+  // Each entry is list of [timeout, callback1 ... callbackn].
+  var timerHeap = [];
+  var timerIndex = {};
+  function addDelayedTimer(f, ms) {
+    var timeout = now() + ms;
+    var timerList = timerIndex[timeout];
+    if (timerList == null) {
+      timerList = [timeout, f];
+      timerIndex[timeout] = timerList;
+      var index = timerHeap.length;
+      timerHeap.length += 1;
+      bubbleUp(index, timeout, timerList);
+    } else {
+      timerList.push(f);
+    }
+  }
+
+  function nextDelayedTimerQueue() {
+    if (timerHeap.length == 0) return null;
+    var result = timerHeap[0];
+    var last = timerHeap.pop();
+    if (timerHeap.length > 0) {
+      bubbleDown(0, last[0], last);
+    }
+    return result;
+  }
+
+  function bubbleUp(index, key, value) {
+    while (index != 0) {
+      var parentIndex = (index - 1) >> 1;
+      var parent = timerHeap[parentIndex];
+      var parentKey = parent[0];
+      if (key > parentKey) break;
+      timerHeap[index] = parent;
+      index = parentIndex;
+    }
+    timerHeap[index] = value;
+  }
+
+  function bubbleDown(index, key, value) {
+    while (true) {
+      var leftChildIndex = index * 2 + 1;
+      if (leftChildIndex >= timerHeap.length) break;
+      var minChildIndex = leftChildIndex;
+      var minChild = timerHeap[leftChildIndex];
+      var minChildKey = minChild[0];
+      var rightChildIndex = leftChildIndex + 1;
+      if (rightChildIndex < timerHeap.length) {
+        var rightChild = timerHeap[rightChildIndex];
+        var rightKey = rightChild[0];
+        if (rightKey < minChildKey) {
+          minChildIndex = rightChildIndex;
+          minChild = rightChild;
+          minChildKey = rightKey;
+        }
+      }
+      if (minChildKey > key) break;
+      timerHeap[index] = minChild;
+      index = minChildIndex;
+    }
+    timerHeap[index] = value;
+  }
+
+  function addInterval(f, ms) {
+    var id = timerIdCounter++;
+    function repeat() {
+      // Reactivate with the same id.
+      repeat.$timerId = id;
+      timerIds[id] = repeat;
+      addDelayedTimer(repeat, ms);
+      f();
+    }
+    repeat.$timerId = id;
+    timerIds[id] = repeat;
+    addDelayedTimer(repeat, ms);
+    return id;
+  }
+
+  function cancelTimer(id) {
+    var f = timerIds[id];
+    if (f == null) return;
+    clearTimerId(f, id);
+  }
+
+  function clearTimerId(f, id) {
+    f.$timerId = undefined;
+    delete timerIds[id];
+  }
+
+  function eventLoop(action) {
+    while (action) {
+      try {
+        action();
+      } catch (e) {
+        if (typeof onerror == "function") {
+          onerror(e, null, -1);
+        } else {
+          throw e;
+        }
+      }
+      action = nextEvent();
+    }
+  }
+
+  dartMainRunner = function(main, args) {
+    // Initialize.
+    var action = function() { main(args); }
+    eventLoop(action);
+  };
+  setTimeout = addTimer;
+  clearTimeout = cancelTimer;
+  setInterval = addInterval;
+  clearInterval = cancelTimer;
+  scheduleImmediate = addTask;
+})();
diff --git a/sdk/lib/_internal/libraries.dart b/sdk/lib/_internal/libraries.dart
index cb6e7b2..8df3421 100644
--- a/sdk/lib/_internal/libraries.dart
+++ b/sdk/lib/_internal/libraries.dart
@@ -94,6 +94,10 @@
       maturity: Maturity.UNSTABLE,
       dart2jsPatchPath: "_internal/lib/mirrors_patch.dart"),
 
+  "profiler": const LibraryInfo(
+      "profiler/profiler.dart",
+      maturity: Maturity.UNSTABLE),
+
   "nativewrappers": const LibraryInfo(
       "html/dartium/nativewrappers.dart",
       category: "Client",
diff --git a/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart b/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart
index cc5e052..38e2ff1 100644
--- a/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart
+++ b/sdk/lib/_internal/pub/asset/dart/serialize/transform.dart
@@ -15,20 +15,19 @@
 import '../serialize.dart';
 import '../utils.dart';
 
-/// Converts [transform] into a serializable map.
-Map serializeTransform(Transform transform) {
+/// Serialize the methods shared between [Transform] and [DeclaringTransform].
+///
+/// [additionalFields] contains additional serialized fields to add to the
+/// serialized transform. [methodHandlers] is a set of additional methods. Each
+/// value should take a JSON message and return the response (which may be a
+/// Future).
+Map _serializeBaseTransform(transform, Map additionalFields,
+    Map<String, Function> methodHandlers) {
   var receivePort = new ReceivePort();
   receivePort.listen((wrappedMessage) {
     respond(wrappedMessage, (message) {
-      if (message['type'] == 'getInput') {
-        return transform.getInput(deserializeId(message['id']))
-            .then((asset) => serializeAsset(asset));
-      }
-
-      if (message['type'] == 'addOutput') {
-        transform.addOutput(deserializeAsset(message['output']));
-        return null;
-      }
+      var handler = methodHandlers[message['type']];
+      if (handler != null) return handler(message);
 
       if (message['type'] == 'consumePrimary') {
         transform.consumePrimary();
@@ -36,17 +35,13 @@
       }
 
       assert(message['type'] == 'log');
-      var method;
-      if (message['level'] == 'Info') {
-        method = transform.logger.info;
-      } else if (message['level'] == 'Fine') {
-        method = transform.logger.fine;
-      } else if (message['level'] == 'Warning') {
-        method = transform.logger.warning;
-      } else {
-        assert(message['level'] == 'Error');
-        method = transform.logger.error;
-      }
+      var method = {
+        'Info': transform.logger.info,
+        'Fine': transform.logger.fine,
+        'Warning': transform.logger.warning,
+        'Error': transform.logger.error
+      }[message['level']];
+      assert(method != null);
 
       var assetId = message['assetId'] == null ? null :
         deserializeId(message['assetId']);
@@ -56,30 +51,43 @@
     });
   });
 
-  return {
-    'port': receivePort.sendPort,
-    'primaryInput': serializeAsset(transform.primaryInput)
-  };
+  return {'port': receivePort.sendPort}..addAll(additionalFields);
 }
 
-/// A wrapper for a [Transform] that's in the host isolate.
-///
-/// This retrieves inputs from and sends outputs and logs to the host isolate.
-class ForeignTransform implements Transform {
+/// Converts [transform] into a serializable map.
+Map serializeTransform(Transform transform) {
+  return _serializeBaseTransform(transform, {
+    'primaryInput': serializeAsset(transform.primaryInput)
+  }, {
+    'getInput': (message) => transform.getInput(deserializeId(message['id']))
+        .then((asset) => serializeAsset(asset)),
+    'addOutput': (message) =>
+        transform.addOutput(deserializeAsset(message['output']))
+  });
+}
+
+/// Converts [transform] into a serializable map.
+Map serializeDeclaringTransform(DeclaringTransform transform) {
+  return _serializeBaseTransform(transform, {
+    'primaryId': serializeId(transform.primaryId)
+  }, {
+    'declareOutput': (message) =>
+        transform.declareOutput(deserializeId(message['output']))
+  });
+}
+
+/// The base class for wrappers for [Transform]s that are in the host isolate.
+class _ForeignBaseTransform {
   /// The port with which we communicate with the host isolate.
   ///
   /// This port and all messages sent across it are specific to this transform.
   final SendPort _port;
 
-  final Asset primaryInput;
-
   TransformLogger get logger => _logger;
   TransformLogger _logger;
 
-  /// Creates a transform from a serializable map sent from the host isolate.
-  ForeignTransform(Map transform)
-      : _port = transform['port'],
-        primaryInput = deserializeAsset(transform['primaryInput']) {
+  _ForeignBaseTransform(Map transform)
+      : _port = transform['port'] {
     _logger = new TransformLogger((assetId, level, message, span) {
       call(_port, {
         'type': 'log',
@@ -91,6 +99,22 @@
     });
   }
 
+  void consumePrimary() {
+    call(_port, {'type': 'consumePrimary'});
+  }
+}
+
+/// A wrapper for a [Transform] that's in the host isolate.
+///
+/// This retrieves inputs from and sends outputs and logs to the host isolate.
+class ForeignTransform extends _ForeignBaseTransform implements Transform {
+  final Asset primaryInput;
+
+  /// Creates a transform from a serialized map sent from the host isolate.
+  ForeignTransform(Map transform)
+      : primaryInput = deserializeAsset(transform['primaryInput']),
+        super(transform);
+
   Future<Asset> getInput(AssetId id) {
     return call(_port, {
       'type': 'getInput',
@@ -119,8 +143,22 @@
       'output': serializeAsset(output)
     });
   }
+}
 
-  void consumePrimary() {
-    call(_port, {'type': 'consumePrimary'});
+/// A wrapper for a [DeclaringTransform] that's in the host isolate.
+class ForeignDeclaringTransform extends _ForeignBaseTransform
+    implements DeclaringTransform {
+  final AssetId primaryId;
+
+  /// Creates a transform from a serializable map sent from the host isolate.
+  ForeignDeclaringTransform(Map transform)
+      : primaryId = deserializeId(transform['primaryId']),
+        super(transform);
+
+  void declareOutput(AssetId id) {
+    call(_port, {
+      'type': 'declareOutput',
+      'output': serializeId(id)
+    });
   }
 }
diff --git a/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart b/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart
index e6e030d..a7d3937 100644
--- a/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart
+++ b/sdk/lib/_internal/pub/asset/dart/serialize/transformer.dart
@@ -7,19 +7,21 @@
 import 'dart:isolate';
 
 import 'package:barback/barback.dart';
-// TODO(nweiz): don't import from "src" once issue 14966 is fixed.
-import 'package:barback/src/internal_asset.dart';
 
 import '../serialize.dart';
 import 'transform.dart';
 
 /// Converts [transformer] into a serializable map.
-Map serializeTransformer(Transformer transformer) {
+Map _serializeTransformer(Transformer transformer) {
   var port = new ReceivePort();
   port.listen((wrappedMessage) {
     respond(wrappedMessage, (message) {
       if (message['type'] == 'isPrimary') {
         return transformer.isPrimary(deserializeId(message['id']));
+      } else if (message['type'] == 'declareOutputs') {
+        return (transformer as DeclaringTransformer).declareOutputs(
+            new ForeignDeclaringTransform(message['transform']))
+            .then((_) => null);
       } else {
         assert(message['type'] == 'apply');
 
@@ -31,15 +33,24 @@
     });
   });
 
+  var type;
+  if (transformer is LazyTransformer) {
+    type = 'LazyTransformer';
+  } else if (transformer is DeclaringTransformer) {
+    type = 'DeclaringTransformer';
+  } else {
+    type = 'Transformer';
+  }
+
   return {
-    'type': 'Transformer',
+    'type': type,
     'toString': transformer.toString(),
     'port': port.sendPort
   };
 }
 
 // Converts [group] into a serializable map.
-Map serializeTransformerGroup(TransformerGroup group) {
+Map _serializeTransformerGroup(TransformerGroup group) {
   return {
     'type': 'TransformerGroup',
     'toString': group.toString(),
@@ -52,9 +63,9 @@
 /// Converts [transformerOrGroup] into a serializable map.
 Map serializeTransformerOrGroup(transformerOrGroup) {
   if (transformerOrGroup is Transformer) {
-    return serializeTransformer(transformerOrGroup);
+    return _serializeTransformer(transformerOrGroup);
   } else {
     assert(transformerOrGroup is TransformerGroup);
-    return serializeTransformerGroup(transformerOrGroup);
+    return _serializeTransformerGroup(transformerOrGroup);
   }
 }
diff --git a/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart b/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart
index 5b6dfa7..3b66841 100644
--- a/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart
+++ b/sdk/lib/_internal/pub/asset/dart/transformer_isolate.dart
@@ -29,36 +29,53 @@
 
 /// Loads all the transformers and groups defined in [uri].
 ///
-/// Loads the library, finds any Transformer or TransformerGroup subclasses in
-/// it, instantiates them with [configuration] and [mode], and returns them.
-Iterable _initialize(Uri uri, Map configuration, BarbackMode mode) {
+/// Loads the library, finds any [Transformer] or [TransformerGroup] subclasses
+/// in it, instantiates them with [configuration] and [mode], and returns them.
+List _initialize(Uri uri, Map configuration, BarbackMode mode) {
   var mirrors = currentMirrorSystem();
   var transformerClass = reflectClass(Transformer);
   var groupClass = reflectClass(TransformerGroup);
 
-  // TODO(nweiz): if no valid transformers are found, throw an error message
-  // describing candidates and why they were rejected.
-  return mirrors.libraries[uri].declarations.values.map((declaration) {
-    if (declaration is! ClassMirror) return null;
-    var classMirror = declaration;
-    if (classMirror.isPrivate) return null;
-    if (classMirror.isAbstract) return null;
-    if (!classMirror.isSubtypeOf(transformerClass) &&
-        !classMirror.isSubtypeOf(groupClass)) {
-      return null;
+  var seen = new Set();
+  var transformers = [];
+
+  loadFromLibrary(library) {
+    if (seen.contains(library)) return;
+    seen.add(library);
+
+    // Load transformers from libraries exported by [library].
+    for (var dependency in library.libraryDependencies) {
+      if (!dependency.isExport) continue;
+      loadFromLibrary(dependency.targetLibrary);
     }
 
-    var constructor = _getConstructor(classMirror, 'asPlugin');
-    if (constructor == null) return null;
-    if (constructor.parameters.isEmpty) {
-      if (configuration.isNotEmpty) return null;
-      return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee;
-    }
-    if (constructor.parameters.length != 1) return null;
+    // TODO(nweiz): if no valid transformers are found, throw an error message
+    // describing candidates and why they were rejected.
+    transformers.addAll(library.declarations.values.map((declaration) {
+      if (declaration is! ClassMirror) return null;
+      var classMirror = declaration;
+      if (classMirror.isPrivate) return null;
+      if (classMirror.isAbstract) return null;
+      if (!classMirror.isSubtypeOf(transformerClass) &&
+          !classMirror.isSubtypeOf(groupClass)) {
+        return null;
+      }
 
-    return classMirror.newInstance(const Symbol('asPlugin'),
-        [new BarbackSettings(configuration, mode)]).reflectee;
-  }).where((classMirror) => classMirror != null);
+      var constructor = _getConstructor(classMirror, 'asPlugin');
+      if (constructor == null) return null;
+      if (constructor.parameters.isEmpty) {
+        if (configuration.isNotEmpty) return null;
+        return classMirror.newInstance(const Symbol('asPlugin'), []).reflectee;
+      }
+      if (constructor.parameters.length != 1) return null;
+
+      return classMirror.newInstance(const Symbol('asPlugin'),
+          [new BarbackSettings(configuration, mode)]).reflectee;
+    }).where((classMirror) => classMirror != null));
+  }
+
+  loadFromLibrary(mirrors.libraries[uri]);
+  return transformers;
 }
 
 // TODO(nweiz): clean this up when issue 13248 is fixed.
diff --git a/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart b/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart
index f07b7fa..f31d4d2 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/admin_server.dart
@@ -10,8 +10,8 @@
 import 'package:stack_trace/stack_trace.dart';
 
 import '../log.dart' as log;
+import 'asset_environment.dart';
 import 'base_server.dart';
-import 'build_environment.dart';
 import 'web_socket_api.dart';
 
 /// The web admin interface to pub serve.
@@ -22,7 +22,7 @@
   final _webSockets = new Set<WebSocket>();
 
   /// Creates a new server and binds it to [port] of [host].
-  static Future<AdminServer> bind(BuildEnvironment environment,
+  static Future<AdminServer> bind(AssetEnvironment environment,
       String host, int port) {
     return Chain.track(HttpServer.bind(host, port)).then((server) {
       log.fine('Bound admin server to $host:$port.');
@@ -30,7 +30,7 @@
     });
   }
 
-  AdminServer._(BuildEnvironment environment, HttpServer server)
+  AdminServer._(AssetEnvironment environment, HttpServer server)
       : super(environment, server);
 
   /// Closes the server and all Web Socket connections.
diff --git a/sdk/lib/_internal/pub/lib/src/barback/build_environment.dart b/sdk/lib/_internal/pub/lib/src/barback/asset_environment.dart
similarity index 92%
rename from sdk/lib/_internal/pub/lib/src/barback/build_environment.dart
rename to sdk/lib/_internal/pub/lib/src/barback/asset_environment.dart
index a332dd5..8bed911 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/build_environment.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/asset_environment.dart
@@ -2,7 +2,7 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-library pub.barback.build_environment;
+library pub.barback.asset_environment;
 
 import 'dart:async';
 import 'dart:io';
@@ -19,20 +19,21 @@
 import '../package_graph.dart';
 import '../sdk.dart' as sdk;
 import 'admin_server.dart';
-import 'build_directory.dart';
+import 'barback_server.dart';
 import 'dart_forwarding_transformer.dart';
 import 'dart2js_transformer.dart';
 import 'load_all_transformers.dart';
 import 'pub_package_provider.dart';
-import 'barback_server.dart';
+import 'source_directory.dart';
 
 /// The entire "visible" state of the assets of a package and all of its
 /// dependencies, taking into account the user's configuration when running pub.
 ///
 /// Where [PackageGraph] just describes the entrypoint's dependencies as
 /// specified by pubspecs, this includes "transient" information like the mode
-/// that the user is running pub in, or which directories they want to build.
-class BuildEnvironment {
+/// that the user is running pub in, or which directories they want to
+/// transform.
+class AssetEnvironment {
   /// Creates a new build environment for working with the assets used by
   /// [entrypoint] and its dependencies.
   ///
@@ -53,7 +54,7 @@
   ///
   /// Returns a [Future] that completes to the environment once the inputs,
   /// transformers, and server are loaded and ready.
-  static Future<BuildEnvironment> create(Entrypoint entrypoint,
+  static Future<AssetEnvironment> create(Entrypoint entrypoint,
       String hostname, int basePort, BarbackMode mode, WatcherType watcherType,
       {bool useDart2JS: true}) {
     return entrypoint.loadPackageGraph().then((graph) {
@@ -61,7 +62,7 @@
       var barback = new Barback(new PubPackageProvider(graph));
       barback.log.listen(_log);
 
-      var environment = new BuildEnvironment._(graph, barback, mode,
+      var environment = new AssetEnvironment._(graph, barback, mode,
           watcherType, hostname, basePort);
 
       return environment._load(useDart2JS: useDart2JS)
@@ -72,9 +73,9 @@
   /// The server for the Web Socket API and admin interface.
   AdminServer _adminServer;
 
-  /// The public directories in the root package that are available for
-  /// building, keyed by their root directory.
-  final _directories = new Map<String, BuildDirectory>();
+  /// The public directories in the root package that are included in the asset
+  /// environment, keyed by their root directory.
+  final _directories = new Map<String, SourceDirectory>();
 
   /// The [Barback] instance used to process assets in this environment.
   final Barback barback;
@@ -120,7 +121,7 @@
   /// go to barback immediately.
   Set<AssetId> _modifiedSources;
 
-  BuildEnvironment._(this.graph, this.barback, this.mode, this._watcherType,
+  AssetEnvironment._(this.graph, this.barback, this.mode, this._watcherType,
       this._hostname, this._basePort);
 
   /// Gets the built-in [Transformer]s that should be added to [package].
@@ -190,14 +191,14 @@
       }
     }
 
-    var buildDirectory = new BuildDirectory(
+    var sourceDirectory = new SourceDirectory(
         this, rootDirectory, _hostname, port);
-    _directories[rootDirectory] = buildDirectory;
+    _directories[rootDirectory] = sourceDirectory;
 
     return _provideDirectorySources(rootPackage, rootDirectory)
         .then((subscription) {
-      buildDirectory.watchSubscription = subscription;
-      return buildDirectory.serve();
+      sourceDirectory.watchSubscription = subscription;
+      return sourceDirectory.serve();
     });
   }
 
@@ -213,20 +214,18 @@
 
     return directory.server.then((server) {
       var url = server.url;
-      return directory.close().then((_) {
-        // Remove the sources from barback, unless some other build directory
-        // includes them.
-        return _removeDirectorySources(rootDirectory);
-      }).then((_) => url);
+      return directory.close()
+          .then((_) => _removeDirectorySources(rootDirectory))
+          .then((_) => url);
     });
   }
 
-  /// Gets the build directory that contains [assetPath] within the entrypoint
+  /// Gets the source directory that contains [assetPath] within the entrypoint
   /// package.
   ///
-  /// If [assetPath] is not contained within a build directory, this will
-  /// throw an exception.
-  String getBuildDirectoryContaining(String assetPath) =>
+  /// If [assetPath] is not contained within a source directory, this throws
+  /// an exception.
+  String getSourceDirectoryContaining(String assetPath) =>
       _directories.values
           .firstWhere((dir) => path.isWithin(dir.directory, assetPath))
           .directory;
@@ -496,16 +495,9 @@
     });
   }
 
-  /// Removes all of the files in [dir] in the root package from barback unless
-  /// some other build directory still contains them.
+  /// Removes all of the files in [dir] in the root package from barback.
   Future _removeDirectorySources(String dir) {
-    return _listDirectorySources(rootPackage, dir, where: (relative) {
-      // TODO(rnystrom): This is O(n*m) where n is the number of files and
-      // m is the number of served directories. Consider something more
-      // optimal if this becomes a bottleneck.
-      // Don't remove a source if some other directory still includes it.
-      return !_directories.keys.any((dir) => path.isWithin(dir, relative));
-    }).then((ids) {
+    return _listDirectorySources(rootPackage, dir).then((ids) {
       if (_modifiedSources == null) {
         barback.removeSources(ids);
       } else {
@@ -519,12 +511,7 @@
   /// For large packages, listing the contents is a performance bottleneck, so
   /// this is optimized for our needs in here instead of using the more general
   /// but slower [listDir].
-  ///
-  /// If [where] is given, then it is used to filter the resulting list of
-  /// packages. Only assets whose relative path within [package] matches that
-  /// will be included in the results.
-  Future<List<AssetId>> _listDirectorySources(Package package, String dir,
-      {bool where(String relativePath)}) {
+  Future<List<AssetId>> _listDirectorySources(Package package, String dir) {
     var subdirectory = path.join(package.dir, dir);
     if (!dirExists(subdirectory)) return new Future.value([]);
 
@@ -556,8 +543,6 @@
       if (relative.endsWith(".dart.js.map")) return [];
       if (relative.endsWith(".dart.precompiled.js")) return [];
 
-      if (where != null && !where(relative)) return [];
-
       return [new AssetId(package.name, relative)];
     }).toList();
   }
diff --git a/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart b/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart
index 611d62f..413f929 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/barback_server.dart
@@ -16,7 +16,7 @@
 import '../log.dart' as log;
 import '../utils.dart';
 import 'base_server.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
 
 /// Callback for determining if an asset with [id] should be served or not.
 typedef bool AllowAsset(AssetId id);
@@ -40,16 +40,11 @@
   /// If this is `null`, all assets may be served.
   AllowAsset allowAsset;
 
-  // TODO(rnystrom): Remove this when the Editor is using the admin server.
-  // port. See #17640.
-  /// All currently open [WebSocket] connections.
-  final _webSockets = new Set<WebSocket>();
-
   /// Creates a new server and binds it to [port] of [host].
   ///
   /// This server will serve assets from [barback], and use [rootDirectory] as
   /// the root directory.
-  static Future<BarbackServer> bind(BuildEnvironment environment,
+  static Future<BarbackServer> bind(AssetEnvironment environment,
       String host, int port, String rootDirectory) {
     return Chain.track(HttpServer.bind(host, port)).then((server) {
       log.fine('Bound "$rootDirectory" to $host:$port.');
@@ -57,19 +52,10 @@
     });
   }
 
-  BarbackServer._(BuildEnvironment environment, HttpServer server,
+  BarbackServer._(AssetEnvironment environment, HttpServer server,
       this.rootDirectory)
       : super(environment, server);
 
-  // TODO(rnystrom): Remove this when the Editor is using the admin server.
-  // port. See #17640.
-  /// Closes the server.
-  Future close() {
-    var futures = [super.close()];
-    futures.addAll(_webSockets.map((socket) => socket.close()));
-    return Future.wait(futures);
-  }
-
   /// Converts a [url] served by this server into an [AssetId] that can be
   /// requested from barback.
   AssetId urlToId(Uri url) {
@@ -117,6 +103,25 @@
       return;
     }
 
+    // TODO(rnystrom): Remove this when #16647 is fixed.
+    // The "assets" path is deprecated so warn if the user is relying on it.
+    // We do this here in pub serve so we only warn if they in fact actually
+    // use an asset from a package's "asset" directory.
+    if (id.path.startsWith("asset/")) {
+      var message = 'Warning: Support for the "asset" directory is deprecated '
+          'and will be removed soon.\n';
+
+      var fixed = id.path.replaceAll(new RegExp(r"^asset/"), "lib/");
+      if (id.package == environment.rootPackage.name) {
+        message += 'Please move "${id.path}" to "$fixed".';
+      } else {
+        message += 'Please ask the maintainer of "${id.package}" to move '
+            '"${id.path}" to "$fixed".';
+      }
+
+      log.warning(log.yellow(message));
+    }
+
     logRequest(request, "Loading $id");
     environment.barback.getAssetById(id).then((result) {
       logRequest(request, "getAssetById($id) returned");
diff --git a/sdk/lib/_internal/pub/lib/src/barback/base_server.dart b/sdk/lib/_internal/pub/lib/src/barback/base_server.dart
index dcf739d..0dcc936 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/base_server.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/base_server.dart
@@ -13,12 +13,12 @@
 
 import '../log.dart' as log;
 import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
 
 /// Base class for a pub-controlled server.
 abstract class BaseServer<T> {
   /// The [BuildEnvironment] being served.
-  final BuildEnvironment environment;
+  final AssetEnvironment environment;
 
   /// The underlying HTTP server.
   final HttpServer _server;
diff --git a/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart b/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart
index ca71b15..9a71186 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/dart2js_transformer.dart
@@ -6,7 +6,6 @@
 
 import 'dart:async';
 import 'dart:convert';
-import 'dart:io';
 
 import 'package:analyzer/analyzer.dart';
 import 'package:barback/barback.dart';
@@ -21,7 +20,7 @@
 import '../dart.dart' as dart;
 import '../pool.dart';
 import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
 
 /// The set of all valid configuration options for this transformer.
 final _validOptions = new Set<String>.from([
@@ -40,7 +39,7 @@
   /// is here: https://code.google.com/p/dart/issues/detail?id=14730.
   static final _pool = new Pool(1);
 
-  final BuildEnvironment _environment;
+  final AssetEnvironment _environment;
   final BarbackSettings _settings;
 
   /// Whether source maps should be generated for the compiled JS.
@@ -56,7 +55,7 @@
         "${toSentence(invalidOptions.map((option) => '"$option"'))}.");
   }
 
-  Dart2JSTransformer(BuildEnvironment environment, BarbackMode mode)
+  Dart2JSTransformer(AssetEnvironment environment, BarbackMode mode)
       : this.withSettings(environment, new BarbackSettings({}, mode));
 
   /// Only ".dart" entrypoint files within a buildable directory are processed.
@@ -202,7 +201,7 @@
 class _BarbackCompilerProvider implements dart.CompilerProvider {
   Uri get libraryRoot => Uri.parse("${path.toUri(_libraryRootPath)}/");
 
-  final BuildEnvironment _environment;
+  final AssetEnvironment _environment;
   final Transform _transform;
   String _libraryRootPath;
 
@@ -246,10 +245,10 @@
     // do that by placing them in a special "$sdk" pseudo-package. In order for
     // dart2js to generate the right URLs to point to that package, we give it
     // a library root that corresponds to where that package can be found
-    // relative to the public build directory containing that entrypoint.
+    // relative to the public source directory containing that entrypoint.
     //
     // For example, say the package being compiled is "/dev/myapp", the
-    // entrypoint is "web/sub/foo/bar.dart", and the build directory is
+    // entrypoint is "web/sub/foo/bar.dart", and the source directory is
     // "web/sub". This means the SDK sources will be (conceptually) at:
     //
     //     /dev/myapp/web/sub/packages/$sdk/lib/
@@ -259,7 +258,7 @@
     //     $sdk|lib/lib/...
     //
     // TODO(rnystrom): Fix this if #17751 is fixed.
-    var buildDir = _environment.getBuildDirectoryContaining(
+    var buildDir = _environment.getSourceDirectoryContaining(
         _transform.primaryInput.id.path);
     _libraryRootPath = path.join(_environment.rootPackage.dir,
         buildDir, "packages", r"$sdk");
diff --git a/sdk/lib/_internal/pub/lib/src/barback/foreign_transformer.dart b/sdk/lib/_internal/pub/lib/src/barback/foreign_transformer.dart
new file mode 100644
index 0000000..2fc1a0f
--- /dev/null
+++ b/sdk/lib/_internal/pub/lib/src/barback/foreign_transformer.dart
@@ -0,0 +1,102 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub.foreign_transformer;
+
+import 'dart:async';
+import 'dart:isolate';
+
+import 'package:barback/barback.dart';
+
+import '../../../asset/dart/serialize.dart';
+import '../barback.dart';
+import 'excluding_transformer.dart';
+
+/// A wrapper for a transformer that's in a different isolate.
+class _ForeignTransformer extends Transformer {
+  /// The port with which we communicate with the child isolate.
+  ///
+  /// This port and all messages sent across it are specific to this
+  /// transformer.
+  final SendPort _port;
+
+  /// The result of calling [toString] on the transformer in the isolate.
+  final String _toString;
+
+  _ForeignTransformer(Map map)
+      : _port = map['port'],
+        _toString = map['toString'];
+
+  Future<bool> isPrimary(AssetId id) {
+    return call(_port, {
+      'type': 'isPrimary',
+      'id': serializeId(id)
+    });
+  }
+
+  Future apply(Transform transform) {
+    return call(_port, {
+      'type': 'apply',
+      'transform': serializeTransform(transform)
+    });
+  }
+
+  String toString() => _toString;
+}
+
+class _ForeignDeclaringTransformer extends _ForeignTransformer
+    implements DeclaringTransformer {
+  _ForeignDeclaringTransformer(Map map)
+      : super(map);
+
+  Future declareOutputs(DeclaringTransform transform) {
+    return call(_port, {
+      'type': 'declareOutputs',
+      'transform': serializeDeclaringTransform(transform)
+    });
+  }
+}
+
+class _ForeignLazyTransformer extends _ForeignDeclaringTransformer
+    implements LazyTransformer {
+  _ForeignLazyTransformer(Map map)
+      : super(map);
+}
+
+/// A wrapper for a transformer group that's in a different isolate.
+class _ForeignGroup implements TransformerGroup {
+  final Iterable<Iterable> phases;
+
+  /// The result of calling [toString] on the transformer group in the isolate.
+  final String _toString;
+
+  _ForeignGroup(TransformerId id, Map map)
+      : phases = map['phases'].map((phase) {
+          return phase.map((transformer) => deserializeTransformerOrGroup(
+              transformer, id)).toList();
+        }).toList(),
+        _toString = map['toString'];
+
+  String toString() => _toString;
+}
+
+/// Converts a serializable map into a [Transformer] or a [TransformerGroup].
+deserializeTransformerOrGroup(Map map, TransformerId id) {
+  var transformer;
+  switch(map['type']) {
+    case 'TransformerGroup': return new _ForeignGroup(id, map);
+    case 'Transformer':
+      transformer = new _ForeignTransformer(map);
+      break;
+    case 'DeclaringTransformer':
+      transformer = new _ForeignDeclaringTransformer(map);
+      break;
+    case 'LazyTransformer':
+      transformer = new _ForeignLazyTransformer(map);
+      break;
+    default: assert(false);
+  }
+
+  return ExcludingTransformer.wrap(transformer, id.includes, id.excludes);
+}
diff --git a/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
index 03e24d6..0f9016d 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_all_transformers.dart
@@ -12,7 +12,7 @@
 import '../log.dart' as log;
 import '../package_graph.dart';
 import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
 import 'dart2js_transformer.dart';
 import 'excluding_transformer.dart';
 import 'load_transformers.dart';
@@ -27,7 +27,7 @@
 ///
 /// Any built-in transformers that are provided by the environment will
 /// automatically be added to the end of the root package's cascade.
-Future loadAllTransformers(BuildEnvironment environment,
+Future loadAllTransformers(AssetEnvironment environment,
     BarbackServer transformerServer) {
   // In order to determine in what order we should load transformers, we need to
   // know which transformers depend on which others. This is different than
@@ -230,7 +230,7 @@
 
 /// A class that loads transformers defined in specific files.
 class _TransformerLoader {
-  final BuildEnvironment _environment;
+  final AssetEnvironment _environment;
 
   final BarbackServer _transformerServer;
 
diff --git a/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart b/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
index c5cb4b9..2d5d8cb 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/load_transformers.dart
@@ -8,22 +8,18 @@
 import 'dart:convert';
 import 'dart:isolate';
 
-import 'package:barback/barback.dart';
-// TODO(nweiz): don't import from "src" once issue 14966 is fixed.
-import 'package:barback/src/internal_asset.dart';
-
 import '../../../asset/dart/serialize.dart';
 import '../barback.dart';
 import '../dart.dart' as dart;
 import '../log.dart' as log;
 import '../utils.dart';
-import 'build_environment.dart';
-import 'excluding_transformer.dart';
+import 'asset_environment.dart';
+import 'foreign_transformer.dart';
 import 'barback_server.dart';
 
 /// Load and return all transformers and groups from the library identified by
 /// [id].
-Future<Set> loadTransformers(BuildEnvironment environment,
+Future<Set> loadTransformers(AssetEnvironment environment,
     BarbackServer transformerServer, TransformerId id) {
   return id.getAssetId(environment.barback).then((assetId) {
     var path = assetId.path.replaceFirst('lib/', '');
@@ -53,7 +49,7 @@
         'configuration': JSON.encode(id.configuration)
       }).then((transformers) {
         transformers = transformers.map(
-            (transformer) => _deserializeTransformerOrGroup(transformer, id))
+            (transformer) => deserializeTransformerOrGroup(transformer, id))
             .toSet();
         log.fine("Transformers from $assetId: $transformers");
         return transformers;
@@ -75,63 +71,3 @@
     });
   });
 }
-
-/// A wrapper for a transformer that's in a different isolate.
-class _ForeignTransformer extends Transformer {
-  /// The port with which we communicate with the child isolate.
-  ///
-  /// This port and all messages sent across it are specific to this
-  /// transformer.
-  final SendPort _port;
-
-  /// The result of calling [toString] on the transformer in the isolate.
-  final String _toString;
-
-  _ForeignTransformer(Map map)
-      : _port = map['port'],
-        _toString = map['toString'];
-
-  Future<bool> isPrimary(AssetId id) {
-    return call(_port, {
-      'type': 'isPrimary',
-      'id': serializeId(id)
-    });
-  }
-
-  Future apply(Transform transform) {
-    return call(_port, {
-      'type': 'apply',
-      'transform': serializeTransform(transform)
-    });
-  }
-
-  String toString() => _toString;
-}
-
-/// A wrapper for a transformer group that's in a different isolate.
-class _ForeignGroup implements TransformerGroup {
-  final Iterable<Iterable> phases;
-
-  /// The result of calling [toString] on the transformer group in the isolate.
-  final String _toString;
-
-  _ForeignGroup(TransformerId id, Map map)
-      : phases = map['phases'].map((phase) {
-          return phase.map((transformer) => _deserializeTransformerOrGroup(
-              transformer, id)).toList();
-        }).toList(),
-        _toString = map['toString'];
-
-  String toString() => _toString;
-}
-
-/// Converts a serializable map into a [Transformer] or a [TransformerGroup].
-_deserializeTransformerOrGroup(Map map, TransformerId id) {
-  if (map['type'] == 'Transformer') {
-    var transformer = new _ForeignTransformer(map);
-    return ExcludingTransformer.wrap(transformer, id.includes, id.excludes);
-  }
-
-  assert(map['type'] == 'TransformerGroup');
-  return new _ForeignGroup(id, map);
-}
diff --git a/sdk/lib/_internal/pub/lib/src/barback/build_directory.dart b/sdk/lib/_internal/pub/lib/src/barback/source_directory.dart
similarity index 81%
rename from sdk/lib/_internal/pub/lib/src/barback/build_directory.dart
rename to sdk/lib/_internal/pub/lib/src/barback/source_directory.dart
index c01da04..725c4fc 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/build_directory.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/source_directory.dart
@@ -2,21 +2,19 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
-library pub.barback.build_environment;
+library pub.barback.source_directory;
 
 import 'dart:async';
 
 import 'package:watcher/watcher.dart';
 
-import 'build_environment.dart';
+import 'asset_environment.dart';
 import 'barback_server.dart';
 
-// TODO(rnystrom): Rename to "SourceDirectory" and clean up various doc
-// comments that refer to "build directories" to use "source directory".
 /// A directory in the entrypoint package whose contents have been made
 /// available to barback and that are bound to a server.
-class BuildDirectory {
-  final BuildEnvironment _environment;
+class SourceDirectory {
+  final AssetEnvironment _environment;
 
   /// The relative directory path within the package.
   final String directory;
@@ -40,7 +38,7 @@
   /// If the directory is not being watched, this will be `null`.
   StreamSubscription<WatchEvent> watchSubscription;
 
-  BuildDirectory(this._environment, this.directory, this.hostname, this.port);
+  SourceDirectory(this._environment, this.directory, this.hostname, this.port);
 
   /// Binds a server running on [hostname]:[port] to this directory.
   Future<BarbackServer> serve() {
@@ -51,7 +49,7 @@
     });
   }
 
-  /// Removes the build directory from the build environment.
+  /// Removes the source directory from the build environment.
   ///
   /// Closes the server, removes the assets from barback, and stops watching it.
   Future close() {
diff --git a/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart b/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart
index beb8c11..6953d7c 100644
--- a/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart
+++ b/sdk/lib/_internal/pub/lib/src/barback/web_socket_api.dart
@@ -11,7 +11,7 @@
 import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
 
 import '../utils.dart';
-import 'build_environment.dart';
+import 'asset_environment.dart';
 
 /// Implements the [WebSocket] API for communicating with a running pub serve
 /// process, mainly for use by the Editor.
@@ -20,7 +20,7 @@
 /// methods are described in the method-level documentation below.
 class WebSocketApi {
   final WebSocket _socket;
-  final BuildEnvironment _environment;
+  final AssetEnvironment _environment;
   final _server = new json_rpc.Server();
 
   WebSocketApi(this._socket, this._environment) {
diff --git a/sdk/lib/_internal/pub/lib/src/command/build.dart b/sdk/lib/_internal/pub/lib/src/command/build.dart
index 61e46ef..560bbe2 100644
--- a/sdk/lib/_internal/pub/lib/src/command/build.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/build.dart
@@ -9,7 +9,7 @@
 import 'package:barback/barback.dart';
 import 'package:path/path.dart' as path;
 
-import '../barback/build_environment.dart';
+import '../barback/asset_environment.dart';
 import '../exit_codes.dart' as exit_codes;
 import '../io.dart';
 import '../log.dart' as log;
@@ -53,7 +53,7 @@
     // Since this server will only be hit by the transformer loader and isn't
     // user-facing, just use an IPv4 address to avoid a weird bug on the
     // OS X buildbots.
-    return BuildEnvironment.create(entrypoint, "127.0.0.1", 0, mode,
+    return AssetEnvironment.create(entrypoint, "127.0.0.1", 0, mode,
         WatcherType.NONE, useDart2JS: true)
           .then((environment) {
       // Show in-progress errors, but not results. Those get handled
diff --git a/sdk/lib/_internal/pub/lib/src/command/cache.dart b/sdk/lib/_internal/pub/lib/src/command/cache.dart
index 93897fa..63d027b 100644
--- a/sdk/lib/_internal/pub/lib/src/command/cache.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/cache.dart
@@ -7,6 +7,7 @@
 import '../command.dart';
 import 'cache_add.dart';
 import 'cache_list.dart';
+import 'cache_repair.dart';
 
 /// Handles the `cache` pub command.
 class CacheCommand extends PubCommand {
@@ -15,6 +16,7 @@
 
   final subcommands = {
     "add": new CacheAddCommand(),
-    "list": new CacheListCommand()
+    "list": new CacheListCommand(),
+    "repair": new CacheRepairCommand()
   };
 }
diff --git a/sdk/lib/_internal/pub/lib/src/command/cache_add.dart b/sdk/lib/_internal/pub/lib/src/command/cache_add.dart
index fac394a..4b775f2 100644
--- a/sdk/lib/_internal/pub/lib/src/command/cache_add.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/cache_add.dart
@@ -55,7 +55,7 @@
     }
 
     // TODO(rnystrom): Support installing from git too.
-    var source = this.cache.sources["hosted"];
+    var source = cache.sources["hosted"];
 
     // TODO(rnystrom): Allow specifying the server.
     return source.getVersions(package, package).then((versions) {
diff --git a/sdk/lib/_internal/pub/lib/src/command/cache_repair.dart b/sdk/lib/_internal/pub/lib/src/command/cache_repair.dart
new file mode 100644
index 0000000..450951b
--- /dev/null
+++ b/sdk/lib/_internal/pub/lib/src/command/cache_repair.dart
@@ -0,0 +1,50 @@
+// 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 pub.command.cache_repair;
+
+import 'dart:async';
+
+import '../command.dart';
+import '../exit_codes.dart' as exit_codes;
+import '../io.dart';
+import '../log.dart' as log;
+import '../utils.dart';
+
+/// Handles the `cache repair` pub command.
+class CacheRepairCommand extends PubCommand {
+  String get description => "Reinstall cached packages.";
+  String get usage => "pub cache repair";
+  bool get requiresEntrypoint => false;
+
+  Future onRun() {
+    var successes = 0;
+    var failures = 0;
+
+    // Repair every cached source.
+    return Future.forEach(cache.sources.where(
+        (source) => source.shouldCache), (source) {
+      return source.repairCachedPackages().then((results) {
+        successes += results.first;
+        failures += results.last;
+      });
+    }).then((_) {
+      if (successes > 0) {
+        var packages = pluralize("package", successes);
+        log.message("Reinstalled ${log.green(successes)} $packages.");
+      }
+
+      if (failures > 0) {
+        var packages = pluralize("package", failures);
+        log.message("Failed to reinstall ${log.red(failures)} $packages.");
+      }
+
+      if (successes == 0 && failures == 0) {
+        log.message("No packages in cache, so nothing to repair.");
+      }
+
+      if (failures > 0) return flushThenExit(exit_codes.UNAVAILABLE);
+    });
+  }
+}
diff --git a/sdk/lib/_internal/pub/lib/src/command/lish.dart b/sdk/lib/_internal/pub/lib/src/command/lish.dart
index 91bfacf..b85d0f4 100644
--- a/sdk/lib/_internal/pub/lib/src/command/lish.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/lish.dart
@@ -56,6 +56,7 @@
           if (url is! String) invalidServerResponse(response);
           cloudStorageUrl = Uri.parse(url);
           var request = new http.MultipartRequest('POST', cloudStorageUrl);
+          request.headers['Pub-Http-Timeout'] = 'None';
 
           var fields = _expectField(parameters, 'fields', response);
           if (fields is! Map) invalidServerResponse(response);
diff --git a/sdk/lib/_internal/pub/lib/src/command/serve.dart b/sdk/lib/_internal/pub/lib/src/command/serve.dart
index a2cc64e..55895d6 100644
--- a/sdk/lib/_internal/pub/lib/src/command/serve.dart
+++ b/sdk/lib/_internal/pub/lib/src/command/serve.dart
@@ -9,7 +9,7 @@
 
 import 'package:barback/barback.dart';
 
-import '../barback/build_environment.dart';
+import '../barback/asset_environment.dart';
 import '../barback/pub_package_provider.dart';
 import '../log.dart' as log;
 import '../utils.dart';
@@ -86,7 +86,7 @@
     var watcherType = commandOptions['force-poll'] ?
         WatcherType.POLLING : WatcherType.AUTO;
 
-    return BuildEnvironment.create(entrypoint, hostname, port, mode,
+    return AssetEnvironment.create(entrypoint, hostname, port, mode,
         watcherType, useDart2JS: useDart2JS).then((environment) {
 
       var directoryLength = sourceDirectories.map((dir) => dir.length)
@@ -132,7 +132,7 @@
     });
   }
 
-  Future _startServer(BuildEnvironment environment, String rootDirectory,
+  Future _startServer(AssetEnvironment environment, String rootDirectory,
       int directoryLength) {
     return environment.serveDirectory(rootDirectory).then((server) {
       // In release mode, strip out .dart files since all relevant ones have
diff --git a/sdk/lib/_internal/pub/lib/src/entrypoint.dart b/sdk/lib/_internal/pub/lib/src/entrypoint.dart
index c098eec..2c360e14 100644
--- a/sdk/lib/_internal/pub/lib/src/entrypoint.dart
+++ b/sdk/lib/_internal/pub/lib/src/entrypoint.dart
@@ -248,57 +248,21 @@
     });
   }
 
-  /// Warns users if they have directory or file named `assets` directly
-  /// inside a top-level directory.
-  void _warnOnAssetsPaths() {
-    var buffer = new StringBuffer();
-
-    warn(message) {
-      if (buffer.isEmpty) {
-        buffer.writeln(
-            'Warning: Pub reserves paths containing "assets" for using assets '
-            'from packages.');
-      }
-
-      buffer.writeln(message);
-    }
-
-    // Look inside all of the top-level directories.
-    for (var dir in ordered(listDir(root.dir))) {
-      var assetsPath = path.join(dir, "assets");
-      var relative = path.relative(assetsPath, from: root.dir);
-      if (dirExists(assetsPath)) {
-        warn('Please rename the directory "$relative".');
-      } else if (entryExists(assetsPath)) {
-        warn('Please rename the file "$relative".');
-      }
-    }
-
-    if (buffer.isNotEmpty) log.warning(buffer);
-  }
-
   /// Loads the package graph for the application and all of its transitive
   /// dependencies. Before loading makes sure the lockfile and dependencies are
   /// installed and up to date.
-  Future<PackageGraph> loadPackageGraph() =>
-    _ensureLockFileIsUpToDate()
-      .then((_) {
-        _warnOnAssetsPaths();
-        return _loadPackageGraph();
+  Future<PackageGraph> loadPackageGraph() {
+    return _ensureLockFileIsUpToDate().then((_) {
+      var lockFile = loadLockFile();
+      return Future.wait(lockFile.packages.values.map((id) {
+        var source = cache.sources[id.source];
+        return source.getDirectory(id)
+            .then((dir) => new Package.load(id.name, dir, cache.sources));
+      })).then((packages) {
+        var packageMap = new Map.fromIterable(packages, key: (p) => p.name);
+        packageMap[root.name] = root;
+        return new PackageGraph(this, lockFile, packageMap);
       });
-
-  /// Loads the package graph for the application and all of its transitive
-  /// dependencies.
-  Future<PackageGraph> _loadPackageGraph() {
-    var lockFile = loadLockFile();
-    return Future.wait(lockFile.packages.values.map((id) {
-      var source = cache.sources[id.source];
-      return source.getDirectory(id)
-          .then((dir) => new Package.load(id.name, dir, cache.sources));
-    })).then((packages) {
-      var packageMap = new Map.fromIterable(packages, key: (p) => p.name);
-      packageMap[root.name] = root;
-      return new PackageGraph(this, lockFile, packageMap);
     });
   }
 
diff --git a/sdk/lib/_internal/pub/lib/src/http.dart b/sdk/lib/_internal/pub/lib/src/http.dart
index afaefd8..08c30fd 100644
--- a/sdk/lib/_internal/pub/lib/src/http.dart
+++ b/sdk/lib/_internal/pub/lib/src/http.dart
@@ -34,6 +34,10 @@
 
 /// An HTTP client that transforms 40* errors and socket exceptions into more
 /// user-friendly error messages.
+///
+/// This also adds a 30-second timeout to every request. This can be configured
+/// on a per-request basis by setting the 'Pub-Request-Timeout' header to the
+/// desired number of milliseconds, or to "None" to disable the timeout.
 class PubHttpClient extends http.BaseClient {
   final _requestStopwatches = new Map<http.BaseRequest, Stopwatch>();
 
@@ -55,7 +59,15 @@
       stackTrace = localStackTrace;
     }
 
-    return timeout(inner.send(request).then((streamedResponse) {
+    var timeoutLength = HTTP_TIMEOUT;
+    var timeoutString = request.headers.remove('Pub-Request-Timeout');
+    if (timeoutString == 'None') {
+      timeoutLength = null;
+    } else if (timeoutString != null) {
+      timeoutLength = int.parse(timeoutString);
+    }
+
+    var future = inner.send(request).then((streamedResponse) {
       _logResponse(streamedResponse);
 
       var status = streamedResponse.statusCode;
@@ -95,7 +107,10 @@
         }
       }
       throw error;
-    }), HTTP_TIMEOUT, 'fetching URL "${request.url}"');
+    });
+
+    if (timeoutLength == null) return future;
+    return timeout(future, timeoutLength, 'fetching URL "${request.url}"');
   }
 
   /// Logs the fact that [request] was sent, and information about it.
diff --git a/sdk/lib/_internal/pub/lib/src/package.dart b/sdk/lib/_internal/pub/lib/src/package.dart
index 7d25a2e..8f6f617 100644
--- a/sdk/lib/_internal/pub/lib/src/package.dart
+++ b/sdk/lib/_internal/pub/lib/src/package.dart
@@ -15,6 +15,18 @@
 
 /// A named, versioned, unit of code and resource reuse.
 class Package {
+  /// Compares [a] and [b] orders them by name then version number.
+  ///
+  /// This is normally used as a [Comparator] to pass to sort. This does not
+  /// take a package's description or root directory into account, so multiple
+  /// distinct packages may order the same.
+  static int orderByNameAndVersion(Package a, Package b) {
+    var name = a.name.compareTo(b.name);
+    if (name != 0) return name;
+
+    return a.version.compareTo(b.version);
+  }
+
   /// The path to the directory containing the package.
   final String dir;
 
diff --git a/sdk/lib/_internal/pub/lib/src/pubspec.dart b/sdk/lib/_internal/pub/lib/src/pubspec.dart
index e6344a4..759f01e 100644
--- a/sdk/lib/_internal/pub/lib/src/pubspec.dart
+++ b/sdk/lib/_internal/pub/lib/src/pubspec.dart
@@ -149,7 +149,7 @@
         } else {
           if (transformer.length != 1) {
             _error('"$field" must have a single key: the transformer '
-                'identifier.');
+                'identifier. Was "$transformer".');
           } else if (transformer.keys.single is! String) {
             _error('"$field" transformer identifier must be a string, but was '
                 '"$library".');
diff --git a/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart b/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart
index 186ac8e..d4994a2 100644
--- a/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart
+++ b/sdk/lib/_internal/pub/lib/src/solver/backtracking_solver.dart
@@ -144,14 +144,7 @@
       // Gather some solving metrics.
       var buffer = new StringBuffer();
       buffer.writeln('${runtimeType} took ${stopwatch.elapsed} seconds.');
-      buffer.writeln(
-          '- Requested ${cache.versionCacheMisses} version lists');
-      buffer.writeln(
-          '- Looked up ${cache.versionCacheHits} cached version lists');
-      buffer.writeln(
-          '- Requested ${cache.pubspecCacheMisses} pubspecs');
-      buffer.writeln(
-          '- Looked up ${cache.pubspecCacheHits} cached pubspecs');
+      buffer.writeln(cache.describeResults());
       log.solver(buffer);
     });
   }
@@ -491,11 +484,11 @@
       for (var dep in deps) {
         if (!dep.isRoot && !_solver.sources.contains(dep.source)) {
           throw new UnknownSourceException(id.name,
-              [new Dependency(id.name, dep)]);
+              [new Dependency(id.name, id.version, dep)]);
         }
       }
 
-      return _traverseDeps(id.name, new DependencyQueue(_solver, deps));
+      return _traverseDeps(id, new DependencyQueue(_solver, deps));
     });
   }
 
@@ -504,7 +497,8 @@
   /// Desctructively modifies [deps]. Completes to a list of packages if the
   /// traversal is complete. Completes it to an error if a failure occurred.
   /// Otherwise, recurses.
-  Future<List<PackageId>> _traverseDeps(String depender, DependencyQueue deps) {
+  Future<List<PackageId>> _traverseDeps(PackageId depender,
+      DependencyQueue deps) {
     // Move onto the next package if we've traversed all of these references.
     if (deps.isEmpty) return _traversePackage();
 
@@ -514,7 +508,7 @@
 
         // Add the dependency.
         var dependencies = _getDependencies(dep.name);
-        dependencies.add(new Dependency(depender, dep));
+        dependencies.add(new Dependency(depender.name, depender.version, dep));
 
         // If the package is barback, pub has an implicit version constraint on
         // it since pub itself uses barback too. Note that we don't check for
@@ -534,7 +528,7 @@
           // find barback.
           var barbackDep = new PackageDep(dep.name, dep.source,
               barback.supportedVersions, dep.description);
-          dependencies.add(new Dependency("pub itself", barbackDep));
+          dependencies.add(new Dependency("pub itself", null, barbackDep));
         }
 
         var constraint = _getConstraint(dep.name);
@@ -578,7 +572,7 @@
 
       if (allowed.isEmpty) {
         _solver.logSolve('no versions for ${dep.name} match $constraint');
-        throw new NoVersionException(dep.name, constraint,
+        throw new NoVersionException(dep.name, null, constraint,
             _getDependencies(dep.name));
       }
 
@@ -608,7 +602,7 @@
   /// other dependencies on the same package. Throws a [SolveFailure]
   /// exception if not. Only validates sources and descriptions, not the
   /// version.
-  void _validateDependency(PackageDep dep, String depender) {
+  void _validateDependency(PackageDep dep, PackageId depender) {
     // Make sure the dependencies agree on source and description.
     var required = _getRequired(dep.name);
     if (required == null) return;
@@ -618,7 +612,7 @@
       _solver.logSolve('source mismatch on ${dep.name}: ${required.dep.source} '
                        '!= ${dep.source}');
       throw new SourceMismatchException(dep.name,
-          [required, new Dependency(depender, dep)]);
+          [required, new Dependency(depender.name, depender.version, dep)]);
     }
 
     // Make sure all of the existing descriptions match the new reference.
@@ -627,7 +621,7 @@
       _solver.logSolve('description mismatch on ${dep.name}: '
                        '${required.dep.description} != ${dep.description}');
       throw new DescriptionMismatchException(dep.name,
-          [required, new Dependency(depender, dep)]);
+          [required, new Dependency(depender.name, depender.version, dep)]);
     }
   }
 
@@ -643,7 +637,7 @@
     // Make sure it meets the constraint.
     if (!dep.constraint.allows(selected.version)) {
       _solver.logSolve('selection $selected does not match $constraint');
-      throw new NoVersionException(dep.name, constraint,
+      throw new NoVersionException(dep.name, selected.version, constraint,
                                    _getDependencies(dep.name));
     }
 
diff --git a/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart b/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart
index c462120..b215247 100644
--- a/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart
+++ b/sdk/lib/_internal/pub/lib/src/solver/dependency_queue.dart
@@ -130,6 +130,18 @@
     }
 
     return _solver.cache.getVersions(dep.toRef()).then((versions) {
+      // If the root package depends on this one, ignore versions that don't
+      // match that constraint. Since the root package's dependency constraints
+      // won't change during solving, we can safely filter out packages that
+      // don't meet it.
+      for (var rootDep in _solver.root.immediateDependencies) {
+        if (rootDep.name == dep.name) {
+          versions = versions.where(
+              (id) => rootDep.constraint.allows(id.version));
+          break;
+        }
+      }
+
       return versions.length;
     }).catchError((error, trace) {
       // If it fails for any reason, just treat that as no versions. This
diff --git a/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart b/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
index 6ac4aad..3fc55d0 100644
--- a/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
+++ b/sdk/lib/_internal/pub/lib/src/solver/version_solver.dart
@@ -116,19 +116,19 @@
 
   /// The number of times a version list was requested and it wasn't cached and
   /// had to be requested from the source.
-  int versionCacheMisses = 0;
+  int _versionCacheMisses = 0;
 
   /// The number of times a version list was requested and the cached version
   /// was returned.
-  int versionCacheHits = 0;
+  int _versionCacheHits = 0;
 
   /// The number of times a pubspec was requested and it wasn't cached and had
   /// to be requested from the source.
-  int pubspecCacheMisses = 0;
+  int _pubspecCacheMisses = 0;
 
   /// The number of times a pubspec was requested and the cached version was
   /// returned.
-  int pubspecCacheHits = 0;
+  int _pubspecCacheHits = 0;
 
   PubspecCache(this._sources);
 
@@ -141,11 +141,11 @@
   Future<Pubspec> getPubspec(PackageId id) {
     // Complete immediately if it's already cached.
     if (_pubspecs.containsKey(id)) {
-      pubspecCacheHits++;
+      _pubspecCacheHits++;
       return new Future<Pubspec>.value(_pubspecs[id]);
     }
 
-    pubspecCacheMisses++;
+    _pubspecCacheMisses++;
 
     var source = _sources[id.source];
     return source.describe(id).then((pubspec) {
@@ -172,11 +172,10 @@
     // See if we have it cached.
     var versions = _versions[package];
     if (versions != null) {
-      versionCacheHits++;
+      _versionCacheHits++;
       return new Future.value(versions);
     }
-
-    versionCacheMisses++;
+    _versionCacheMisses++;
 
     var source = _sources[package.source];
     return source.getVersions(package.name, package.description)
@@ -194,6 +193,49 @@
   /// Returns the previously cached list of versions for the package identified
   /// by [package] or returns `null` if not in the cache.
   List<PackageId> getCachedVersions(PackageRef package) => _versions[package];
+
+  /// Returns a user-friendly output string describing metrics of the solve.
+  String describeResults() {
+    var results = '''- Requested $_versionCacheMisses version lists
+- Looked up $_versionCacheHits cached version lists
+- Requested $_pubspecCacheMisses pubspecs
+- Looked up $_pubspecCacheHits cached pubspecs
+''';
+
+    // Uncomment this to dump the visited package graph to JSON.
+    //results += _debugWritePackageGraph();
+
+    return results;
+  }
+
+  /// This dumps the set of packages that were looked at by the solver to a
+  /// JSON map whose format matches the map passed to [testResolve] in the
+  /// version solver unit tests.
+  ///
+  /// If a real-world version solve is failing, this can be used to mirror that
+  /// data to build a regression test using mock packages.
+  String _debugDescribePackageGraph() {
+    var packages = {};
+    _pubspecs.forEach((id, pubspec) {
+      var deps = {};
+      packages["${id.name} ${id.version}"] = deps;
+
+      for (var dep in pubspec.dependencies) {
+        deps[dep.name] = dep.constraint.toString();
+      }
+    });
+
+    // Add in the packages that we know of but didn't need their pubspecs.
+    _versions.forEach((ref, versions) {
+      for (var id in versions) {
+        packages.putIfAbsent("${id.name} ${id.version}", () => {});
+      }
+    });
+
+    // TODO(rnystrom): Include dev dependencies and dependency overrides.
+
+    return JSON.encode(packages);
+  }
 }
 
 /// A reference from a depending package to a package that it depends on.
@@ -201,12 +243,17 @@
   /// The name of the package that has this dependency.
   final String depender;
 
+  /// The version of the depender that has this dependency.
+  ///
+  /// This will be `null` when [depender] is the magic "pub itself" dependency.
+  final Version dependerVersion;
+
   /// The package being depended on.
   final PackageDep dep;
 
-  Dependency(this.depender, this.dep);
+  Dependency(this.depender, this.dependerVersion, this.dep);
 
-  String toString() => '$depender -> $dep';
+  String toString() => '$depender $dependerVersion -> $dep';
 }
 
 /// Base class for all failures that can occur while trying to resolve versions.
@@ -238,16 +285,16 @@
     var buffer = new StringBuffer();
     buffer.write("$_message:");
 
-    var map = {};
-    for (var dep in dependencies) {
-      map[dep.depender] = dep.dep;
-    }
+    var sorted = dependencies.toList();
+    sorted.sort((a, b) => a.depender.compareTo(b.depender));
 
-    var names = ordered(map.keys);
-
-    for (var name in names) {
+    for (var dep in sorted) {
       buffer.writeln();
-      buffer.write("- $name ${_describeDependency(map[name])}");
+      buffer.write("- ${log.bold(dep.depender)}");
+      if (dep.dependerVersion != null) {
+        buffer.write(" ${dep.dependerVersion}");
+      }
+      buffer.write(" ${_describeDependency(dep.dep)}");
     }
 
     return buffer.toString();
@@ -275,12 +322,27 @@
 class NoVersionException extends SolveFailure {
   final VersionConstraint constraint;
 
-  NoVersionException(String package, this.constraint,
+  /// The last selected version of the package that failed to meet the new
+  /// constraint.
+  ///
+  /// This will be `null` when the failure occurred because there are no
+  /// versions of the package *at all* that match the constraint. It will be
+  /// non-`null` when a version was selected, but then the solver tightened a
+  /// constraint such that that version was no longer allowed.
+  final Version version;
+
+  NoVersionException(String package, this.version, this.constraint,
       Iterable<Dependency> dependencies)
       : super(package, dependencies);
 
-  String get _message => "Package $package has no versions that match "
-      "$constraint derived from";
+  String get _message {
+    if (version == null) {
+      return "Package $package has no versions that match $constraint derived "
+          "from";
+    }
+
+    return "Package $package $version does not match $constraint derived from";
+  }
 }
 
 // TODO(rnystrom): Report the list of depending packages and their constraints.
diff --git a/sdk/lib/_internal/pub/lib/src/source.dart b/sdk/lib/_internal/pub/lib/src/source.dart
index 2927cc4..06181c5 100644
--- a/sdk/lib/_internal/pub/lib/src/source.dart
+++ b/sdk/lib/_internal/pub/lib/src/source.dart
@@ -160,16 +160,25 @@
   ///
   /// This is only called for sources with [shouldCache] set to true. By
   /// default, this uses [systemCacheDirectory] and [get].
-  Future<Package> downloadToSystemCache(PackageId id) {
+  ///
+  /// If [force] is `true`, then the package is downloaded even if it already
+  /// exists in the cache. The previous one will be deleted.
+  Future<Package> downloadToSystemCache(PackageId id, {bool force}) {
+    if (force == null) force = false;
+
     var packageDir;
     return systemCacheDirectory(id).then((p) {
       packageDir = p;
 
       // See if it's already cached.
       if (dirExists(packageDir)) {
-        if (!_isCachedPackageCorrupted(packageDir)) return true;
-        // Busted, so wipe out the package and re-download.
-        deleteEntry(packageDir);
+        if (force || _isCachedPackageCorrupted(packageDir)) {
+          // Wipe it out and re-install it.
+          deleteEntry(packageDir);
+        } else {
+          // Already downloaded.
+          return true;
+        }
       }
 
       ensureDir(path.dirname(packageDir));
@@ -220,6 +229,19 @@
         new Chain.current());
   }
 
+  /// Reinstalls all packages that have been previously installed into the
+  /// system cache by this source.
+  ///
+  /// Returns a [Pair] whose first element is the number of packages
+  /// successfully repaired and the second is the number of failures.
+  Future<Pair<int, int>> repairCachedPackages() {
+    if (shouldCache) {
+      throw new UnimplementedError("Source $name must implement this.");
+    }
+    throw new UnsupportedError("Cannot call repairCachedPackages() on an "
+        "uncached source.");
+  }
+
   /// When a [Pubspec] or [LockFile] is parsed, it reads in the description for
   /// each dependency. It is up to the dependency's [Source] to determine how
   /// that should be interpreted. This will be called during parsing to validate
diff --git a/sdk/lib/_internal/pub/lib/src/source/git.dart b/sdk/lib/_internal/pub/lib/src/source/git.dart
index 3b47cb4..563ea57 100644
--- a/sdk/lib/_internal/pub/lib/src/source/git.dart
+++ b/sdk/lib/_internal/pub/lib/src/source/git.dart
@@ -10,6 +10,7 @@
 
 import '../git.dart' as git;
 import '../io.dart';
+import '../log.dart' as log;
 import '../package.dart';
 import '../source.dart';
 import '../utils.dart';
@@ -38,7 +39,11 @@
   /// `<package name>-<url hash>`. These are used to check out the repository
   /// itself; each of the commit-specific directories are clones of a directory
   /// in `cache/`.
-  Future<Package> downloadToSystemCache(PackageId id) {
+  Future<Package> downloadToSystemCache(PackageId id, {bool force}) {
+    // Force is not supported because the cache repair command doesn't need it.
+    // Instead, it uses [resetCachedPackages].
+    assert(force != true);
+
     var revisionCachePath;
 
     return git.isInstalled.then((installed) {
@@ -118,6 +123,45 @@
     });
   }
 
+  /// Resets all cached packages back to the pristine state of the Git
+  /// repository at the revision they are pinned to.
+  Future<Pair<int, int>> repairCachedPackages() {
+    if (!dirExists(systemCacheRoot)) return new Future.value(new Pair(0, 0));
+
+    var successes = 0;
+    var failures = 0;
+
+    var packages = listDir(systemCacheRoot)
+      .where((entry) => dirExists(path.join(entry, ".git")))
+      .map((packageDir) => new Package.load(null, packageDir,
+          systemCache.sources))
+      .toList();
+
+    // Note that there may be multiple packages with the same name and version
+    // (pinned to different commits). The sort order of those is unspecified.
+    packages.sort(Package.orderByNameAndVersion);
+
+    return Future.wait(packages.map((package) {
+      log.message("Resetting Git repository for "
+          "${log.bold(package.name)} ${package.version}...");
+
+      // Remove all untracked files.
+      return git.run(["clean", "-d", "--force", "-x"],
+          workingDir: package.dir).then((_) {
+        // Discard all changes to tracked files.
+        return git.run(["reset", "--hard", "HEAD"], workingDir: package.dir);
+      }).then((_) {
+        successes++;
+      }).catchError((error, stackTrace) {
+        failures++;
+        log.error("Failed to reset ${log.bold(package.name)} "
+            "${package.version}. Error:\n$error");
+        log.fine(stackTrace);
+        failures++;
+      }, test: (error) => error is git.GitException);
+    })).then((_) => new Pair(successes, failures));
+  }
+
   // TODO(keertip): Implement getCachedPackages().
 
   /// Ensure that the canonical clone of the repository referred to by [id] (the
diff --git a/sdk/lib/_internal/pub/lib/src/source/hosted.dart b/sdk/lib/_internal/pub/lib/src/source/hosted.dart
index 781ab0c..dd5cb0f 100644
--- a/sdk/lib/_internal/pub/lib/src/source/hosted.dart
+++ b/sdk/lib/_internal/pub/lib/src/source/hosted.dart
@@ -78,28 +78,8 @@
 
   /// Downloads a package from the site and unpacks it.
   Future<bool> get(PackageId id, String destPath) {
-    return syncFuture(() {
-      var url = _makeVersionUrl(id, (server, package, version) =>
-          "$server/packages/$package/versions/$version.tar.gz");
-      log.io("Get package from $url.");
-
-      log.message('Downloading ${id.name} ${id.version}...');
-
-      // Download and extract the archive to a temp directory.
-      var tempDir = systemCache.createTempDir();
-      return httpClient.send(new http.Request("GET", url))
-          .then((response) => response.stream)
-          .then((stream) {
-        return timeout(extractTarGz(stream, tempDir), HTTP_TIMEOUT,
-            'fetching URL "$url"');
-      }).then((_) {
-        // Now that the get has succeeded, move it to the real location in the
-        // cache. This ensures that we don't leave half-busted ghost
-        // directories in the user's pub cache if a get fails.
-        renameDir(tempDir, destPath);
-        return true;
-      });
-    });
+    var parsed = _parseDescription(id.description);
+    return _download(parsed.last, parsed.first, id.version, destPath);
   }
 
   /// The system cache directory for the hosted source contains subdirectories
@@ -108,7 +88,7 @@
   /// from that site.
   Future<String> systemCacheDirectory(PackageId id) {
     var parsed = _parseDescription(id.description);
-    var dir = _getSourceDirectory(parsed.last);
+    var dir = _urlToDirectory(parsed.last);
 
     return new Future.value(
         path.join(systemCacheRoot, dir, "${parsed.first}-${id.version}"));
@@ -130,25 +110,80 @@
     return description;
   }
 
-  List<Package> getCachedPackages([String url]) {
-    if (url == null) url = defaultUrl;
+  /// Re-downloads all packages that have been previously downloaded into the
+  /// system cache from any server.
+  Future<Pair<int, int>> repairCachedPackages() {
+    if (!dirExists(systemCacheRoot)) return new Future.value(new Pair(0, 0));
 
-    var cacheDir = path.join(systemCacheRoot,
-                             _getSourceDirectory(url));
-    if (!dirExists(cacheDir)) return [];
+    var successes = 0;
+    var failures = 0;
 
-    return listDir(path.join(cacheDir)).map((entry) {
-      // TODO(keertip): instead of catching exception in pubspec parse with
-      // sdk dependency, fix to parse and report usage of sdk dependency.
-      // dartbug.com/10190
-      try {
-        return new Package.load(null, entry, systemCache.sources);
-      }  on ArgumentError catch (e) {
-        log.error(e);
-      }
-    }).where((package) => package != null).toList();
+    return Future.wait(listDir(systemCacheRoot).map((serverDir) {
+      var url = _directoryToUrl(path.basename(serverDir));
+      var packages = _getCachedPackagesInDirectory(path.basename(serverDir));
+      packages.sort(Package.orderByNameAndVersion);
+      return Future.wait(packages.map((package) {
+        return _download(url, package.name, package.version, package.dir)
+            .then((_) {
+          successes++;
+        }).catchError((error, stackTrace) {
+          failures++;
+          var message = "Failed to repair ${log.bold(package.name)} "
+              "${package.version}";
+          if (url != defaultUrl) message += " from $url";
+          log.error("$message. Error:\n$error");
+          log.fine(stackTrace);
+        });
+      }));
+    })).then((_) => new Pair(successes, failures));
   }
 
+  /// Gets all of the packages that have been downloaded into the system cache
+  /// from the default server.
+  List<Package> getCachedPackages() {
+    return _getCachedPackagesInDirectory(_urlToDirectory(defaultUrl));
+  }
+
+  /// Gets all of the packages that have been downloaded into the system cache
+  /// into [dir].
+  List<Package> _getCachedPackagesInDirectory(String dir) {
+    var cacheDir = path.join(systemCacheRoot, dir);
+    if (!dirExists(cacheDir)) return [];
+
+    return listDir(cacheDir)
+        .map((entry) => new Package.load(null, entry, systemCache.sources))
+        .toList();
+  }
+
+  /// Downloads package [package] at [version] from [server], and unpacks it
+  /// into [destPath].
+  Future<bool> _download(String server, String package, Version version,
+      String destPath) {
+    return syncFuture(() {
+      var url = Uri.parse("$server/packages/$package/versions/$version.tar.gz");
+      log.io("Get package from $url.");
+      log.message('Downloading ${log.bold(package)} ${version}...');
+
+      // Download and extract the archive to a temp directory.
+      var tempDir = systemCache.createTempDir();
+      return httpClient.send(new http.Request("GET", url))
+          .then((response) => response.stream)
+          .then((stream) {
+        return timeout(extractTarGz(stream, tempDir), HTTP_TIMEOUT,
+            'fetching URL "$url"');
+      }).then((_) {
+        // Remove the existing directory if it exists. This will happen if
+        // we're forcing a download to repair the cache.
+        if (dirExists(destPath)) deleteEntry(destPath);
+
+        // Now that the get has succeeded, move it to the real location in the
+        // cache. This ensures that we don't leave half-busted ghost
+        // directories in the user's pub cache if a get fails.
+        renameDir(tempDir, destPath);
+        return true;
+      });
+    });
+  }
   /// When an error occurs trying to read something about [package] from [url],
   /// this tries to translate into a more user friendly error message. Always
   /// throws an error, either the original one or a better one.
@@ -185,8 +220,8 @@
       var parsed = _parseDescription(description);
       var server = parsed.last;
       log.io("Finding versions of $name in "
-             "${systemCache.rootDir}/${_getSourceDirectory(server)}");
-      return getCachedPackages(server)
+             "$systemCacheRoot/${_urlToDirectory(server)}");
+      return _getCachedPackagesInDirectory(_urlToDirectory(server))
           .where((package) => package.name == name)
           .map((package) => package.version)
           .toList();
@@ -212,12 +247,58 @@
   }
 }
 
-String _getSourceDirectory(String url) {
+/// Given a URL, returns a "normalized" string to be used as a directory name
+/// for packages downloaded from the server at that URL.
+///
+/// This normalization strips off the scheme (which is presumed to be HTTP or
+/// HTTPS) and *sort of* URL-encodes it. I say "sort of" because it does it
+/// incorrectly: it uses the character's *decimal* ASCII value instead of hex.
+///
+/// This could cause an ambiguity since some characters get encoded as three
+/// digits and others two. It's possible for one to be a prefix of the other.
+/// In practice, the set of characters that are encoded don't happen to have
+/// any collisions, so the encoding is reversible.
+///
+/// This behavior is a bug, but is being preserved for compatibility.
+String _urlToDirectory(String url) {
   url = url.replaceAll(new RegExp(r"^https?://"), "");
   return replace(url, new RegExp(r'[<>:"\\/|?*%]'),
       (match) => '%${match[0].codeUnitAt(0)}');
 }
 
+/// Given a directory name in the system cache, returns the URL of the server
+/// whose packages it contains.
+///
+/// See [_urlToDirectory] for details on the mapping. Note that because the
+/// directory name does not preserve the scheme, this has to guess at it. It
+/// chooses "http" for loopback URLs (mainly to support the pub tests) and
+/// "https" for all others.
+String _directoryToUrl(String url) {
+  // Decode the pseudo-URL-encoded characters.
+  var chars = '<>:"\\/|?*%';
+  for (var i = 0; i < chars.length; i++) {
+    var c = chars.substring(i, i + 1);
+    url = url.replaceAll("%${c.codeUnitAt(0)}", c);
+  }
+
+  // Figure out the scheme.
+  var scheme = "https";
+
+  // See if it's a loopback IP address.
+  try {
+    var urlWithoutPort = url.replaceAll(new RegExp(":.*"), "");
+    var address = new io.InternetAddress(urlWithoutPort);
+    if (address.isLoopback) scheme = "http";
+  } on ArgumentError catch(error) {
+    // If we got here, it's not a raw IP address, so it's probably a regular
+    // URL.
+  }
+
+  if (url == "localhost") scheme = "http";
+
+  return "$scheme://$url";
+}
+
 /// Parses [description] into its server and package name components, then
 /// converts that to a Uri given [pattern]. Ensures the package name is
 /// properly URL encoded.
diff --git a/sdk/lib/_internal/pub/lib/src/source_registry.dart b/sdk/lib/_internal/pub/lib/src/source_registry.dart
index cd3dc3e..9d05e15 100644
--- a/sdk/lib/_internal/pub/lib/src/source_registry.dart
+++ b/sdk/lib/_internal/pub/lib/src/source_registry.dart
@@ -4,10 +4,12 @@
 
 library pub.source_registry;
 
+import 'dart:collection';
+
 import 'source.dart';
 
 /// A class that keeps track of [Source]s used for getting packages.
-class SourceRegistry {
+class SourceRegistry extends IterableBase<Source> {
   final Map<String, Source> _map;
   Source _default;
 
@@ -17,6 +19,13 @@
   /// Returns the default source, which is used when no source is specified.
   Source get defaultSource => _default;
 
+  /// Iterates over the registered sources in name order.
+  Iterator<Source> get iterator {
+    var sources = _map.values.toList();
+    sources.sort((a, b) => a.name.compareTo(b.name));
+    return sources.iterator;
+  }
+
   /// Sets the default source. This takes a string, which must be the name of a
   /// registered source.
   void setDefault(String name) {
diff --git a/sdk/lib/_internal/pub/lib/src/system_cache.dart b/sdk/lib/_internal/pub/lib/src/system_cache.dart
index 4e011dd..9cfca43 100644
--- a/sdk/lib/_internal/pub/lib/src/system_cache.dart
+++ b/sdk/lib/_internal/pub/lib/src/system_cache.dart
@@ -82,7 +82,10 @@
   ///
   /// It is an error to try downloading a package from a source with
   /// `shouldCache == false`.
-  Future<Package> download(PackageId id) {
+  ///
+  /// If [force] is `true`, then the package is downloaded even if it already
+  /// exists in the cache. The previous one will be deleted.
+  Future<Package> download(PackageId id, {bool force}) {
     var source = sources[id.source];
 
     if (!source.shouldCache) {
@@ -92,7 +95,8 @@
     var pending = _pendingDownloads[id];
     if (pending != null) return pending;
 
-    var future = source.downloadToSystemCache(id).whenComplete(() {
+    var future = source.downloadToSystemCache(id, force: force)
+        .whenComplete(() {
       _pendingDownloads.remove(id);
     });
 
diff --git a/sdk/lib/_internal/pub/lib/src/utils.dart b/sdk/lib/_internal/pub/lib/src/utils.dart
index 2dec803..d9ab8a9 100644
--- a/sdk/lib/_internal/pub/lib/src/utils.dart
+++ b/sdk/lib/_internal/pub/lib/src/utils.dart
@@ -117,9 +117,9 @@
   } else {
     runZoned(wrappedCallback, onError: (e, stackTrace) {
       if (stackTrace == null) {
-        stackTrace = new Chain([new Trace.from(stackTrace)]);
+        stackTrace = new Chain.current();
       } else {
-        stackTrace = new Chain([]);
+        stackTrace = new Chain([new Trace.from(stackTrace)]);
       }
       completer.completeError(e, stackTrace);
     });
diff --git a/sdk/lib/_internal/pub/pub.status b/sdk/lib/_internal/pub/pub.status
index 35bb8c3..9cb5d30 100644
--- a/sdk/lib/_internal/pub/pub.status
+++ b/sdk/lib/_internal/pub/pub.status
@@ -3,6 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 test/hosted/version_negotiation_test: Pass, Timeout # Issue 14346
+test/serve/web_socket/url_to_asset_id_test: Pass, Slow
 
 [ $runtime == vm && $system == windows ]
 test/serve/warns_on_assets_paths_test: Pass, Fail # Issue 15741
diff --git a/sdk/lib/_internal/pub/test/build/warns_on_assets_paths_test.dart b/sdk/lib/_internal/pub/test/build/warns_on_assets_paths_test.dart
deleted file mode 100644
index bd3bb4e..0000000
--- a/sdk/lib/_internal/pub/test/build/warns_on_assets_paths_test.dart
+++ /dev/null
@@ -1,79 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library pub_tests;
-
-import 'package:path/path.dart' as path;
-
-import '../descriptor.dart' as d;
-import '../test_pub.dart';
-
-main() {
-  initConfig();
-
-  integration('warns user about top-level "assets" directories', () {
-    d.dir(appPath, [
-      d.appPubspec(),
-      d.dir('bin', [
-        d.dir('assets')
-      ]),
-      d.dir('test', [
-        d.dir('assets')
-      ]),
-      d.dir('web', [
-        d.file('index.html'),
-        d.dir('assets')
-      ])
-    ]).create();
-
-    schedulePub(args: ['build', 'bin', 'test', 'web'],
-        error: """
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the directory "${path.join('bin', 'assets')}".
-Please rename the directory "${path.join('test', 'assets')}".
-Please rename the directory "${path.join('web', 'assets')}".
-""");
-  });
-
-  integration('warns user about top-level "assets" files', () {
-    d.dir(appPath, [
-      d.appPubspec(),
-      d.dir('bin', [
-        d.file('assets', '...')
-      ]),
-      d.dir('test', [
-        d.file('assets', '...')
-      ]),
-      d.dir('web', [
-        d.file('index.html'),
-        d.file('assets', '...')
-      ])
-    ]).create();
-
-    schedulePub(args: ['build', 'bin', 'test', 'web'],
-        error: """
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the file "${path.join('bin', 'assets')}".
-Please rename the file "${path.join('test', 'assets')}".
-Please rename the file "${path.join('web', 'assets')}".
-""");
-  });
-
-  integration('does not warn on "assets" in subdirectories', () {
-    d.dir(appPath, [
-      d.appPubspec(),
-      d.dir('web', [
-        d.file('index.html'),
-        d.dir('foo', [
-          d.dir('assets')
-        ]),
-        d.dir('bar', [
-          d.file('assets', '...')
-        ])
-      ])
-    ]).create();
-
-    schedulePub(args: ['build'], error: new RegExp(r"^$"));
-  });
-}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/empty_cache_test.dart b/sdk/lib/_internal/pub/test/cache/repair/empty_cache_test.dart
new file mode 100644
index 0000000..9f398b7
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/empty_cache_test.dart
@@ -0,0 +1,16 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../../test_pub.dart';
+
+main() {
+  initConfig();
+  integration('does nothing if the cache is empty', () {
+    // Repair them.
+    schedulePub(args: ["cache", "repair"],
+        output: "No packages in cache, so nothing to repair.");
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/handles_failure_test.dart b/sdk/lib/_internal/pub/test/cache/repair/handles_failure_test.dart
new file mode 100644
index 0000000..8519a74
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/handles_failure_test.dart
@@ -0,0 +1,58 @@
+// 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 pub_tests;
+
+import 'package:scheduled_test/scheduled_test.dart';
+import 'package:scheduled_test/scheduled_stream.dart';
+
+import '../../../lib/src/exit_codes.dart' as exit_codes;
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+  initConfig();
+  integration('handles failure to reinstall some packages', () {
+    // Only serve two packages so repairing will have a failure.
+    servePackages([
+      packageMap("foo", "1.2.3"),
+      packageMap("foo", "1.2.5")
+    ]);
+
+    // Set up a cache with some packages.
+    d.dir(cachePath, [
+      d.dir('hosted', [
+        d.async(port.then((p) => d.dir('127.0.0.1%58$p', [
+          d.dir("foo-1.2.3", [
+            d.libPubspec("foo", "1.2.3"),
+            d.file("broken.txt")
+          ]),
+          d.dir("foo-1.2.4", [
+            d.libPubspec("foo", "1.2.4"),
+            d.file("broken.txt")
+          ]),
+          d.dir("foo-1.2.5", [
+            d.libPubspec("foo", "1.2.5"),
+            d.file("broken.txt")
+          ])
+        ])))
+      ])
+    ]).create();
+
+    // Repair them.
+    var pub = startPub(args: ["cache", "repair"]);
+
+    pub.stdout.expect("Downloading foo 1.2.3...");
+    pub.stdout.expect("Downloading foo 1.2.4...");
+    pub.stdout.expect("Downloading foo 1.2.5...");
+
+    pub.stderr.expect(startsWith("Failed to repair foo 1.2.4. Error:"));
+    pub.stderr.expect("HTTP error 404: Not Found");
+
+    pub.stdout.expect("Reinstalled 2 packages.");
+    pub.stdout.expect("Failed to reinstall 1 package.");
+
+    pub.shouldExit(exit_codes.UNAVAILABLE);
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/reinstalls_git_packages_test.dart b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_git_packages_test.dart
new file mode 100644
index 0000000..138cd04
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_git_packages_test.dart
@@ -0,0 +1,68 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import 'package:path/path.dart' as path;
+import 'package:scheduled_test/scheduled_test.dart';
+
+import '../../../lib/src/io.dart';
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+  initConfig();
+  integration('reinstalls previously cached git packages', () {
+    // Create two cached revisions of foo.
+    d.git('foo.git', [
+      d.libDir('foo'),
+      d.libPubspec('foo', '1.0.0')
+    ]).create();
+
+    d.appDir({"foo": {"git": "../foo.git"}}).create();
+    pubGet();
+
+    d.git('foo.git', [
+      d.libDir('foo'),
+      d.libPubspec('foo', '1.0.1')
+    ]).commit();
+
+    pubUpgrade();
+
+    // Break them.
+    var fooDirs;
+    schedule(() {
+      // Find the cached foo packages for each revision.
+      var gitCacheDir = path.join(sandboxDir, cachePath, "git");
+      fooDirs = listDir(gitCacheDir)
+          .where((dir) => path.basename(dir).startsWith("foo-")).toList();
+
+      // Delete "foo.dart" from them.
+      for (var dir in fooDirs) {
+        deleteEntry(path.join(dir, "lib", "foo.dart"));
+      }
+    });
+
+    // Repair them.
+    schedulePub(args: ["cache", "repair"],
+        output: '''
+          Resetting Git repository for foo 1.0.0...
+          Resetting Git repository for foo 1.0.1...
+          Reinstalled 2 packages.''');
+
+    // The missing libraries should have been replaced.
+    schedule(() {
+      var fooLibs = fooDirs.map((dir) {
+        var fooDirName = path.basename(dir);
+        return d.dir(fooDirName, [
+          d.dir("lib", [d.file("foo.dart", 'main() => "foo";')])
+        ]);
+      }).toList();
+
+      d.dir(cachePath, [
+        d.dir("git", fooLibs)
+      ]).validate();
+    });
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/cache/repair/reinstalls_hosted_packages_test.dart b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_hosted_packages_test.dart
new file mode 100644
index 0000000..5df38af
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/cache/repair/reinstalls_hosted_packages_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+main() {
+  initConfig();
+  integration('reinstalls previously cached hosted packages', () {
+    servePackages([
+      packageMap("foo", "1.2.3"),
+      packageMap("foo", "1.2.4"),
+      packageMap("foo", "1.2.5"),
+      packageMap("bar", "1.2.3"),
+      packageMap("bar", "1.2.4")
+    ]);
+
+    // Set up a cache with some broken packages.
+    d.dir(cachePath, [
+      d.dir('hosted', [
+        d.async(port.then((p) => d.dir('127.0.0.1%58$p', [
+          d.dir("foo-1.2.3", [
+            d.libPubspec("foo", "1.2.3"),
+            d.file("broken.txt")
+          ]),
+          d.dir("foo-1.2.5", [
+            d.libPubspec("foo", "1.2.5"),
+            d.file("broken.txt")
+          ]),
+          d.dir("bar-1.2.4", [
+            d.libPubspec("bar", "1.2.4"),
+            d.file("broken.txt")
+          ])
+        ])))
+      ])
+    ]).create();
+
+    // Repair them.
+    schedulePub(args: ["cache", "repair"],
+        output: '''
+          Downloading bar 1.2.4...
+          Downloading foo 1.2.3...
+          Downloading foo 1.2.5...
+          Reinstalled 3 packages.''');
+
+    // The broken versions should have been replaced.
+    d.hostedCache([
+      d.dir("bar-1.2.4", [d.nothing("broken.txt")]),
+      d.dir("foo-1.2.3", [d.nothing("broken.txt")]),
+      d.dir("foo-1.2.5", [d.nothing("broken.txt")])
+    ]).validate();
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart b/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart
index 35552bb..95698fc 100644
--- a/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart
+++ b/sdk/lib/_internal/pub/test/get/path/nonexistent_dir_test.dart
@@ -20,6 +20,6 @@
 
     pubGet(error: """Could not find package foo at "$badPath".
 Depended on by:
-- myapp""");
+- myapp 0.0.0""");
   });
 }
\ No newline at end of file
diff --git a/sdk/lib/_internal/pub/test/hosted/offline_test.dart b/sdk/lib/_internal/pub/test/hosted/offline_test.dart
index b799198..981d24d 100644
--- a/sdk/lib/_internal/pub/test/hosted/offline_test.dart
+++ b/sdk/lib/_internal/pub/test/hosted/offline_test.dart
@@ -61,7 +61,7 @@
 
       pubCommand(command, args: ['--offline'], error:
           "Package foo has no versions that match >2.0.0 derived from:\n"
-          "- myapp depends on version >2.0.0");
+          "- myapp 0.0.0 depends on version >2.0.0");
     });
   });
 }
diff --git a/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart b/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart
index 3bc488a..a12ab1e 100644
--- a/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart
+++ b/sdk/lib/_internal/pub/test/implicit_barback_dependency_test.dart
@@ -115,7 +115,7 @@
 
     pubGet(error: """
 Package barback has no versions that match >=$current <$max derived from:
-- myapp depends on version any
+- myapp 0.0.0 depends on version any
 - pub itself depends on version >=$current <$max""");
   });
 
@@ -129,7 +129,7 @@
 
     pubGet(error: """
 Incompatible version constraints on barback:
-- myapp depends on version $previous
+- myapp 0.0.0 depends on version $previous
 - pub itself depends on version >=$current <$max""");
   });
 }
\ No newline at end of file
diff --git a/sdk/lib/_internal/pub/test/pub_test.dart b/sdk/lib/_internal/pub/test/pub_test.dart
index a7af4aa..168008c 100644
--- a/sdk/lib/_internal/pub/test/pub_test.dart
+++ b/sdk/lib/_internal/pub/test/pub_test.dart
@@ -93,7 +93,8 @@
           -h, --help    Print usage information for this command.
 
           Available subcommands:
-            add   Install a package.
+            add      Install a package.
+            repair   Reinstall cached packages.
      ''');
   });
 
@@ -131,7 +132,8 @@
         -h, --help    Print usage information for this command.
 
         Available subcommands:
-          add   Install a package.
+          add      Install a package.
+          repair   Reinstall cached packages.
         ''',
         exitCode: exit_codes.USAGE);
   });
@@ -176,7 +178,8 @@
         -h, --help    Print usage information for this command.
 
         Available subcommands:
-          add   Install a package.
+          add      Install a package.
+          repair   Reinstall cached packages.
         ''',
         exitCode: exit_codes.USAGE);
   });
@@ -277,7 +280,8 @@
             -h, --help    Print usage information for this command.
 
             Available subcommands:
-              add   Install a package.
+              add      Install a package.
+              repair   Reinstall cached packages.
             ''',
             exitCode: exit_codes.USAGE);
     });
diff --git a/sdk/lib/_internal/pub/test/serve/supports_user_defined_declaring_transformers.dart b/sdk/lib/_internal/pub/test/serve/supports_user_defined_declaring_transformers.dart
new file mode 100644
index 0000000..d066b2d
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/supports_user_defined_declaring_transformers.dart
@@ -0,0 +1,96 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS d.file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+const LAZY_TRANSFORMER = """
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+class LazyRewriteTransformer extends Transformer implements LazyTransformer {
+  LazyRewriteTransformer.asPlugin();
+
+  String get allowedExtensions => '.in';
+
+  Future apply(Transform transform) {
+    transform.logger.info('Rewriting \${transform.primaryInput.id}.');
+    return transform.primaryInput.readAsString().then((contents) {
+      var id = transform.primaryInput.id.changeExtension(".mid");
+      transform.addOutput(new Asset.fromString(id, "\$contents.mid"));
+    });
+  }
+
+  Future declareOutputs(DeclaringTransform transform) {
+    transform.declareOutput(transform.primaryId.changeExtension(".mid"));
+    return new Future.value();
+  }
+}
+""";
+
+const DECLARING_TRANSFORMER = """
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+class DeclaringRewriteTransformer extends Transformer
+    implements DeclaringTransformer {
+  DeclaringRewriteTransformer.asPlugin();
+
+  String get allowedExtensions => '.mid';
+
+  Future apply(Transform transform) {
+    transform.logger.info('Rewriting \${transform.primaryInput.id}.');
+    return transform.primaryInput.readAsString().then((contents) {
+      var id = transform.primaryInput.id.changeExtension(".final");
+      transform.addOutput(new Asset.fromString(id, "\$contents.final"));
+    });
+  }
+
+  Future declareOutputs(DeclaringTransform transform) {
+    transform.declareOutput(transform.primaryId.changeExtension(".final"));
+    return new Future.value();
+  }
+}
+""";
+
+main() {
+  initConfig();
+  integration("supports a user-defined declaring transformer", () {
+    d.dir(appPath, [
+      d.pubspec({
+        "name": "myapp",
+        "transformers": ["myapp/src/lazy", "myapp/src/declaring"]
+      }),
+      d.dir("lib", [d.dir("src", [
+        // Include a lazy transformer before the declaring transformer, because
+        // otherwise its behavior is indistinguishable from a normal
+        // transformer.
+        d.file("lazy.dart", LAZY_TRANSFORMER),
+        d.file("declaring.dart", DECLARING_TRANSFORMER)
+      ])]),
+      d.dir("web", [
+        d.file("foo.in", "foo")
+      ])
+    ]).create();
+
+    createLockFile('myapp', pkg: ['barback']);
+
+    var server = pubServe();
+    // The build should complete without either transformer logging anything.
+    server.stdout.expect('Build completed successfully');
+
+    requestShouldSucceed("foo.final", "foo.mid.final");
+    server.stdout.expect(emitsLines(
+        '[Info from LazyRewrite]:\n'
+        'Rewriting myapp|web/foo.in.\n'
+        '[Info from DeclaringRewrite]:\n'
+        'Rewriting myapp|web/foo.mid.'));
+    endPubServe();
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/supports_user_defined_lazy_transformers_test.dart b/sdk/lib/_internal/pub/test/serve/supports_user_defined_lazy_transformers_test.dart
new file mode 100644
index 0000000..fd88315
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/supports_user_defined_lazy_transformers_test.dart
@@ -0,0 +1,64 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS d.file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+const TRANSFORMER = """
+import 'dart:async';
+
+import 'package:barback/barback.dart';
+
+class LazyRewriteTransformer extends Transformer implements LazyTransformer {
+  LazyRewriteTransformer.asPlugin();
+
+  String get allowedExtensions => '.txt';
+
+  Future apply(Transform transform) {
+    transform.logger.info('Rewriting \${transform.primaryInput.id}.');
+    return transform.primaryInput.readAsString().then((contents) {
+      var id = transform.primaryInput.id.changeExtension(".out");
+      transform.addOutput(new Asset.fromString(id, "\$contents.out"));
+    });
+  }
+
+  Future declareOutputs(DeclaringTransform transform) {
+    transform.declareOutput(transform.primaryId.changeExtension(".out"));
+    return new Future.value();
+  }
+}
+""";
+
+main() {
+  initConfig();
+  integration("supports a user-defined lazy transformer", () {
+    d.dir(appPath, [
+      d.pubspec({
+        "name": "myapp",
+        "transformers": ["myapp/src/transformer"]
+      }),
+      d.dir("lib", [d.dir("src", [
+        d.file("transformer.dart", TRANSFORMER)
+      ])]),
+      d.dir("web", [
+        d.file("foo.txt", "foo")
+      ])
+    ]).create();
+
+    createLockFile('myapp', pkg: ['barback']);
+
+    var server = pubServe();
+    // The build should complete without the transformer logging anything.
+    server.stdout.expect('Build completed successfully');
+
+    requestShouldSucceed("foo.out", "foo.out");
+    server.stdout.expect(emitsLines(
+        '[Info from LazyRewrite]:\n'
+        'Rewriting myapp|web/foo.txt.'));
+    endPubServe();
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/serve/warns_on_assets_paths_test.dart b/sdk/lib/_internal/pub/test/serve/warns_on_assets_paths_test.dart
deleted file mode 100644
index 3d16fd1..0000000
--- a/sdk/lib/_internal/pub/test/serve/warns_on_assets_paths_test.dart
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-library pub_tests;
-
-import 'package:path/path.dart' as path;
-import 'package:scheduled_test/scheduled_stream.dart';
-import 'package:scheduled_test/scheduled_test.dart';
-
-import '../descriptor.dart' as d;
-import '../test_pub.dart';
-import 'utils.dart';
-
-main() {
-  initConfig();
-  integration('warns user about top-level "assets" directories', () {
-    d.dir(appPath, [
-      d.appPubspec(),
-      d.dir('bin', [
-        d.dir('assets')
-      ]),
-      d.dir('test', [
-        d.dir('assets')
-      ]),
-      d.dir('web', [
-        d.file('index.html'),
-        d.dir('assets')
-      ])
-    ]).create();
-
-    var pub = pubServe();
-    waitForBuildSuccess();
-
-    pub.stderr.expect(emitsLines('''
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the directory "${path.join('bin', 'assets')}".
-Please rename the directory "${path.join('test', 'assets')}".
-Please rename the directory "${path.join('web', 'assets')}".'''));
-    endPubServe();
-  });
-
-  integration('warns user about top-level "assets" files', () {
-    d.dir(appPath, [
-      d.appPubspec(),
-      d.dir('bin', [
-        d.file('assets', '...')
-      ]),
-      d.dir('test', [
-        d.file('assets', '...')
-      ]),
-      d.dir('web', [
-        d.file('index.html'),
-        d.file('assets', '...')
-      ])
-    ]).create();
-
-    var pub = pubServe();
-    waitForBuildSuccess();
-    pub.stderr.expect(emitsLines('''
-Warning: Pub reserves paths containing "assets" for using assets from packages.
-Please rename the file "${path.join('bin', 'assets')}".
-Please rename the file "${path.join('test', 'assets')}".
-Please rename the file "${path.join('web', 'assets')}".'''));
-    endPubServe();
-  });
-
-  integration('does not warn on "assets" in subdirectories', () {
-    d.dir(appPath, [
-      d.appPubspec(),
-      d.dir('web', [
-        d.file('index.html'),
-        d.dir('foo', [
-          d.dir('assets')
-        ]),
-        d.dir('bar', [
-          d.file('assets', '...')
-        ])
-      ])
-    ]).create();
-
-    var pub = pubServe();
-    waitForBuildSuccess();
-    endPubServe();
-    pub.stderr.expect(never(contains("Warning")));
-  });
-}
diff --git a/sdk/lib/_internal/pub/test/serve/warns_on_serve_from_asset_test.dart b/sdk/lib/_internal/pub/test/serve/warns_on_serve_from_asset_test.dart
new file mode 100644
index 0000000..2f4fc497c
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/serve/warns_on_serve_from_asset_test.dart
@@ -0,0 +1,56 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import 'package:scheduled_test/scheduled_stream.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import 'utils.dart';
+
+main() {
+  initConfig();
+  integration("warns if an asset from the entrypoint package's 'asset' "
+      "directory is requested", () {
+    d.dir(appPath, [
+      d.appPubspec(),
+      d.dir("asset", [
+        d.file("file.txt", "body")
+      ])
+    ]).create();
+
+    var pub = pubServe();
+    requestShouldSucceed("assets/myapp/file.txt", "body");
+
+    pub.stderr.expect(consumeThrough(emitsLines('''
+Warning: Support for the "asset" directory is deprecated and will be removed soon.
+Please move "asset/file.txt" to "lib/file.txt".''')));
+    endPubServe();
+  });
+
+  integration("warns if an asset from a dependency's 'asset' directory is "
+      "requested", () {
+    d.dir("foo", [
+      d.libPubspec("foo", "0.0.1"),
+      d.dir("asset", [
+        d.file("file.txt", "body")
+      ])
+    ]).create();
+
+    d.dir(appPath, [
+      d.appPubspec({
+        "foo": {"path": "../foo"}
+      })
+    ]).create();
+
+    var pub = pubServe(shouldGetFirst: true);
+    requestShouldSucceed("assets/foo/file.txt", "body");
+
+    pub.stderr.expect(consumeThrough(emitsLines('''
+Warning: Support for the "asset" directory is deprecated and will be removed soon.
+Please ask the maintainer of "foo" to move "asset/file.txt" to "lib/file.txt".''')));
+    endPubServe();
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/test_pub.dart b/sdk/lib/_internal/pub/test/test_pub.dart
index 42d0aa0..0eb2417 100644
--- a/sdk/lib/_internal/pub/test/test_pub.dart
+++ b/sdk/lib/_internal/pub/test/test_pub.dart
@@ -611,7 +611,7 @@
 /// [hosted] is a list of package names to version strings for dependencies on
 /// hosted packages.
 void createLockFile(String package, {Iterable<String> sandbox,
-    Iterable<String> pkg, Map<String, Version> hosted}) {
+    Iterable<String> pkg, Map<String, String> hosted}) {
   var dependencies = {};
 
   if (sandbox != null) {
diff --git a/sdk/lib/_internal/pub/test/transformer/loads_a_transformer_defined_in_an_exported_library_test.dart b/sdk/lib/_internal/pub/test/transformer/loads_a_transformer_defined_in_an_exported_library_test.dart
new file mode 100644
index 0000000..d38baa6
--- /dev/null
+++ b/sdk/lib/_internal/pub/test/transformer/loads_a_transformer_defined_in_an_exported_library_test.dart
@@ -0,0 +1,36 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS d.file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library pub_tests;
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+import '../serve/utils.dart';
+
+main() {
+  initConfig();
+  integration("loads a transformer defined in an exported library", () {
+    d.dir(appPath, [
+      d.pubspec({
+        "name": "myapp",
+        "transformers": ["myapp"]
+      }),
+      d.dir("lib", [
+        d.file("myapp.dart", "export 'src/transformer.dart';"),
+        d.dir("src", [
+          d.file("transformer.dart", REWRITE_TRANSFORMER)
+        ])
+      ]),
+      d.dir("web", [
+        d.file("foo.txt", "foo")
+      ])
+    ]).create();
+
+    createLockFile('myapp', pkg: ['barback']);
+
+    pubServe();
+    requestShouldSucceed("foo.out", "foo.out");
+    endPubServe();
+  });
+}
diff --git a/sdk/lib/_internal/pub/test/version_solver_test.dart b/sdk/lib/_internal/pub/test/version_solver_test.dart
index 11afd79..0ea5ea5 100644
--- a/sdk/lib/_internal/pub/test/version_solver_test.dart
+++ b/sdk/lib/_internal/pub/test/version_solver_test.dart
@@ -413,6 +413,32 @@
     'a 1.0.0': {},
     'b 1.0.0': {}
   }, error: noVersion(['myapp', 'b']), maxTries: 1);
+
+
+  // This is a regression test for #18300.
+  testResolve('...', {
+    "myapp 0.0.0": {
+      "angular": "any",
+      "collection": "any"
+    },
+    "analyzer 0.12.2": {},
+    "angular 0.10.0": {
+      "di": ">=0.0.32 <0.1.0",
+      "collection": ">=0.9.1 <1.0.0"
+    },
+    "angular 0.9.11": {
+      "di": ">=0.0.32 <0.1.0",
+      "collection": ">=0.9.1 <1.0.0"
+    },
+    "angular 0.9.10": {
+      "di": ">=0.0.32 <0.1.0",
+      "collection": ">=0.9.1 <1.0.0"
+    },
+    "collection 0.9.0": {},
+    "collection 0.9.1": {},
+    "di 0.0.37": {"analyzer": ">=0.13.0 <0.14.0"},
+    "di 0.0.36": {"analyzer": ">=0.13.0 <0.14.0"}
+  }, error: noVersion(['myapp', 'angular', 'collection']), maxTries: 9);
 }
 
 badSource() {
@@ -681,6 +707,27 @@
     'c': '2.0.0'
   }, maxTries: 2);
 
+  // This is similar to the above test. When getting the number of versions of
+  // a package to determine which to traverse first, versions that are
+  // disallowed by the root package's constraints should not be considered.
+  // Here, foo has more versions of bar in total (4), but fewer that meet
+  // myapp's constraints (only 2). There is no solution, but we will do less
+  // backtracking if foo is tested first.
+  testResolve('take root package constraints into counting versions', {
+    "myapp 0.0.0": {
+      "foo": ">2.0.0",
+      "bar": "any"
+    },
+    "foo 1.0.0": {"none": "2.0.0"},
+    "foo 2.0.0": {"none": "2.0.0"},
+    "foo 3.0.0": {"none": "2.0.0"},
+    "foo 4.0.0": {"none": "2.0.0"},
+    "bar 1.0.0": {},
+    "bar 2.0.0": {},
+    "bar 3.0.0": {},
+    "none 1.0.0": {}
+  }, error: noVersion(["foo", "none"]), maxTries: 2);
+
   // This sets up a hundred versions of foo and bar, 0.0.0 through 9.9.0. Each
   // version of foo depends on a baz with the same major version. Each version
   // of bar depends on a baz with the same minor version. There is only one
@@ -1372,7 +1419,7 @@
 /// The "from mock" optional suffix is the name of a source for the package.
 /// If omitted, it defaults to "mock1".
 PackageId parseSpec(String text, [String version]) {
-  var pattern = new RegExp(r"(([a-z]*)(-[a-z]+)?)( ([^ ]+))?( from (.*))?$");
+  var pattern = new RegExp(r"(([a-z_]*)(-[a-z_]+)?)( ([^ ]+))?( from (.*))?$");
   var match = pattern.firstMatch(text);
   if (match == null) {
     throw new FormatException("Could not parse spec '$text'.");
diff --git a/sdk/lib/async/schedule_microtask.dart b/sdk/lib/async/schedule_microtask.dart
index 02114d4..7581c0a 100644
--- a/sdk/lib/async/schedule_microtask.dart
+++ b/sdk/lib/async/schedule_microtask.dart
@@ -31,13 +31,6 @@
   try {
     _asyncRunCallbackLoop();
   } catch (e, s) {
-    // TODO(efortuna): Remove these debugging statements.
-    if (e is Error) {
-      print('microtask error ${e.stackTrace}');
-    } else {
-      print('microtask error $e');
-    }
-    print('microtask error stack trace: $s');
     _AsyncRun._scheduleImmediate(_asyncRunCallback);
     _nextCallback = _nextCallback.next;
     rethrow;
diff --git a/sdk/lib/core/expando.dart b/sdk/lib/core/expando.dart
index 5b239de..2ecd2dc 100644
--- a/sdk/lib/core/expando.dart
+++ b/sdk/lib/core/expando.dart
@@ -6,6 +6,21 @@
 
 /**
  * An [Expando] allows adding new properties to objects.
+ *
+ * Does not work on numbers, strings, booleans or null.
+ *
+ * An `Expando` does not hold on to the added property value after an object
+ * becomes inacessible.
+ *
+ * Since you can always create a new number that is identical to an existing
+ * number, it means that an expando property on a number could never be
+ * released. To avoid this, expando properties cannot be added to numbers.
+ * The same argument applies to strings, booleans and null, which also have
+ * literals that evaluate to identical values when they occur more than once.
+ *
+ * There is no restriction on other classes, even for compile time constant
+ * objects. Be careful if adding expando properties to compile time constants,
+ * since they will stay alive forever.
  */
 class Expando<T> {
 
@@ -32,6 +47,8 @@
    * Gets the value of this [Expando]'s property on the given
    * object. If the object hasn't been expanded, the method returns
    * [:null:].
+   *
+   * The object must not be a number, a string, a boolean or null.
    */
   external T operator [](Object object);
 
@@ -39,6 +56,8 @@
    * Sets the value of this [Expando]'s property on the given
    * object. Properties can effectively be removed again by setting
    * their value to null.
+   *
+   * The object must not be a number, a string, a boolean or null.
    */
   external void operator []=(Object object, T value);
 
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index 1bc1d73..a8b6082 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -10170,7 +10170,7 @@
 
       // Workaround for Chrome bug 229142- URIs are not resolved in new doc.
       var base = _parseDocument.createElement('base');
-      base.href = document._baseUri;
+      base.href = document.baseUri;
       _parseDocument.head.append(base);
     }
     var contextElement;
@@ -14353,32 +14353,6 @@
    * This custom element can also be instantiated via HTML using the syntax
    * `<input is="x-bar"></input>`
    *
-   * The [nativeTagName] parameter is needed by platforms without native support
-   * when subclassing a native type other than:
-   *
-   * * HtmlElement
-   * * SvgElement
-   * * AnchorElement
-   * * AudioElement
-   * * ButtonElement
-   * * CanvasElement
-   * * DivElement
-   * * ImageElement
-   * * InputElement
-   * * LIElement
-   * * LabelElement
-   * * MenuElement
-   * * MeterElement
-   * * OListElement
-   * * OptionElement
-   * * OutputElement
-   * * ParagraphElement
-   * * PreElement
-   * * ProgressElement
-   * * SelectElement
-   * * SpanElement
-   * * UListElement
-   * * VideoElement
    */
   void register(String tag, Type customElementClass, {String extendsTag}) {
     _registerCustomElement(JS('', 'window'), this, tag, customElementClass,
@@ -14420,6 +14394,19 @@
   @Experimental()
   Stream<Event> get onVisibilityChange =>
       visibilityChangeEvent.forTarget(this);
+
+  /// Creates an element upgrader which can be used to change the Dart wrapper
+  /// type for elements.
+  ///
+  /// The type specified must be a subclass of HtmlElement, when an element is
+  /// upgraded then the created constructor will be invoked on that element.
+  ///
+  /// If the type is not a direct subclass of HtmlElement then the extendsTag
+  /// parameter must be provided.
+  @Experimental()
+  ElementUpgrader createElementUpgrader(Type type, {String extendsTag}) {
+    return new _JSElementUpgrader(this, type, extendsTag);
+  }
 }
 // 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
@@ -15298,7 +15285,7 @@
 @DomName('XMLHttpRequestUpload')
 // http://xhr.spec.whatwg.org/#xmlhttprequestupload
 @Experimental()
-class HttpRequestUpload extends HttpRequestEventTarget native "XMLHttpRequestUpload,XMLHttpRequestEventTarget" {
+class HttpRequestUpload extends HttpRequestEventTarget native "XMLHttpRequestUpload" {
   // To suppress missing implicit constructor warnings.
   factory HttpRequestUpload._() { throw new UnsupportedError("Not supported"); }
 }
@@ -19904,7 +19891,7 @@
   @JSName('baseURI')
   @DomName('Node.baseURI')
   @DocsEditable()
-  final String _baseUri;
+  final String baseUri;
 
   /**
    * A list of this node's children.
@@ -27469,59 +27456,29 @@
   }
 
   // API level getter and setter for Location.
-  // TODO: The cross domain safe wrapper can be inserted here or folded into
-  // _LocationWrapper.
+  // TODO: The cross domain safe wrapper can be inserted here.
   /**
    * The current location of this window.
    *
    *     Location currentLocation = window.location;
    *     print(currentLocation.href); // 'http://www.example.com:80/'
    */
-  Location get location {
-    // Firefox work-around for Location.  The Firefox location object cannot be
-    // made to behave like a Dart object so must be wrapped.
-    var result = _location;
-    if (_isDartLocation(result)) return result;  // e.g. on Chrome.
-    if (null == _location_wrapper) {
-      _location_wrapper = new _LocationWrapper(result);
-    }
-    return _location_wrapper;
-  }
+  Location get location => _location;
 
   // TODO: consider forcing users to do: window.location.assign('string').
   /**
    * Sets the window's location, which causes the browser to navigate to the new
-   * location. [value] may be a Location object or a string.
+   * location. [value] may be a Location object or a String.
    */
   void set location(value) {
-    if (value is _LocationWrapper) {
-      _location = value._ptr;
-    } else {
-      _location = value;
-    }
+    _location = value;
   }
 
-  _LocationWrapper _location_wrapper;  // Cached wrapped Location object.
-
   // Native getter and setter to access raw Location object.
-  dynamic get _location => JS('Location|=Object', '#.location', this);
+  dynamic get _location => JS('Location|Null', '#.location', this);
   void set _location(value) {
     JS('void', '#.location = #', this, value);
   }
-  // Prevent compiled from thinking 'location' property is available for a Dart
-  // member.
-  @JSName('location')
-  _protect_location() native;
-
-  static _isDartLocation(thing) {
-    // On Firefox the code that implements 'is Location' fails to find the patch
-    // stub on Object.prototype and throws an exception.
-    try {
-      return thing is Location;
-    } catch (e) {
-      return false;
-    }
-  }
 
   /**
    * Called to draw an animation frame and then request the window to repaint
@@ -35328,7 +35285,7 @@
   if (extendsTagName == null) {
     if (baseClassName != 'HTMLElement') {
       throw new UnsupportedError('Class must provide extendsTag if base '
-          'native class is not HTMLElement');
+          'native class is not HtmlElement');
     }
   } else {
     if (!JS('bool', '(#.createElement(#) instanceof window[#])',
@@ -35369,6 +35326,63 @@
 void _initializeCustomElement(Element e) {
   // TODO(blois): Add validation that this is only in response to an upgrade.
 }
+
+/// Dart2JS implementation of ElementUpgrader
+class _JSElementUpgrader implements ElementUpgrader {
+  var _interceptor;
+  var _constructor;
+  var _nativeType;
+
+  _JSElementUpgrader(Document document, Type type, String extendsTag) {
+    var interceptorClass = findInterceptorConstructorForType(type);
+    if (interceptorClass == null) {
+      throw new ArgumentError(type);
+    }
+
+    _constructor = findConstructorForNativeSubclassType(type, 'created');
+    if (_constructor == null) {
+      throw new ArgumentError("$type has no constructor called 'created'");
+    }
+
+    // Workaround for 13190- use an article element to ensure that HTMLElement's
+    // interceptor is resolved correctly.
+    getNativeInterceptor(new Element.tag('article'));
+
+    var baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
+    if (baseClassName == null) {
+      throw new ArgumentError(type);
+    }
+
+    if (extendsTag == null) {
+      if (baseClassName != 'HTMLElement') {
+        throw new UnsupportedError('Class must provide extendsTag if base '
+            'native class is not HtmlElement');
+      }
+      _nativeType = HtmlElement;
+    } else {
+      var element = document.createElement(extendsTag);
+      if (!JS('bool', '(# instanceof window[#])',
+          element, baseClassName)) {
+        throw new UnsupportedError(
+            'extendsTag does not match base native class');
+      }
+      _nativeType = element.runtimeType;
+    }
+
+    _interceptor = JS('=Object', '#.prototype', interceptorClass);
+  }
+
+  Element upgrade(Element element) {
+    // Only exact type matches are supported- cannot be a subclass.
+    if (element.runtimeType != _nativeType) {
+      throw new ArgumentError('element is not subclass of $_nativeType');
+    }
+
+    setNativeSubclassDispatchRecord(element, _interceptor);
+    JS('', '#(#)', _constructor, element);
+    return element;
+  }
+}
 // 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.
@@ -35723,94 +35737,6 @@
   bool get repeat => throw new UnimplementedError();
   dynamic get _get_view => throw new UnimplementedError();
 }
-// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-
-// On Firefox 11, the object obtained from 'window.location' is very strange.
-// It can't be monkey-patched and seems immune to putting methods on
-// Object.prototype.  We are forced to wrap the object.
-
-class _LocationWrapper implements Location {
-
-  final _ptr;  // Opaque reference to real location.
-
-  _LocationWrapper(this._ptr);
-
-  // TODO(sra): Replace all the _set and _get calls with 'JS' forms.
-
-  // final List<String> ancestorOrigins;
-  List<String> get ancestorOrigins => _get(_ptr, 'ancestorOrigins');
-
-  // String hash;
-  String get hash => _get(_ptr, 'hash');
-  void set hash(String value) {
-    _set(_ptr, 'hash', value);
-  }
-
-  // String host;
-  String get host => _get(_ptr, 'host');
-  void set host(String value) {
-    _set(_ptr, 'host', value);
-  }
-
-  // String hostname;
-  String get hostname => _get(_ptr, 'hostname');
-  void set hostname(String value) {
-    _set(_ptr, 'hostname', value);
-  }
-
-  // String href;
-  String get href => _get(_ptr, 'href');
-  void set href(String value) {
-    _set(_ptr, 'href', value);
-  }
-
-  // final String origin;
-  String get origin {
-    if (JS('bool', '("origin" in #)', _ptr)) {
-      return JS('String', '#.origin', _ptr);
-    }
-    return '${this.protocol}//${this.host}';
-  }
-
-  // String pathname;
-  String get pathname => _get(_ptr, 'pathname');
-  void set pathname(String value) {
-    _set(_ptr, 'pathname', value);
-  }
-
-  // String port;
-  String get port => _get(_ptr, 'port');
-  void set port(String value) {
-    _set(_ptr, 'port', value);
-  }
-
-  // String protocol;
-  String get protocol => _get(_ptr, 'protocol');
-  void set protocol(String value) {
-    _set(_ptr, 'protocol', value);
-  }
-
-  // String search;
-  String get search => _get(_ptr, 'search');
-  void set search(String value) {
-    _set(_ptr, 'search', value);
-  }
-
-  void assign(String url) => JS('void', '#.assign(#)', _ptr, url);
-
-  void reload() => JS('void', '#.reload()', _ptr);
-
-  void replace(String url) => JS('void', '#.replace(#)', _ptr, url);
-
-  String toString() => JS('String', '#.toString()', _ptr);
-
-
-  static _get(p, m) => JS('var', '#[#]', p, m);
-  static _set(p, m, v) => JS('void', '#[#] = #', p, m, v);
-}
 // 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.
@@ -35900,6 +35826,15 @@
  * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
  */
 ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+
+/// A utility for changing the Dart wrapper type for elements.
+abstract class ElementUpgrader {
+  /// Upgrade the specified element to be of the Dart type this was created for.
+  ///
+  /// After upgrading the element passed in is invalid and the returned value
+  /// should be used instead.
+  Element upgrade(Element element);
+}
 // 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.
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 1a2f68a..3b10f34 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -106,7 +106,8 @@
 Future<Isolate> spawnDomUri(Uri uri, List<String> args, message) {
   // TODO(17738): Plumb arguments and return value through.
   return _Utils.spawnDomUri(uri.toString());
-}// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+}
+// 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.
 
@@ -10469,7 +10470,7 @@
 
       // Workaround for Chrome bug 229142- URIs are not resolved in new doc.
       var base = _parseDocument.createElement('base');
-      base.href = document._baseUri;
+      base.href = document.baseUri;
       _parseDocument.head.append(base);
     }
     var contextElement;
@@ -14766,32 +14767,6 @@
    * This custom element can also be instantiated via HTML using the syntax
    * `<input is="x-bar"></input>`
    *
-   * The [nativeTagName] parameter is needed by platforms without native support
-   * when subclassing a native type other than:
-   *
-   * * HtmlElement
-   * * SvgElement
-   * * AnchorElement
-   * * AudioElement
-   * * ButtonElement
-   * * CanvasElement
-   * * DivElement
-   * * ImageElement
-   * * InputElement
-   * * LIElement
-   * * LabelElement
-   * * MenuElement
-   * * MeterElement
-   * * OListElement
-   * * OptionElement
-   * * OutputElement
-   * * ParagraphElement
-   * * PreElement
-   * * ProgressElement
-   * * SelectElement
-   * * SpanElement
-   * * UListElement
-   * * VideoElement
    */
   void register(String tag, Type customElementClass, {String extendsTag}) {
     _Utils.register(this, tag, customElementClass, extendsTag);
@@ -14822,6 +14797,19 @@
   @Experimental()
   Stream<Event> get onVisibilityChange =>
       visibilityChangeEvent.forTarget(this);
+
+  /// Creates an element upgrader which can be used to change the Dart wrapper
+  /// type for elements.
+  ///
+  /// The type specified must be a subclass of HtmlElement, when an element is
+  /// upgraded then the created constructor will be invoked on that element.
+  ///
+  /// If the type is not a direct subclass of HtmlElement then the extendsTag
+  /// parameter must be provided.
+  @Experimental()
+  ElementUpgrader createElementUpgrader(Type type, {String extendsTag}) {
+    return new _VMElementUpgrader(this, type, extendsTag);
+  }
 }
 // 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
@@ -21631,7 +21619,7 @@
 
   @DomName('Node.baseURI')
   @DocsEditable()
-  String get _baseUri native "Node_baseURI_Getter";
+  String get baseUri native "Node_baseURI_Getter";
 
   /**
    * A list of this node's children.
@@ -28952,13 +28940,13 @@
     if ((blob_OR_source_OR_stream is Blob || blob_OR_source_OR_stream == null)) {
       return _createObjectURL_1(blob_OR_source_OR_stream);
     }
-    if ((blob_OR_source_OR_stream is MediaSource || blob_OR_source_OR_stream == null)) {
+    if ((blob_OR_source_OR_stream is MediaStream || blob_OR_source_OR_stream == null)) {
       return _createObjectURL_2(blob_OR_source_OR_stream);
     }
-    if ((blob_OR_source_OR_stream is _WebKitMediaSource || blob_OR_source_OR_stream == null)) {
+    if ((blob_OR_source_OR_stream is MediaSource || blob_OR_source_OR_stream == null)) {
       return _createObjectURL_3(blob_OR_source_OR_stream);
     }
-    if ((blob_OR_source_OR_stream is MediaStream || blob_OR_source_OR_stream == null)) {
+    if ((blob_OR_source_OR_stream is _WebKitMediaSource || blob_OR_source_OR_stream == null)) {
       return _createObjectURL_4(blob_OR_source_OR_stream);
     }
     throw new ArgumentError("Incorrect number or type of arguments");
@@ -38002,6 +37990,94 @@
 
   T get current => _current;
 }
+// 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.
+
+
+/// Dartium ElementUpgrader implementation.
+class _VMElementUpgrader implements ElementUpgrader {
+  final Type _type;
+  final Type _nativeType;
+
+  _VMElementUpgrader(Document document, Type type, String extendsTag) :
+      _type = type,
+      _nativeType = _validateCustomType(type).reflectedType {
+
+    if (extendsTag == null) {
+      if (_nativeType != HtmlElement) {
+        throw new UnsupportedError('Class must provide extendsTag if base '
+              'native class is not HtmlElement');
+      }
+    } else {
+      if (document.createElement(extendsTag).runtimeType != _nativeType) {
+        throw new UnsupportedError(
+            'extendsTag does not match base native class');
+      }
+    }
+  }
+
+  Element upgrade(Element element) {
+    if (element.runtimeType != _nativeType) {
+      throw new UnsupportedError('Element is incorrect type');
+    }
+    return _Utils.changeElementWrapper(element, _type);
+    return null;
+  }
+}
+
+/// Validates that the custom type is properly formed-
+///
+/// * Is a user-defined class.
+/// * Has a created constructor with zero args.
+/// * Derives from an Element subclass.
+///
+/// Then returns the native base class.
+ClassMirror _validateCustomType(Type type) {
+  ClassMirror cls = reflectClass(type);
+  if (_isBuiltinType(cls)) {
+    throw new UnsupportedError('Invalid custom element from '
+        '${(cls.owner as LibraryMirror).uri}.');
+  }
+
+  var className = MirrorSystem.getName(cls.simpleName);
+  var createdConstructor = cls.declarations[new Symbol('$className.created')];
+  if (createdConstructor == null ||
+      createdConstructor is! MethodMirror ||
+      !createdConstructor.isConstructor) {
+    throw new UnsupportedError(
+        'Class is missing constructor $className.created');
+  }
+
+  if (createdConstructor.parameters.length > 0) {
+    throw new UnsupportedError(
+        'Constructor $className.created must take zero arguments');
+  }
+
+  Symbol objectName = reflectClass(Object).qualifiedName;
+  bool isRoot(ClassMirror cls) =>
+      cls == null || cls.qualifiedName == objectName;
+  Symbol elementName = reflectClass(HtmlElement).qualifiedName;
+  bool isElement(ClassMirror cls) =>
+      cls != null && cls.qualifiedName == elementName;
+  ClassMirror superClass = cls.superclass;
+  ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
+  while(!isRoot(superClass) && !isElement(superClass)) {
+    superClass = superClass.superclass;
+    if (nativeClass == null && _isBuiltinType(superClass)) {
+      nativeClass = superClass;
+    }
+  }
+  return nativeClass;
+}
+
+
+bool _isBuiltinType(ClassMirror cls) {
+  // TODO(vsm): Find a less hackish way to do this.
+  LibraryMirror lib = cls.owner;
+  String libName = lib.uri.toString();
+  return libName.startsWith('dart:');
+}
 /**
  * A custom KeyboardEvent that attempts to eliminate cross-browser
  * inconsistencies, and also provide both keyCode and charCode information
@@ -38248,6 +38324,15 @@
  * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
  */
 ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+
+/// A utility for changing the Dart wrapper type for elements.
+abstract class ElementUpgrader {
+  /// Upgrade the specified element to be of the Dart type this was created for.
+  ///
+  /// After upgrading the element passed in is invalid and the returned value
+  /// should be used instead.
+  Element upgrade(Element element);
+}
 // 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.
@@ -38645,48 +38730,10 @@
 
   static bool isNoSuchMethodError(obj) => obj is NoSuchMethodError;
 
-  static bool _isBuiltinType(ClassMirror cls) {
-    // TODO(vsm): Find a less hackish way to do this.
-    LibraryMirror lib = cls.owner;
-    String libName = lib.uri.toString();
-    return libName.startsWith('dart:');
-  }
-
   static void register(Document document, String tag, Type type,
       String extendsTagName) {
-    // TODO(vsm): Move these checks into native code.
-    ClassMirror cls = reflectClass(type);
-    if (_isBuiltinType(cls)) {
-      throw new UnsupportedError("Invalid custom element from ${(cls.owner as LibraryMirror).uri}.");
-    }
-    var className = MirrorSystem.getName(cls.simpleName);
-    var createdConstructor = cls.declarations[new Symbol('$className.created')];
-    if (createdConstructor == null ||
-        createdConstructor is! MethodMirror ||
-        !createdConstructor.isConstructor) {
-      throw new UnsupportedError(
-          'Class is missing constructor $className.created');
-    }
+    var nativeClass = _validateCustomType(type);
 
-    if (createdConstructor.parameters.length > 0) {
-      throw new UnsupportedError(
-          'Constructor $className.created must take zero arguments');
-    }
-
-    Symbol objectName = reflectClass(Object).qualifiedName;
-    bool isRoot(ClassMirror cls) =>
-        cls == null || cls.qualifiedName == objectName;
-    Symbol elementName = reflectClass(HtmlElement).qualifiedName;
-    bool isElement(ClassMirror cls) =>
-        cls != null && cls.qualifiedName == elementName;
-    ClassMirror superClass = cls.superclass;
-    ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
-    while(!isRoot(superClass) && !isElement(superClass)) {
-      superClass = superClass.superclass;
-      if (nativeClass == null && _isBuiltinType(superClass)) {
-        nativeClass = superClass;
-      }
-    }
     if (extendsTagName == null) {
       if (nativeClass.reflectedType != HtmlElement) {
         throw new UnsupportedError('Class must provide extendsTag if base '
@@ -38703,6 +38750,8 @@
   static Element createElement(Document document, String tagName) native "Utils_createElement";
 
   static void initializeCustomElement(HtmlElement element) native "Utils_initializeCustomElement";
+
+  static void changeElementWrapper(HtmlElement element, Type type) native "Utils_changeElementWrapper";
 }
 
 class _DOMWindowCrossFrame extends NativeFieldWrapperClass2 implements
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index a262c21..1920c37 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -126,6 +126,7 @@
     if (_position < 0) {
       _controller.addError(new RangeError("Bad start position: $_position"));
       _controller.close();
+      _closeCompleter.complete();
       return;
     }
 
diff --git a/sdk/lib/io/file_system_entity.dart b/sdk/lib/io/file_system_entity.dart
index 4b8950f..3f0cf7c 100644
--- a/sdk/lib/io/file_system_entity.dart
+++ b/sdk/lib/io/file_system_entity.dart
@@ -659,7 +659,7 @@
   static String _trimTrailingPathSeparators(String path) {
     // Don't handle argument errors here.
     if (path is! String) return path;
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       while (path.length > 1 &&
              (path.endsWith(Platform.pathSeparator) ||
               path.endsWith('/'))) {
@@ -677,7 +677,7 @@
     // Don't handle argument errors here.
     if (path is! String) return path;
     if (path.isEmpty) path = '.';
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       while (!path.endsWith(Platform.pathSeparator) && !path.endsWith('/')) {
         path = "$path${Platform.pathSeparator}";
       }
diff --git a/sdk/lib/io/http.dart b/sdk/lib/io/http.dart
index 765f583..b869f39 100644
--- a/sdk/lib/io/http.dart
+++ b/sdk/lib/io/http.dart
@@ -1317,6 +1317,86 @@
   Future<HttpClientRequest> postUrl(Uri url);
 
   /**
+   * Opens a HTTP connection using the PUT method.
+   *
+   * The server is specified using [host] and [port], and the path
+   * (including possible fragment and query) is specified using
+   * [path].
+   *
+   * See [open] for details.
+   */
+  Future<HttpClientRequest> put(String host, int port, String path);
+
+  /**
+   * Opens a HTTP connection using the PUT method.
+   *
+   * The URL to use is specified in [url].
+   *
+   * See [openUrl] for details.
+   */
+  Future<HttpClientRequest> putUrl(Uri url);
+
+  /**
+   * Opens a HTTP connection using the DELETE method.
+   *
+   * The server is specified using [host] and [port], and the path
+   * (including possible fragment and query) is specified using
+   * [path].
+   *
+   * See [open] for details.
+   */
+  Future<HttpClientRequest> delete(String host, int port, String path);
+
+  /**
+   * Opens a HTTP connection using the DELETE method.
+   *
+   * The URL to use is specified in [url].
+   *
+   * See [openUrl] for details.
+   */
+  Future<HttpClientRequest> deleteUrl(Uri url);
+
+  /**
+   * Opens a HTTP connection using the PATCH method.
+   *
+   * The server is specified using [host] and [port], and the path
+   * (including possible fragment and query) is specified using
+   * [path].
+   *
+   * See [open] for details.
+   */
+  Future<HttpClientRequest> patch(String host, int port, String path);
+
+  /**
+   * Opens a HTTP connection using the PATCH method.
+   *
+   * The URL to use is specified in [url].
+   *
+   * See [openUrl] for details.
+   */
+  Future<HttpClientRequest> patchUrl(Uri url);
+
+  /**
+   * Opens a HTTP connection using the HEAD method.
+   *
+   * The server is specified using [host] and [port], and the path
+   * (including possible fragment and query) is specified using
+   * [path].
+   *
+   * See [open] for details.
+   */
+  Future<HttpClientRequest> head(String host, int port, String path);
+
+  /**
+   * Opens a HTTP connection using the HEAD method.
+   *
+   * The URL to use is specified in [url].
+   *
+   * See [openUrl] for details.
+   */
+  Future<HttpClientRequest> headUrl(Uri url);
+
+  /**
    * Sets the function to be called when a site is requesting
    * authentication. The URL requested and the security realm from the
    * server are passed in the arguments [url] and [realm].
diff --git a/sdk/lib/io/http_impl.dart b/sdk/lib/io/http_impl.dart
index 5d13f54..d3325229 100644
--- a/sdk/lib/io/http_impl.dart
+++ b/sdk/lib/io/http_impl.dart
@@ -1549,25 +1549,35 @@
     return _openUrl(method, url);
   }
 
-  Future<HttpClientRequest> get(String host,
-                                int port,
-                                String path) {
-    return open("get", host, port, path);
-  }
+  Future<HttpClientRequest> get(String host, int port, String path)
+      => open("get", host, port, path);
 
-  Future<HttpClientRequest> getUrl(Uri url) {
-    return _openUrl("get", url);
-  }
+  Future<HttpClientRequest> getUrl(Uri url) => _openUrl("get", url);
 
-  Future<HttpClientRequest> post(String host,
-                                 int port,
-                                 String path) {
-    return open("post", host, port, path);
-  }
+  Future<HttpClientRequest> post(String host, int port, String path)
+      => open("post", host, port, path);
 
-  Future<HttpClientRequest> postUrl(Uri url) {
-    return _openUrl("post", url);
-  }
+  Future<HttpClientRequest> postUrl(Uri url) => _openUrl("post", url);
+
+  Future<HttpClientRequest> put(String host, int port, String path)
+      => open("put", host, port, path);
+
+  Future<HttpClientRequest> putUrl(Uri url) => _openUrl("put", url);
+
+  Future<HttpClientRequest> delete(String host, int port, String path)
+      => open("delete", host, port, path);
+
+  Future<HttpClientRequest> deleteUrl(Uri url) => _openUrl("delete", url);
+
+  Future<HttpClientRequest> head(String host, int port, String path)
+      => open("head", host, port, path);
+
+  Future<HttpClientRequest> headUrl(Uri url) => _openUrl("head", url);
+
+  Future<HttpClientRequest> patch(String host, int port, String path)
+      => open("patch", host, port, path);
+
+  Future<HttpClientRequest> patchUrl(Uri url) => _openUrl("patch", url);
 
   void close({bool force: false}) {
     _closing = true;
diff --git a/sdk/lib/io/link.dart b/sdk/lib/io/link.dart
index 5833ac3..13c1869 100644
--- a/sdk/lib/io/link.dart
+++ b/sdk/lib/io/link.dart
@@ -163,7 +163,7 @@
   FileStat statSync() => FileStat.statSync(path);
 
   Future<Link> create(String target, {bool recursive: false}) {
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       target = _makeWindowsLinkTarget(target);
     }
     var result = recursive ? parent.create(recursive: true)
@@ -183,7 +183,7 @@
     if (recursive) {
       parent.createSync(recursive: true);
     }
-    if (Platform.operatingSystem == 'windows') {
+    if (Platform.isWindows) {
       target = _makeWindowsLinkTarget(target);
     }
     var result = _File._createLink(path, target);
diff --git a/sdk/lib/io/platform.dart b/sdk/lib/io/platform.dart
index d6109a2..c1447c4 100644
--- a/sdk/lib/io/platform.dart
+++ b/sdk/lib/io/platform.dart
@@ -100,22 +100,22 @@
   /**
    * Returns true if the operating system is Linux.
    */
-  static bool get isLinux => _operatingSystem == "linux";
+  static final bool isLinux = (_operatingSystem == "linux");
 
   /**
    * Returns true if the operating system is Mac OS.
    */
-  static bool get isMacOS => _operatingSystem == "macos";
+  static final bool isMacOS = (_operatingSystem == "macos");
 
   /**
    * Returns true if the operating system is Windows.
    */
-  static bool get isWindows => _operatingSystem == "windows";
+  static final bool isWindows = (_operatingSystem == "windows");
 
   /**
    * Returns true if the operating system is Android.
    */
-  static bool get isAndroid => _operatingSystem == "android";
+  static final bool isAndroid = (_operatingSystem == "android");
 
   /**
    * Get the environment for this process.
diff --git a/sdk/lib/io/stdio.dart b/sdk/lib/io/stdio.dart
index 9c034f4..f36dd83 100644
--- a/sdk/lib/io/stdio.dart
+++ b/sdk/lib/io/stdio.dart
@@ -40,84 +40,73 @@
 
   /**
    * Synchronously read a line from stdin. This call will block until a full
-   * line is available. The line will contain the newline character(s).
+   * line is available.
    *
-   * If end-of-file is reached, `null` is returned.
+   * The argument [encoding] can be used to changed how the input should be
+   * decoded. Default is [SYSTEM_ENCODING].
    *
-   * If end-of-file is reached after some data has already been read, that data
-   * is returned.
+   * If [retainNewlines] is `false`, the returned String will not contain the
+   * final newline. If `true`, the returned String will contain the line
+   * terminator. Default is `false`.
+   *
+   * If end-of-file is reached after any bytes have been read from stdin,
+   * that data is returned.
+   * Returns `null` if no bytes preceeded the end of input.
    */
   String readLineSync({Encoding encoding: SYSTEM_ENCODING,
                        bool retainNewlines: false}) {
     const CR = 13;
     const LF = 10;
-    var line = new StringBuffer();
-    bool end = false;
-    bool lastCharWasCR = false;
-    var error;
-
-    StreamController<List<int>> controller =
-        new StreamController<List<int>>(sync: true);
-    Stream stream = controller.stream.transform(encoding.decoder);
-    stream.listen((String str) {
-      line.write(str);
-    }, onError: (e) {
-      error = e;
-    }, onDone: () {
-      end = true;
-    });
-
-    bool empty = true;
-    while (!end) {
-      int b = readByteSync();
-
-      if (b < 0) {
-        // We didn't write the carriage return in case a line feed would be
-        // the next character. Add it now.
-        if (lastCharWasCR && !retainNewlines) controller.add([CR]);
-        controller.close();
-      } else {
-        empty = false;
-        // We consider \r\n and \n as new lines.
-        // A \r on its own is treated like a normal character.
-
-        if (b == CR) {
-          if (lastCharWasCR && !retainNewlines) {
-            // We didn't write the carriage return in case a line feed would be
-            // the next character.
-            // Add it now (since we treat it like a normal character now).
-            controller.add([CR]);
-          }
-          // We add the carriage return only if we keep new lines.
-          // Otherwise we need to wait for the next character (in case it is
-          // a line feed).
-          if (retainNewlines) controller.add([b]);
-          lastCharWasCR = true;
-        } else if (b == LF) {
-          end = true;
-          // We don't care if there was a carriage return before. If we keep
-          // the line separators it has already been added to the controller.
-          // Otherwise we don't want it anyway.
-          if (retainNewlines) controller.add([b]);
-          controller.close();
-        } else {
-          // Since the current character is not a line feed we flush the
-          // carriage return we didn't write last iteration.
-          if (lastCharWasCR) {
-            controller.add([CR]);
-            lastCharWasCR = false;
-          }
-          controller.add([b]);
+    final List line = [];
+    // On Windows, if lineMode is disabled, only CR is received.
+    bool crIsNewline = Platform.isWindows &&
+        (stdioType(stdin) == StdioType.TERMINAL) &&
+        !lineMode;
+    if (retainNewlines) {
+      int byte;
+      do {
+        byte = readByteSync();
+        if (byte < 0) {
+          break;
         }
+        line.add(byte);
+      } while (byte != LF && !(byte == CR && crIsNewline));
+      if (line.isEmpty) {
+        return null;
       }
-      if (error != null) {
-        // Error during decoding.
-        throw error;
+    } else if (crIsNewline) {
+      // CR and LF are both line terminators, neither is retained.
+      while (true) {
+        int byte = readByteSync();
+        if (byte < 0) {
+          if (line.isEmpty) return null;
+          break;
+        }
+        if (byte == LF || byte == CR) break;
+        line.add(byte);
+      }
+    } else {
+      // Case having to hande CR LF as a single unretained line terminator.
+      outer: while (true) {
+        int byte = readByteSync();
+        if (byte == LF) break;
+        if (byte == CR) {
+          do {
+            byte = readByteSync();
+            if (byte == LF) break outer;
+
+            line.add(CR);
+          } while (byte == CR);
+          // Fall through and handle non-CR character.
+        }
+        if (byte < 0) {
+          if (line.isEmpty) return null;
+          break;
+        }
+        line.add(byte);
       }
     }
-
-    if (empty) return null;
-    return line.toString();
+    return encoding.decode(line);
   }
 
   /**
diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart
index ea6a4df..65f0bcee 100644
--- a/sdk/lib/isolate/isolate.dart
+++ b/sdk/lib/isolate/isolate.dart
@@ -29,12 +29,12 @@
 }
 
 class Isolate {
-  /** Argument to `ping`: Ask for immediate response. */
-  static const int PING_ALIVE = 0;
-  /** Argument to `ping`: Ask for response after control events. */
-  static const int PING_CONTROL = 1;
-  /** Argument to `ping`: Ask for response after normal events. */
-  static const int PING_EVENT = 2;
+  /** Argument to `ping` and `kill`: Ask for immediate action. */
+  static const int IMMEDIATE = 0;
+  /** Argument to `ping` and `kill`: Ask for action before the next event. */
+  static const int BEFORE_NEXT_EVENT = 1;
+  /** Argument to `ping` and `kill`: Ask for action after normal events. */
+  static const int AS_EVENT = 2;
 
   /**
    * Control port used to send control messages to the isolate.
@@ -115,7 +115,6 @@
   external static Future<Isolate> spawnUri(
       Uri uri, List<String> args, var message, { bool paused: false });
 
-
   /**
    * Requests the isolate to pause.
    *
@@ -232,6 +231,41 @@
   }
 
   /**
+   * Requests the isolate to shut down.
+   *
+   * WARNING: This method is experimental and not handled on every platform yet.
+   *
+   * The isolate is requested to terminate itself.
+   * The [priority] argument specifies when this must happen.
+   *
+   * The [priority] must be one of [IMMEDIATE], [BEFORE_NEXT_EVENT] or
+   * [AS_EVENT].
+   * The shutdown is performed at different times depending on the priority:
+   *
+   * * `IMMEDIATE`: The the isolate shuts down as soon as possible.
+   *     Control messages are handled in order, so all previously sent control
+   *     events from this isolate will all have been processed.
+   *     The shutdown should happen no later than if sent with
+   *     `BEFORE_NEXT_EVENT`.
+   *     It may happen earlier if the system has a way to shut down cleanly
+   *     at an earlier time, even during the execution of another event.
+   * * `BEFORE_NEXT_EVENT`: The shutdown is scheduled for the next time
+   *     control returns to the event loop of the receiving isolate.
+   *     If more than one such event are scheduled, they are executed in
+   *     the order their control messages were received.
+   * * `AS_EVENT`: The shutdown does not happen until all prevously sent
+   *     non-control messages from the current isolate to the receiving isolate
+   *     have been processed.
+   *     The kill operation effectively puts the shutdown into the normal event
+   *     queue after previously sent messages, and it is affected by any control
+   *     messages that affect normal events, including `pause`.
+   *     This can be used to wait for a another event to be processed.
+   */
+  void kill([int priority = BEFORE_NEXT_EVENT]) {
+    controlPort.send(["kill", terminateCapability, priority]);
+  }
+
+  /**
    * Request that the isolate send a response on the [responsePort].
    *
    * WARNING: This method is experimental and not handled on every platform yet.
@@ -239,24 +273,25 @@
    * If the isolate is alive, it will eventually send a `null` response on
    * the response port.
    *
-   * The [pingType] must be one of [PING_ALIVE], [PING_CONTROL] or [PING_EVENT].
+   * The [pingType] must be one of [IMMEDIATE], [BEFORE_NEXT_EVENT] or
+   * [AS_EVENT].
    * The response is sent at different times depending on the ping type:
    *
-   * * `PING_ALIVE`: The the isolate responds as soon as possible.
-   *     The response should happen no later than if sent with `PING_CONTROL`.
-   *     It may be sent earlier if the system has a way to do so.
-   * * `PING_CONTROL`: The response it not sent until all previously sent
-   *     control messages from the current isolate to the receiving isolate
-   *     have been processed. This can be used to wait for
-   *     previously sent control messages.
-   * * `PING_EVENT`: The response is not sent until all prevously sent
+   * * `IMMEDIATE`: The the isolate responds as soon as it receives the
+   *     control message.
+   * * `BEFORE_NEXT_EVENT`: The response is scheduled for the next time
+   *     control returns to the event loop of the receiving isolate.
+   *     If more than one such event are scheduled, they are executed in
+   *     the order their control messages were received.
+   * * `AS_EVENT`: The response is not sent until all prevously sent
    *     non-control messages from the current isolate to the receiving isolate
    *     have been processed.
-   *     The ping effectively puts the resonse into the normal event queue after
-   *     previously sent messages.
+   *     The ping effectively puts the response into the normal event queue
+   *     after previously sent messages, and it is affected by any control
+   *     messages that affect normal events, including `pause`.
    *     This can be used to wait for a another event to be processed.
    */
-  void ping(SendPort responsePort, [int pingType = PING_ALIVE]) {
+  void ping(SendPort responsePort, [int pingType = IMMEDIATE]) {
     var message = new List(3)
         ..[0] = "ping"
         ..[1] = responsePort
diff --git a/sdk/lib/profiler/profiler.dart b/sdk/lib/profiler/profiler.dart
new file mode 100644
index 0000000..56614dd
--- /dev/null
+++ b/sdk/lib/profiler/profiler.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library dart.profiler;
+
+/// A UserTag can be used to group samples in the Observatory profiler.
+abstract class UserTag {
+  /// The maximum number of UserTag instances that can be created by a program.
+  static const MAX_USER_TAGS = 64;
+
+  factory UserTag(String label) => new _FakeUserTag(label);
+
+  /// Label of [this].
+  String get label;
+
+  /// Make [this] the current tag for the isolate.
+  makeCurrent();
+}
+
+// This is a fake implementation of UserTag so that code can compile and run
+// in dart2js.
+class _FakeUserTag implements UserTag {
+  static List _instances = [];
+
+  _FakeUserTag.real(this.label);
+
+  factory _FakeUserTag(String label) {
+    // Canonicalize by name.
+    for (var tag in _instances) {
+      if (tag.label == label) {
+        return tag;
+      }
+    }
+    // Throw an exception if we've reached the maximum number of user tags.
+    if (_instances.length == UserTag.MAX_USER_TAGS) {
+      throw new UnsupportedError(
+          'UserTag instance limit (${UserTag.MAX_USER_TAGS}) reached.');
+    }
+    // Create a new instance and add it to the instance list.
+    var instance = new _FakeUserTag.real(label);
+    _instances.add(instance);
+    return instance;
+  }
+
+  final String label;
+
+  makeCurrent() {
+    _currentTag = this;
+  }
+}
+
+var _currentTag = null;
+
+/// Returns the current [UserTag] for the isolate.
+UserTag getCurrentTag() {
+  return _currentTag;
+}
+
+/// Sets the current [UserTag] for the isolate to null. Returns current tag
+/// before clearing.
+UserTag clearCurrentTag() {
+  var old = _currentTag;
+  _currentTag = null;
+  return old;
+}
\ No newline at end of file
diff --git a/sdk/lib/profiler/profiler_sources.gypi b/sdk/lib/profiler/profiler_sources.gypi
new file mode 100644
index 0000000..910d798
--- /dev/null
+++ b/sdk/lib/profiler/profiler_sources.gypi
@@ -0,0 +1,10 @@
+# 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.
+
+{
+  'sources': [
+    'profiler.dart',
+    # The above file needs to be first if additional parts are added to the lib.
+  ],
+}
diff --git a/tests/co19/co19-analyzer.status b/tests/co19/co19-analyzer.status
index 34d3e3e..5f74033 100644
--- a/tests/co19/co19-analyzer.status
+++ b/tests/co19/co19-analyzer.status
@@ -15,9 +15,6 @@
 # TBF: infinite look: class A {const A();final m = const A();}
 Language/12_Expressions/01_Constants_A17_t03: fail
 
-# TBF: it seems that "12.15.3 Unqualied Invocation" was changed, so it may be not an error anymore to call unknown function from top-level
-Language/13_Statements/04_Local_Function_Declaration_A04_t02: Fail # co19-roll r641: Please triage this failure
-
 # TBF: when we override "foo([x = 0]) {}" with "foo([x]) {}" we should report warning - different default value
 Language/07_Classes/1_Instance_Methods_A04_t02: MissingStaticWarning
 Language/07_Classes/4_Abstract_Instance_Members_A07_t04: MissingStaticWarning
@@ -28,18 +25,10 @@
 # co19 issue #442, undefined name "Expect"
 Language/15_Types/4_Interface_Types_A08_t03: fail, OK
 
-# co19 issue #455, undeclared identifier is static warning
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t10: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t12: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t15: fail, OK
-
 # co19 issue #438, Static variables are initialized lazily, need not be constants
 Language/12_Expressions/01_Constants_A16_t01: fail, OK
 Language/12_Expressions/01_Constants_A16_t02: fail, OK
 
-# co19 issue #454 (wrongly closed)
-Language/12_Expressions/12_Instance_Creation/1_New_A01_t04: fail, OK
-
 # co19 issue #543: invocation of a non-function
 Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A03_t02: fail, OK
 
@@ -116,7 +105,6 @@
 Language/05_Variables/05_Variables_A06_t01: MissingCompileTimeError # co19-roll r651: Please triage this failure
 Language/05_Variables/05_Variables_A06_t02: MissingCompileTimeError # co19-roll r651: Please triage this failure
 Language/05_Variables/05_Variables_A06_t03: MissingCompileTimeError # co19-roll r651: Please triage this failure
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t13: CompileTimeError # co19-roll r651: Please triage this failure
 Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t17: MissingCompileTimeError # co19-roll r651: Please triage this failure
 Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t18: MissingCompileTimeError # co19-roll r651: Please triage this failure
 
@@ -131,8 +119,8 @@
 LibTest/typed_data/Float32x4/lessThan_A01_t01: Skip # co19 issue 656
 LibTest/typed_data/Float32x4/lessThanOrEqual_A01_t01: Skip # co19 issue 656
 
-WebPlatformTest1/shadow-dom/*: CompileTimeError # co19 issue 677
-WebPlatformTest1/html-templates/*: CompileTimeError # co19 issue 677
+WebPlatformTest1/shadow-dom/*: StaticWarning # co19 issue 677
+WebPlatformTest1/html-templates/*: StaticWarning # co19 issue 677
 WebPlatformTest1/custom-elements/*: Pass, StaticWarning # Issue 18095.
 
 # co19 roll to r706: Please triage all these issues.
diff --git a/tests/co19/co19-analyzer2.status b/tests/co19/co19-analyzer2.status
index a0c96a0..a60ce5f 100644
--- a/tests/co19/co19-analyzer2.status
+++ b/tests/co19/co19-analyzer2.status
@@ -15,9 +15,6 @@
 # TBF: infinite look: class A {const A();final m = const A();}
 Language/12_Expressions/01_Constants_A17_t03: fail
 
-# TBF: it seems that "12.15.3 Unqualied Invocation" was changed, so it may be not an error anymore to call unknown function from top-level
-Language/13_Statements/04_Local_Function_Declaration_A04_t02: Fail # co19-roll r641: Please triage this failure
-
 # TBF: when we override "foo([x = 0]) {}" with "foo([x]) {}" we should report warning - different default value
 Language/07_Classes/1_Instance_Methods_A04_t02: MissingStaticWarning
 Language/07_Classes/4_Abstract_Instance_Members_A07_t04: MissingStaticWarning
@@ -28,18 +25,10 @@
 # co19 issue #442, undefined name "Expect"
 Language/15_Types/4_Interface_Types_A08_t03: fail, OK
 
-# co19 issue #455, undeclared identifier is static warning
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t10: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t12: fail, OK
-Language/14_Libraries_and_Scripts/1_Imports_A02_t15: fail, OK
-
 # co19 issue #438, Static variables are initialized lazily, need not be constants
 Language/12_Expressions/01_Constants_A16_t01: fail, OK
 Language/12_Expressions/01_Constants_A16_t02: fail, OK
 
-# co19 issue #454 (wrongly closed)
-Language/12_Expressions/12_Instance_Creation/1_New_A01_t04: fail, OK
-
 # co19 issue #543: invocation of a non-function
 Language/12_Expressions/14_Function_Invocation/4_Function_Expression_Invocation_A03_t02: fail, OK
 
@@ -116,7 +105,6 @@
 Language/05_Variables/05_Variables_A06_t01: MissingCompileTimeError # co19-roll r651: Please triage this failure
 Language/05_Variables/05_Variables_A06_t02: MissingCompileTimeError # co19-roll r651: Please triage this failure
 Language/05_Variables/05_Variables_A06_t03: MissingCompileTimeError # co19-roll r651: Please triage this failure
-Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t13: CompileTimeError # co19-roll r651: Please triage this failure
 Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t17: MissingCompileTimeError # co19-roll r651: Please triage this failure
 Language/12_Expressions/14_Function_Invocation/3_Unqualified_Invocation_A01_t18: MissingCompileTimeError # co19-roll r651: Please triage this failure
 
@@ -131,8 +119,8 @@
 LibTest/typed_data/Float32x4/lessThan_A01_t01: Skip # co19 issue 656
 LibTest/typed_data/Float32x4/lessThanOrEqual_A01_t01: Skip # co19 issue 656
 
-WebPlatformTest1/shadow-dom/*: CompileTimeError # co19 issue 677
-WebPlatformTest1/html-templates/*: CompileTimeError # co19 issue 677
+WebPlatformTest1/shadow-dom/*: StaticWarning # co19 issue 677
+WebPlatformTest1/html-templates/*: StaticWarning # co19 issue 677
 WebPlatformTest1/custom-elements/*: Pass, StaticWarning # Issue 18095.
 
 # co19 roll to r706: Please triage all these issues.
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 3e8938e..c1aef28 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -907,53 +907,47 @@
 LibTest/typed_data/Uint8List/toList_A01_t01: fail, timeout # co19-roll r559: Please triage this failure
 
 [ $compiler == dart2js && ($runtime == d8 || $runtime == jsshell) ]
-LibTest/async/Future/Future.delayed_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Future/Future.delayed_A03_t01: fail # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/first_A01_t01: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/first_A02_t02: fail # co19-roll r546: Please triage this failure
-LibTest/async/Stream/Stream.periodic_A02_t01: fail # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/cancel_A01_t01: fail # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/Timer_A01_t01: fail # Issue 7728, timer not supported in d8/jsshell
-
 LibTest/typed_data/Float32x4List/Float32x4List.view_A06_t01: fail # co19-roll r587: Please triage this failure
 Language/12_Expressions/00_Object_Identity/1_Object_Identity_A05_t02: RuntimeError # co19-roll r607: Please triage this failure
 Language/12_Expressions/17_Getter_Invocation_A07_t02: RuntimeError # co19-roll r607: Please triage this failure
-LibTest/isolate/ReceivePort/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/asBroadcastStream_A03_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/isActive_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Future/wait_A01_t07: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/isActive_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Zone/createPeriodicTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Zone/createTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/start_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/typed_data/Int32x4/operator_OR_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
+LibTest/typed_data/Int32x4/operator_OR_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+
+[ $compiler == dart2js && $runtime == jsshell ]
+LibTest/async/Future/Future.delayed_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Future/Future.delayed_A03_t01: fail # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/first_A01_t01: fail # co19-roll r546: Please triage this failure
+LibTest/async/Stream/first_A02_t02: fail # co19-roll r546: Please triage this failure
+LibTest/async/Stream/Stream.periodic_A02_t01: fail # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/cancel_A01_t01: fail # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/Timer_A01_t01: fail # Issue 7728, timer not supported in jsshell
+
+LibTest/isolate/ReceivePort/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/asBroadcastStream_A04_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/asBroadcastStream_A03_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/isActive_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Future/wait_A01_t07: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/isActive_A01_t02: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Zone/createPeriodicTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Zone/createTimer_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/start_A01_t03: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
 LibTest/core/Stopwatch/start_A01_t01: RuntimeError # Please triage this failure
 LibTest/core/Stopwatch/start_A01_t02: RuntimeError # Please triage this failure
 LibTest/core/Stopwatch/elapsedTicks_A01_t01: RuntimeError # Please triage this failure
 
-LibTest/async/Stream/Stream.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Stream/Stream.periodic_A03_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/Timer.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/async/Timer/Timer.periodic_A02_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
+LibTest/async/Stream/Stream.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Stream/Stream.periodic_A03_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/Timer.periodic_A01_t01: RuntimeError # Issue 7728, timer not supported in jsshell
+LibTest/async/Timer/Timer.periodic_A02_t01: RuntimeError # Issue 7728, timer not supported in jsshell
 
 [ $compiler == dart2js && $runtime == d8 ]
-LibTest/core/Stopwatch/elapsedInMs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedInUs_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsedTicks_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t02: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/elapsed_A01_t03: RuntimeError # Issue 7728, timer not supported in d8/jsshell
-LibTest/core/Stopwatch/stop_A01_t01: RuntimeError # Issue 7728, timer not supported in d8/jsshell
 LibTest/math/sin_A01_t02: RuntimeError # V8 issue 3006, https://code.google.com/p/v8/issues/detail?id=3006
 
 [ $compiler == dart2js && $minified ]
diff --git a/tests/co19/co19-dartium.status b/tests/co19/co19-dartium.status
index ae27ff4..ba51748 100644
--- a/tests/co19/co19-dartium.status
+++ b/tests/co19/co19-dartium.status
@@ -202,7 +202,7 @@
 WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/attributes/test-004_t01: RuntimeError # Issue 17758.  Please triage this failure.
 WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/attributes/test-004_t02: RuntimeError # Issue 17758.  Please triage this failure.
 WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-element-interface/methods/elements-001_t01: RuntimeError # Issue 17758.  Please triage this failure.
-WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-001_t01: RuntimeError # Issue 17758.  Please triage this failure.
+WebPlatformTest1/shadow-dom/elements-and-dom-objects/extensions-to-event-interface/event-path-001_t01: RuntimeError, Timeout # Issue 17758.  Please triage this failure.
 WebPlatformTest1/shadow-dom/elements-and-dom-objects/the-content-html-element/test-006_t01: RuntimeError # Issue 17758.  Please triage this failure.
 WebPlatformTest1/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-001_t01: RuntimeError # Issue 17758.  Please triage this failure.
 WebPlatformTest1/shadow-dom/elements-and-dom-objects/the-shadow-html-element/test-005_t01: RuntimeError # Issue 17758.  Please triage this failure.
@@ -233,9 +233,12 @@
 
 LibTest/async/Timer/Timer_A01_t01: RuntimeError, Pass # Issue 16475
 
+[ $compiler == none && $mode == debug && ($runtime == dartium || $runtime == ContentShellOnAndroid ) && $checked ]
+LibTest/collection/ListMixin/ListMixin_class_A01_t01: Pass, Timeout # co19-roll r706: Please triage this failure
+LibTest/collection/ListBase/ListBase_class_A01_t01: Pass, Timeout # co19-roll r706: Please triage this failure
+
 [ $compiler == none && ($runtime == dartium || $runtime == ContentShellOnAndroid ) && $checked ]
 LibTest/core/List/removeAt_A02_t01: Fail # co19-roll r641: Please triage this failure
-
 # New Dartium checked failures on new co19 browser tests in co19 revision 706.
 LayoutTests/fast/html/article-element_t01: RuntimeError # Issue 17758.  Please triage this failure.
 LayoutTests/fast/html/aside-element_t01: RuntimeError # Issue 17758.  Please triage this failure.
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index a325b82..15da15f 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -51,7 +51,6 @@
 
 [ $runtime == vm ]
 # These flaky tests also fail with dart2dart.
-LibTest/math/Rectangle/boundingBox_A01_t01: Pass, RuntimeError # co19-roll r607: Please triage this failure
 LibTest/math/MutableRectangle/MutableRectangle.fromPoints_A01_t01: Pass, RuntimeError # co19-roll r607: Please triage this failure
 
 [ $compiler == none && $runtime == vm && $checked ]
diff --git a/tests/compiler/dart2js/dart2js.status b/tests/compiler/dart2js/dart2js.status
index 770e62f..8af24c6 100644
--- a/tests/compiler/dart2js/dart2js.status
+++ b/tests/compiler/dart2js/dart2js.status
@@ -2,7 +2,6 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-identity_test: Fail # Issue 6638
 boolified_operator_test: Fail # Issue 8001
 
 # simple_function_subtype_test is temporarily(?) disabled due to new method for
diff --git a/tests/compiler/dart2js/dart_backend_test.dart b/tests/compiler/dart2js/dart_backend_test.dart
index 48ba841..23fa7ac 100644
--- a/tests/compiler/dart2js/dart_backend_test.dart
+++ b/tests/compiler/dart2js/dart_backend_test.dart
@@ -183,8 +183,10 @@
 }
 
 testVariableDefinitions() {
-  testDart2Dart('main(){var x,y;final String s=null;}');
-  testDart2Dart('main(){final int x=0,y=0;final String s=null;}');
+  testDart2Dart('main(){var x,y;final String s=null;}',
+      continuation: (String s) { Expect.equals('main(){}', s); });
+  testDart2Dart('main(){final int x=0,y=0;final String s=null;}',
+      continuation: (String s) { Expect.equals('main(){}', s); });
   testDart2Dart('foo(f,g){}main(){foo(1,2);}');
   testDart2Dart('foo(f(arg)){}main(){foo(main);}');
   // A couple of static/finals inside a class.
@@ -388,7 +390,7 @@
   var expectedResult =
     'topfoo(){}'
     'class A{foo(){}}'
-    'A_topfoo(){var x=5;}'
+    'A_topfoo(){}'
     'class A_A{num foo(){}A_A.fromFoo(){}A myliba;List<A_A> mylist;}'
     'A getA(){}'
     'main(){var a=new A();a.foo();var b=new A_A.fromFoo();b.foo();'
diff --git a/tests/compiler/dart2js/identity_test.dart b/tests/compiler/dart2js/identity_test.dart
index 50c768b5..eec4136 100644
--- a/tests/compiler/dart2js/identity_test.dart
+++ b/tests/compiler/dart2js/identity_test.dart
@@ -20,7 +20,7 @@
   // Check that no boolify code is generated.
   RegExp regexp = new RegExp("=== true");
   Iterator matches = regexp.allMatches(generated).iterator;
-  Expect.isFalse(matches.hasNext);
+  Expect.isFalse(matches.moveNext());
 
   regexp = new RegExp("===");
   matches = regexp.allMatches(generated).iterator;
diff --git a/tests/compiler/dart2js/js_parser_test.dart b/tests/compiler/dart2js/js_parser_test.dart
index 5109ccf..db6f43a 100644
--- a/tests/compiler/dart2js/js_parser_test.dart
+++ b/tests/compiler/dart2js/js_parser_test.dart
@@ -96,7 +96,7 @@
   // Empty object literal.
   testExpression('foo({}, {})');
   // *Can't handle non-empty object literals
-  testError('foo({meaning: 42})', 'Expected RBRACE');
+  testExpression('foo({meaning: 42})');
   // Literals.
   testExpression('x(false, true, null)');
   // *We should really throw here.
diff --git a/tests/compiler/dart2js/memory_compiler.dart b/tests/compiler/dart2js/memory_compiler.dart
index 392f51b..a279cad 100644
--- a/tests/compiler/dart2js/memory_compiler.dart
+++ b/tests/compiler/dart2js/memory_compiler.dart
@@ -165,7 +165,7 @@
                                    {});
   if (cachedCompiler != null) {
     compiler.coreLibrary = cachedCompiler.libraries['dart:core'];
-    compiler.types = cachedCompiler.types;
+    compiler.types = cachedCompiler.types.copy(compiler);
     cachedCompiler.libraries.forEach((String uri, library) {
       if (library.isPlatformLibrary) {
         compiler.libraries[uri] = library;
@@ -193,6 +193,27 @@
             treeElements;
       }
     });
+
+    // One potential problem that can occur when reusing elements is that there
+    // is a stale reference to an old compiler object.  By nulling out the old
+    // compiler's fields, such stale references are easier to identify.
+    cachedCompiler.scanner = null;
+    cachedCompiler.dietParser = null;
+    cachedCompiler.parser = null;
+    cachedCompiler.patchParser = null;
+    cachedCompiler.libraryLoader = null;
+    cachedCompiler.validator = null;
+    cachedCompiler.resolver = null;
+    cachedCompiler.closureToClassMapper = null;
+    cachedCompiler.checker = null;
+    cachedCompiler.irBuilder = null;
+    cachedCompiler.typesTask = null;
+    cachedCompiler.backend = null;
+    cachedCompiler.enqueuer = null;
+    cachedCompiler.deferredLoadTask = null;
+    cachedCompiler.mirrorUsageAnalyzerTask = null;
+    cachedCompiler.dumpInfoTask = null;
+    cachedCompiler.buildId = null;
   }
   return compiler;
 }
diff --git a/tests/compiler/dart2js/message_kind_test.dart b/tests/compiler/dart2js/message_kind_test.dart
index b2f1e46..b3cca95 100644
--- a/tests/compiler/dart2js/message_kind_test.dart
+++ b/tests/compiler/dart2js/message_kind_test.dart
@@ -48,18 +48,10 @@
   asyncTest(() => Future.forEach(examples, (String name) {
     print("Checking '$name'.");
     Stopwatch sw = new Stopwatch()..start();
-    bool useCachedCompiler = true;
-    if (name == 'MISSING_LIBRARY_NAME') {
-      // TODO(johnniwinther): Found out why we cannot use the cached compiler
-      // for this message kind.
-      cachedCompiler = null;
-    }
-    return check(kinds[name], cachedCompiler).
-        then((var compiler) {
-          cachedCompiler = compiler;
-          sw.stop();
-          print("Checked '$name' in ${sw.elapsedMilliseconds}ms.");
-        });
-    }
-  ));
+    return check(kinds[name], cachedCompiler).then((var compiler) {
+      cachedCompiler = compiler;
+      sw.stop();
+      print("Checked '$name' in ${sw.elapsedMilliseconds}ms.");
+    });
+  }));
 }
diff --git a/tests/compiler/dart2js/source_map_validator_helper.dart b/tests/compiler/dart2js/source_map_validator_helper.dart
index 934a63c..988c60c 100644
--- a/tests/compiler/dart2js/source_map_validator_helper.dart
+++ b/tests/compiler/dart2js/source_map_validator_helper.dart
@@ -14,9 +14,47 @@
   Uri mapUri = getMapUri(targetUri);
   SingleMapping sourceMap = getSourceMap(mapUri);
   checkFileReferences(targetUri, mapUri, sourceMap);
+  checkIndexReferences(targetUri, mapUri, sourceMap);
   checkRedundancy(sourceMap);
 }
 
+checkIndexReferences(Uri targetUri, Uri mapUri, SingleMapping sourceMap) {
+  List<String> target =
+      new File.fromUri(targetUri).readAsStringSync().split('\n');
+  int urlsLength = sourceMap.urls.length;
+  List<List<String>> sources = new List(urlsLength);
+  print('Reading sources');
+  for (int i = 0; i < urlsLength; i++) {
+    sources[i] = new File.fromUri(mapUri.resolve(sourceMap.urls[i])).
+        readAsStringSync().split('\n');
+  }
+
+  sourceMap.lines.forEach((TargetLineEntry line) {
+    Expect.isTrue(line.line >= 0);
+    Expect.isTrue(line.line < target.length);
+    for (TargetEntry entry in line.entries) {
+      int urlIndex = entry.sourceUrlId;
+
+      // TODO(zarah): Entry columns sometimes point one or more characters too
+      // far. Incomment this check when this is fixed.
+      //
+      // Expect.isTrue(entry.column < target[line.line].length);
+      Expect.isTrue(entry.column >= 0);
+      Expect.isTrue(urlIndex == null ||
+          (urlIndex >= 0 && urlIndex < urlsLength));
+      Expect.isTrue(entry.sourceLine == null ||
+          (entry.sourceLine >= 0 &&
+           entry.sourceLine < sources[urlIndex].length));
+      Expect.isTrue(entry.sourceColumn == null ||
+          (entry.sourceColumn >= 0 &&
+           entry.sourceColumn < sources[urlIndex][entry.sourceLine].length));
+      Expect.isTrue(entry.sourceNameId == null ||
+          (entry.sourceNameId >= 0 &&
+           entry.sourceNameId < sourceMap.names.length));
+    }
+  });
+}
+
 checkFileReferences(Uri targetUri, Uri mapUri, SingleMapping sourceMap) {
   Expect.equals(targetUri, mapUri.resolve(sourceMap.targetUrl));
   print('Checking sources');
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index 409e87d..bd7d93f 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -47,7 +47,7 @@
 [ $compiler == dart2js && ($runtime == drt || $runtime == ff || $runtime == safari || $runtime == chrome || $runtime == chromeOnAndroid) ]
 isolate2_test/01: Fail # Issue 14458.
 
-[ $jscl ]
+[ $runtime == jsshell ]
 timer_test: Fail # Issue 7728.
 
 [ $runtime == none ]
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 4b2c4fa..04cd3d3 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -42,10 +42,6 @@
 string_from_environment3_test/03: Fail
 string_from_environment3_test/04: Fail
 
-[ $compiler == dartanalyzer ]
-string_from_environment_test: Crash # http://dartbug.com/17234
-int_from_environment2_test: Crash # http://dartbug.com/17234
-
 [ $compiler == none ]
 unicode_test: Fail        # Bug 6706
 compare_to2_test: Fail    # Bug 4018
@@ -109,10 +105,10 @@
 
 [ $compiler == dart2js && ($runtime == firefox || $runtime == safari || $runtime == chrome || $runtime == drt) ]
 
-[ $compiler == dart2js && ($runtime == d8 || $runtime == chrome || $runtime == drt || $runtime == safari) ]
+[ $compiler == dart2js && ($runtime == chrome || $runtime == drt || $runtime == safari) ]
 string_trimlr_test/none: Fail # Bug in v8. Fixed in v8 r19222, 2014-02-10.
 
-[ $compiler == dart2js && ($runtime == d8 || $runtime == drt) ]
+[ $compiler == dart2js && ( $runtime == drt) ]
 string_case_test/02: Fail, OK  # Bug in our version of V8.
 
 [ $compiler == dart2js && ($runtime == ie9 || $runtime == ie10) ]
diff --git a/tests/html/custom/attribute_changed_callback_test.html b/tests/html/custom/attribute_changed_callback_test.html
index 8f54393..cf1042b 100644
--- a/tests/html/custom/attribute_changed_callback_test.html
+++ b/tests/html/custom/attribute_changed_callback_test.html
@@ -17,8 +17,6 @@
   <h1> Running attribute_changed_callback_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/constructor_calls_created_synchronously_test.html b/tests/html/custom/constructor_calls_created_synchronously_test.html
index 531162e..3ba2ba8 100644
--- a/tests/html/custom/constructor_calls_created_synchronously_test.html
+++ b/tests/html/custom/constructor_calls_created_synchronously_test.html
@@ -17,8 +17,6 @@
   <h1> Running entered_left_view_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/created_callback_test.html b/tests/html/custom/created_callback_test.html
index 424db1e..e68ef23 100644
--- a/tests/html/custom/created_callback_test.html
+++ b/tests/html/custom/created_callback_test.html
@@ -17,8 +17,6 @@
   <h1> Running created_callback_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/document_register_basic_test.html b/tests/html/custom/document_register_basic_test.html
index f83837e..f87df4b 100644
--- a/tests/html/custom/document_register_basic_test.html
+++ b/tests/html/custom/document_register_basic_test.html
@@ -17,8 +17,6 @@
   <h1> Running document_register_basic_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/document_register_type_extensions_test.html b/tests/html/custom/document_register_type_extensions_test.html
index 1a0fe01..92cdd27 100644
--- a/tests/html/custom/document_register_type_extensions_test.html
+++ b/tests/html/custom/document_register_type_extensions_test.html
@@ -17,8 +17,6 @@
   <h1> Running document_register_type_extensions_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/element_upgrade_test.dart b/tests/html/custom/element_upgrade_test.dart
new file mode 100644
index 0000000..244f21b
--- /dev/null
+++ b/tests/html/custom/element_upgrade_test.dart
@@ -0,0 +1,132 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library register_element_proxy_test;
+
+import 'dart:async';
+import 'dart:html';
+import 'dart:js' as js;
+import 'package:unittest/html_config.dart';
+import 'package:unittest/unittest.dart';
+import '../utils.dart';
+
+class FooElement extends HtmlElement {
+  static final tag = 'x-foo';
+
+  final int initializedField = 666;
+  js.JsObject _proxy;
+
+  factory FooElement() => new Element.tag(tag);
+  FooElement.created() : super.created() {
+    _proxy = new js.JsObject.fromBrowserObject(this);
+  }
+
+  String doSomething() => _proxy.callMethod('doSomething');
+
+  bool get fooCreated => _proxy['fooCreated'];
+}
+
+main() {
+  useHtmlConfiguration();
+
+  var registered = false;
+  var upgrader;
+  setUp(() => customElementsReady.then((_) {
+    if (!registered) {
+      registered = true;
+      upgrader = document.createElementUpgrader(FooElement);
+      js.context['upgradeListener'] = (e) {
+        upgrader.upgrade(e);
+      };
+
+      document.register('custom-element', CustomElement);
+    }
+  }));
+
+  test('created gets proxied', () {
+    var element = document.createElement(FooElement.tag);
+    expect(element is FooElement, isTrue);
+    expect(element.initializedField, 666);
+    expect(element.text, 'constructed');
+
+    js.context.callMethod('validateIsFoo', [element]);
+
+    expect(element.doSomething(), 'didSomething');
+    expect(element.fooCreated, true);
+  });
+
+  test('dart constructor works', () {
+    var element = new FooElement();
+    expect(element is FooElement, isTrue);
+    expect(element.text, 'constructed');
+
+    js.context.callMethod('validateIsFoo', [element]);
+
+    expect(element.doSomething(), 'didSomething');
+  });
+
+  test('cannot create upgrader for interfaces', () {
+    expect(() {
+      document.createElementUpgrader(HtmlElementInterface);
+    }, throws);
+  });
+
+  test('cannot upgrade interfaces', () {
+    expect(() {
+      upgrader.upgrade(new HtmlElementInterface());
+    }, throws);
+  });
+
+  test('cannot upgrade more than once', () {
+    var fooElement = new FooElement();
+    expect(() {
+      upgrader.upgrade(fooElement);
+    }, throws);
+  });
+
+  test('cannot upgrade non-matching elements', () {
+    expect(() {
+      upgrader.upgrade(new DivElement());
+    }, throws);
+  });
+
+  test('cannot upgrade custom elements', () {
+    var custom = new CustomElement();
+    expect(() {
+      upgrader.upgrade(custom);
+    }, throws);
+  });
+
+  test('can upgrade with extendsTag', () {
+    var upgrader =
+        document.createElementUpgrader(CustomDiv, extendsTag: 'div');
+    var div = new DivElement();
+    var customDiv = upgrader.upgrade(div);
+    expect(customDiv is CustomDiv, isTrue);
+
+    var htmlElement = document.createElement('not-registered');
+    expect(() {
+      upgrader.upgrade(htmlElement);
+    }, throws);
+  });
+
+  test('cannot create upgrader for built-in types', () {
+    expect(() {
+      document.createElementUpgrader(HtmlElement);
+    }, throws);
+  });
+}
+
+class HtmlElementInterface implements HtmlElement {
+  HtmlElementInterface.created();
+}
+
+class CustomDiv extends DivElement {
+  CustomDiv.created() : super.created();
+}
+
+class CustomElement extends HtmlElement {
+  factory CustomElement() => document.createElement('custom-element');
+  CustomElement.created() : super.created();
+}
diff --git a/tests/html/custom/element_upgrade_test.html b/tests/html/custom/element_upgrade_test.html
new file mode 100644
index 0000000..498a47b
--- /dev/null
+++ b/tests/html/custom/element_upgrade_test.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<meta name="dart.unittest" content="full-stack-traces">
+<title> element_upgrade_test </title>
+<style>
+   .unittest-table { font-family:monospace; border:1px; }
+   .unittest-pass { background: #6b3;}
+   .unittest-fail { background: #d55;}
+   .unittest-error { background: #a11;}
+</style>
+<script src="/packages/web_components/platform.concat.js"></script>
+<script src="/packages/web_components/dart_support.js"></script>
+
+<body>
+  <h1> Running element_upgrade_test </h1>
+
+<script>
+var Foo = function() {};
+Foo.prototype = Object.create(HTMLElement.prototype);
+Foo.prototype.createdCallback = function() {
+  this.fooCreated = true;
+  this.textContent = 'constructed';
+
+  // Tell the Dart side that this was created.
+  // For testing purposes, for real code this would use a different mechanism.
+  window.upgradeListener(this);
+};
+
+Foo.prototype.doSomething = function() {
+  this.textContent = 'didSomething';
+  return 'didSomething';
+};
+
+Foo = document.registerElement('x-foo', Foo);
+
+function validateIsFoo(element) {
+  if (!(element instanceof Foo)) {
+    throw Error('Element is not a Foo');
+  }
+
+  if (!element.fooCreated) {
+    throw Error('Expected fooCreated to be set');
+  }
+}
+</script>
+
+  <script type="text/javascript"
+      src="/root_dart/tools/testing/dart/test_controller.js"></script>
+  <script type="text/javascript"
+      src="/packages/browser/interop.js"></script>
+  %TEST_SCRIPTS%
+</body>
diff --git a/tests/html/custom/entered_left_view_test.html b/tests/html/custom/entered_left_view_test.html
index 531162e..3ba2ba8 100644
--- a/tests/html/custom/entered_left_view_test.html
+++ b/tests/html/custom/entered_left_view_test.html
@@ -17,8 +17,6 @@
   <h1> Running entered_left_view_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/js_custom_test.html b/tests/html/custom/js_custom_test.html
index c477d92..92a36d1 100644
--- a/tests/html/custom/js_custom_test.html
+++ b/tests/html/custom/js_custom_test.html
@@ -17,8 +17,6 @@
   <h1> Running js_custom_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/mirrors_test.html b/tests/html/custom/mirrors_test.html
index 0211aea..f28c4fa 100644
--- a/tests/html/custom/mirrors_test.html
+++ b/tests/html/custom/mirrors_test.html
@@ -17,8 +17,6 @@
   <h1> Running mirrors_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript"
-      src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/custom/template_wrappers_test.html b/tests/html/custom/template_wrappers_test.html
index 3af84bb..1b22431 100644
--- a/tests/html/custom/template_wrappers_test.html
+++ b/tests/html/custom/template_wrappers_test.html
@@ -12,7 +12,6 @@
   </style>
   <script src="/packages/web_components/platform.concat.js"></script>
   <script src="/packages/web_components/dart_support.js"></script>
-  <script src="/packages/browser/interop.js"></script>
 </head>
 <body>
   <h1> Running template_wrappers_test </h1>
diff --git a/tests/html/custom_elements_test.html b/tests/html/custom_elements_test.html
index 8c2cd1e..7a3eb2c 100644
--- a/tests/html/custom_elements_test.html
+++ b/tests/html/custom_elements_test.html
@@ -17,7 +17,6 @@
   <h1> Running custom_elements_test </h1>
   <script type="text/javascript"
       src="/root_dart/tools/testing/dart/test_controller.js"></script>
-  <script type="text/javascript" src="/packages/browser/interop.js"></script>
   %TEST_SCRIPTS%
 </body>
 </html>
diff --git a/tests/html/html.status b/tests/html/html.status
index 2f7368c..ab76af4 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -11,6 +11,7 @@
 
 [ $compiler == dart2js && $csp ]
 custom/js_custom_test: Fail # Issue 14643
+custom/element_upgrade_test: Fail # Issue 17298
 
 [ $compiler == dart2js && $browser ]
 custom/created_callback_test: Fail # Support for created constructor.
@@ -35,6 +36,7 @@
 async_test: Fail # Background timers not implemented.
 keyboard_event_test: Fail # Issue 13902
 isolates_test: Fail # Issue 13921
+custom/element_upgrade_test: Skip # Issue 17298
 
 [ $compiler == none && ($runtime == drt || $runtime == dartium || $runtime == ContentShellInAndroid) && $mode == debug ]
 websocket_test/websocket: Skip # Issue 17666
@@ -194,6 +196,7 @@
 
 [ $runtime == ie9 || $runtime == ie10 ]
 custom/document_register_type_extensions_test/construction: Fail # Issue 13193
+custom/element_upgrade_test: Fail # Issue 18247
 worker_api_test: Fail # IE does not support URL.createObjectURL in web workers.
 
 [ $runtime == ie9 ]
@@ -440,6 +443,7 @@
 [ $compiler == dartanalyzer || $compiler == dart2analyzer ]
 audiocontext_test: StaticWarning
 custom/document_register_basic_test: StaticWarning
+custom/element_upgrade_test: StaticWarning
 datalistelement_test: StaticWarning
 documentfragment_test: StaticWarning
 element_add_test: StaticWarning
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index eb5f276..f1f41fa 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -22,9 +22,15 @@
 ondone_test: Skip       # Not implemented yet, hangs.
 ping_test: Fail         # Not implemented yet
 ping_pause_test: Fail   # Not implemented yet
+kill_test: Skip         # Not implemented yet, hangs.
+kill2_test: Skip        # Not implemented yet, hangs.
+kill3_test: Skip        # Not implemented yet, hangs.
+kill_self_test: Skip    # Not implemented yet, hangs.
 
 [ $compiler == dart2js && $jscl ]
 browser/*: SkipByDesign  # Browser specific tests
+
+[ $compiler == dart2js && $runtime == jsshell ]
 pause_test: Fail  # non-zero timer not supported.
 
 [ $compiler == dart2js ]
diff --git a/tests/isolate/kill2_test.dart b/tests/isolate/kill2_test.dart
new file mode 100644
index 0000000..911cd69
--- /dev/null
+++ b/tests/isolate/kill2_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+  RawReceivePort port = new RawReceivePort();
+  port.handler = (v) {
+    replyPort.send(v);
+    if (v == 0) port.close();
+  };
+  replyPort.send(port.sendPort);
+}
+
+void main() {
+  asyncStart();
+  var completer = new Completer();  // Completed by first reply from isolate.
+  RawReceivePort reply = new RawReceivePort(completer.complete);
+  Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+    List result = [];
+    completer.future.then((echoPort) {
+      reply.handler = (v) {
+        result.add(v);
+        if (v == 2) {
+          isolate.kill(Isolate.BEFORE_NEXT_EVENT);
+        }
+        echoPort.send(v - 1);
+      };
+      RawReceivePort exitSignal;
+      exitSignal = new RawReceivePort((_) {
+        Expect.listEquals([4, 3, 2], result);
+        exitSignal.close();
+        reply.close();
+        asyncEnd();
+      });
+      isolate.addOnExitListener(exitSignal.sendPort);
+      echoPort.send(4);
+    });
+  });
+}
diff --git a/tests/isolate/kill3_test.dart b/tests/isolate/kill3_test.dart
new file mode 100644
index 0000000..006f7ae
--- /dev/null
+++ b/tests/isolate/kill3_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+  RawReceivePort port = new RawReceivePort();
+  port.handler = (v) {
+    replyPort.send(v);
+    if (v == 0) port.close();
+  };
+  replyPort.send(port.sendPort);
+}
+
+void main() {
+  asyncStart();
+  var completer = new Completer();  // Completed by first reply from isolate.
+  RawReceivePort reply = new RawReceivePort(completer.complete);
+  Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+    List result = [];
+    completer.future.then((echoPort) {
+      reply.handler = (v) {
+        result.add(v);
+        echoPort.send(v - 1);
+        if (v == 2) {
+          isolate.kill(Isolate.AS_EVENT);
+        }
+      };
+      RawReceivePort exitSignal;
+      exitSignal = new RawReceivePort((_) {
+        Expect.listEquals([4, 3, 2, 1], result);
+        exitSignal.close();
+        reply.close();
+        asyncEnd();
+      });
+      isolate.addOnExitListener(exitSignal.sendPort);
+      echoPort.send(4);
+    });
+  });
+}
diff --git a/tests/isolate/kill_self_test.dart b/tests/isolate/kill_self_test.dart
new file mode 100644
index 0000000..932dca6
--- /dev/null
+++ b/tests/isolate/kill_self_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+  RawReceivePort port = new RawReceivePort();
+  bool firstEvent = true;
+  port.handler = (v) {
+    if (!firstEvent) {
+      throw "Survived suicide";
+    }
+    var controlPort = v[0];
+    var killCapability = v[1];
+    firstEvent = false;
+    var isolate = new Isolate(controlPort,
+                              terminateCapability: killCapability);
+    isolate.kill(Isolate.IMMEDIATE);
+  };
+  replyPort.send(port.sendPort);
+}
+
+void main() {
+  asyncStart();
+  var completer = new Completer();  // Completed by first reply from isolate.
+  RawReceivePort reply = new RawReceivePort(completer.complete);
+  Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+    completer.future.then((isolatePort) {
+      RawReceivePort exitSignal;
+      exitSignal = new RawReceivePort((_) {
+        exitSignal.close();
+        asyncEnd();
+      });
+      isolate.addOnExitListener(exitSignal.sendPort);
+      isolatePort.send([isolate.controlPort, isolate.terminateCapability]);
+      reply.close();
+    });
+  });
+}
diff --git a/tests/isolate/kill_test.dart b/tests/isolate/kill_test.dart
new file mode 100644
index 0000000..d0da5ab
--- /dev/null
+++ b/tests/isolate/kill_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:isolate";
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+isomain1(replyPort) {
+  RawReceivePort port = new RawReceivePort();
+  port.handler = (v) {
+    replyPort.send(v);
+    if (v == 0) port.close();
+  };
+  replyPort.send(port.sendPort);
+}
+
+void main() {
+  asyncStart();
+  var completer = new Completer();  // Completed by first reply from isolate.
+  RawReceivePort reply = new RawReceivePort(completer.complete);
+  Isolate.spawn(isomain1, reply.sendPort).then((Isolate isolate) {
+    completer.future.then((SendPort echoPort) {
+      List result = [];
+      reply.handler = (v) {
+        result.add(v);
+        if (v == 2) {
+          isolate.kill(Isolate.IMMEDIATE);
+        }
+        echoPort.send(v - 1);
+      };
+      RawReceivePort exitSignal;
+      exitSignal = new RawReceivePort((_) {
+        Expect.listEquals([4, 3, 2], result);
+        exitSignal.close();
+        reply.close();
+        asyncEnd();
+      });
+      isolate.addOnExitListener(exitSignal.sendPort);
+      echoPort.send(4);
+    });
+  });
+}
diff --git a/tests/isolate/ping_pause_test.dart b/tests/isolate/ping_pause_test.dart
index 11fa951..ec47f7a 100644
--- a/tests/isolate/ping_pause_test.dart
+++ b/tests/isolate/ping_pause_test.dart
@@ -41,7 +41,7 @@
         isolate.resume(resume);
         pingPort.close();
       };
-      isolate.ping(pingPort.sendPort, Isolate.PING_CONTROL);
+      isolate.ping(pingPort.sendPort, Isolate.BEFORE_NEXT_EVENT);
       echoPort.send(2);
       echoPort.send(1);
     });
diff --git a/tests/isolate/ping_test.dart b/tests/isolate/ping_test.dart
index e208189..48b1ec5 100644
--- a/tests/isolate/ping_test.dart
+++ b/tests/isolate/ping_test.dart
@@ -27,19 +27,22 @@
         result.add(v);
         if (v == 0) {
           Expect.listEquals(["alive", "control", "event"],
-                            result.where((x) => x is String).toList());
+                            result.where((x) => x is String).toList(),
+                            "control events");
           Expect.listEquals([4, 3, 2, 1, 0],
-                            result.where((x) => x is int).toList());
-          Expect.isTrue(result.indexOf("alive") < result.indexOf(3));
-          Expect.isTrue(result.indexOf("control") < result.indexOf(2));
+                            result.where((x) => x is int).toList(),
+                            "data events");
+          Expect.isTrue(result.indexOf("alive") < result.indexOf(3),
+                        "alive index < 3");
+          Expect.isTrue(result.indexOf("control") < result.indexOf(2),
+                        "control index < 2");
           int eventIndex = result.indexOf("event");
-          Expect.isTrue(eventIndex > result.indexOf(2));
-          Expect.isTrue(eventIndex < result.indexOf(1));
+          Expect.isTrue(eventIndex > result.indexOf(2), "event index > 2");
+          Expect.isTrue(eventIndex < result.indexOf(1), "event index < 1");
           reply.close();
           asyncEnd();
         }
       };
-      echoPort.send(4);
       SendPort createPingPort(message) {
         var pingPort = new RawReceivePort();
         pingPort.handler = (_) {
@@ -48,11 +51,12 @@
         };
         return pingPort.sendPort;
       }
-      isolate.ping(createPingPort("alive"), Isolate.PING_ALIVE);
+      echoPort.send(4);
+      isolate.ping(createPingPort("alive"), Isolate.IMMEDIATE);
       echoPort.send(3);
-      isolate.ping(createPingPort("control"), Isolate.PING_CONTROL);
+      isolate.ping(createPingPort("control"), Isolate.BEFORE_NEXT_EVENT);
       echoPort.send(2);
-      isolate.ping(createPingPort("event"), Isolate.PING_EVENT);
+      isolate.ping(createPingPort("event"), Isolate.AS_EVENT);
       echoPort.send(1);
       echoPort.send(0);
     });
diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status
index 67d84da..1666a95 100644
--- a/tests/language/language_analyzer.status
+++ b/tests/language/language_analyzer.status
@@ -9,13 +9,6 @@
 # Test issue 12694 (was analyzer issue), (1) when "abstract" is import prefix using it as type is warning; (2) currently analyzer resolves prefix as field (don't ask)
 built_in_identifier_prefix_test: CompileTimeError # Issue 12694
 
-# Issue 15315
-class_literal_test/01: CompileTimeError
-class_literal_test/03: CompileTimeError
-class_literal_test/07: CompileTimeError
-constructor_call_as_function_test/01: fail
-call_type_literal_test/01: fail
-
 # TBF: we should check conflicts not only for methods, but for accessors too
 override_field_test/03: fail
 method_override7_test/03: Fail # Issue 11496
@@ -29,8 +22,6 @@
 
 call_closurization_test: StaticWarning # Issue 17476
 
-function_literals2_test: Fail # Issue 18137
-
 # Please add new failing tests before this line.
 # Section below is for invalid tests.
 #
@@ -122,7 +113,6 @@
 
 # test issue 12381, It is compile-time error to invoke not existing function
 issue11724_test: fail # Issue 12381
-call_nonexistent_static_test/08: fail # Issue 12381
 
 # test issue 12539, rules for finals were loosened, contradiction in spec was fixed
 const_syntax_test/09: fail # Issue 12539
@@ -161,23 +151,6 @@
 # test issue 14736, It is a static warning if a class C declares an instance method named n and has a setter named n=.
 setter4_test: StaticWarning
 
-# test issue 15060
-least_upper_bound_test/23: StaticWarning # Issue 15060
-least_upper_bound_test/24: StaticWarning # Issue 15060
-least_upper_bound_test/25: MissingStaticWarning # Issue 15060
-least_upper_bound_test/26: MissingStaticWarning # Issue 15060
-least_upper_bound_test/29: StaticWarning # Issue 15060
-least_upper_bound_test/30: StaticWarning # Issue 15060
-least_upper_bound_test/31: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/02: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/04: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/05: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/06: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/08: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/10: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/11: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/12: MissingStaticWarning # Issue 15060
-
 # test issue 15467
 proxy_test/05: StaticWarning # Issue 15467
 proxy_test/06: StaticWarning # Issue 15467
@@ -270,7 +243,7 @@
 getter_declaration_negative_test: CompileTimeError
 getter_no_setter2_test/01: StaticWarning
 getter_no_setter_test/01: StaticWarning
-illegal_invocation_test/01: CompileTimeError
+illegal_invocation_test/01: StaticWarning
 implicit_this_test/02: StaticWarning
 implied_interface_test: StaticWarning
 import_combinators_test: StaticWarning
@@ -302,9 +275,9 @@
 label8_negative_test: CompileTimeError
 label_test: StaticWarning
 library_ambiguous_test/00: StaticWarning
-library_ambiguous_test/01: CompileTimeError
-library_ambiguous_test/02: CompileTimeError
-library_ambiguous_test/03: CompileTimeError
+library_ambiguous_test/01: StaticWarning
+library_ambiguous_test/02: StaticWarning
+library_ambiguous_test/03: StaticWarning
 library_negative_test: CompileTimeError
 list_literal2_negative_test: CompileTimeError
 list_literal4_test: StaticWarning
@@ -410,7 +383,7 @@
 script2_negative_test: CompileTimeError
 setter_declaration2_negative_test: CompileTimeError
 setter_declaration_negative_test: CompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
+setter_no_getter_call_test/01: StaticWarning
 source_self_negative_test: CompileTimeError
 static_initializer_type_error_test: StaticWarning
 string_escape4_negative_test: CompileTimeError
@@ -459,7 +432,7 @@
 unary_plus_negative_test: CompileTimeError
 unbound_getter_test: StaticWarning
 unhandled_exception_negative_test: CompileTimeError
-unresolved_top_level_method_negative_test: CompileTimeError
+unresolved_top_level_method_negative_test: StaticWarning
 vm/type_cast_vm_test: StaticWarning
 vm/type_vm_test: StaticWarning
 void_type_test: StaticWarning
@@ -480,6 +453,6 @@
 deferred_constraints_constants_old_syntax_test/*: Pass, Fail
 deferred_shadow_load_library_test: Fail
 deferred_closurize_load_library_test: Fail
-deferred_load_library_wrong_args_test/01: Fail
-deferred_load_library_wrong_args_test/none: Fail
-deferred_load_inval_code_test: Fail
+deferred_load_library_wrong_args_test/01: StaticWarning
+deferred_load_library_wrong_args_test/none: StaticWarning
+deferred_load_inval_code_test: StaticWarning
diff --git a/tests/language/language_analyzer2.status b/tests/language/language_analyzer2.status
index 6af40b1..f84c5b1 100644
--- a/tests/language/language_analyzer2.status
+++ b/tests/language/language_analyzer2.status
@@ -9,13 +9,6 @@
 # Test issue 12694 (was analyzer issue), (1) when "abstract" is import prefix using it as type is warning; (2) currently analyzer resolves prefix as field (don't ask)
 built_in_identifier_prefix_test: CompileTimeError # Issue 12694
 
-# Issue 15315
-class_literal_test/01: CompileTimeError
-class_literal_test/03: CompileTimeError
-class_literal_test/07: CompileTimeError
-constructor_call_as_function_test/01: fail
-call_type_literal_test/01: fail
-
 # TBF: we should check conflicts not only for methods, but for accessors too
 override_field_test/03: fail
 method_override7_test/03: Fail # Issue 11496
@@ -120,7 +113,6 @@
 
 # test issue 12381, It is compile-time error to invoke not existing function
 issue11724_test: fail # Issue 12381
-call_nonexistent_static_test/08: fail # Issue 12381
 
 # test issue 12539, rules for finals were loosened, contradiction in spec was fixed
 const_syntax_test/09: fail # Issue 12539
@@ -159,23 +151,6 @@
 # test issue 14736, It is a static warning if a class C declares an instance method named n and has a setter named n=.
 setter4_test: StaticWarning
 
-# test issue 15060
-least_upper_bound_test/23: StaticWarning # Issue 15060
-least_upper_bound_test/24: StaticWarning # Issue 15060
-least_upper_bound_test/25: MissingStaticWarning # Issue 15060
-least_upper_bound_test/26: MissingStaticWarning # Issue 15060
-least_upper_bound_test/29: StaticWarning # Issue 15060
-least_upper_bound_test/30: StaticWarning # Issue 15060
-least_upper_bound_test/31: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/02: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/04: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/05: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/06: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/08: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/10: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/11: MissingStaticWarning # Issue 15060
-least_upper_bound_expansive_test/12: MissingStaticWarning # Issue 15060
-
 # test issue 15467
 proxy_test/05: StaticWarning # Issue 15467
 proxy_test/06: StaticWarning # Issue 15467
@@ -268,7 +243,7 @@
 getter_declaration_negative_test: CompileTimeError
 getter_no_setter2_test/01: StaticWarning
 getter_no_setter_test/01: StaticWarning
-illegal_invocation_test/01: CompileTimeError
+illegal_invocation_test/01: StaticWarning
 implicit_this_test/02: StaticWarning
 implied_interface_test: StaticWarning
 import_combinators_test: StaticWarning
@@ -300,9 +275,9 @@
 label8_negative_test: CompileTimeError
 label_test: StaticWarning
 library_ambiguous_test/00: StaticWarning
-library_ambiguous_test/01: CompileTimeError
-library_ambiguous_test/02: CompileTimeError
-library_ambiguous_test/03: CompileTimeError
+library_ambiguous_test/01: StaticWarning
+library_ambiguous_test/02: StaticWarning
+library_ambiguous_test/03: StaticWarning
 library_negative_test: CompileTimeError
 list_literal2_negative_test: CompileTimeError
 list_literal4_test: StaticWarning
@@ -408,7 +383,7 @@
 script2_negative_test: CompileTimeError
 setter_declaration2_negative_test: CompileTimeError
 setter_declaration_negative_test: CompileTimeError
-setter_no_getter_call_test/01: CompileTimeError
+setter_no_getter_call_test/01: StaticWarning
 source_self_negative_test: CompileTimeError
 static_initializer_type_error_test: StaticWarning
 string_escape4_negative_test: CompileTimeError
@@ -457,7 +432,7 @@
 unary_plus_negative_test: CompileTimeError
 unbound_getter_test: StaticWarning
 unhandled_exception_negative_test: CompileTimeError
-unresolved_top_level_method_negative_test: CompileTimeError
+unresolved_top_level_method_negative_test: StaticWarning
 vm/type_cast_vm_test: StaticWarning
 vm/type_vm_test: StaticWarning
 void_type_test: StaticWarning
@@ -478,6 +453,6 @@
 deferred_constraints_constants_old_syntax_test/*: Pass, Fail
 deferred_shadow_load_library_test: Fail
 deferred_closurize_load_library_test: Fail
-deferred_load_library_wrong_args_test/01: Fail
-deferred_load_library_wrong_args_test/none: Fail
-deferred_load_inval_code_test: Fail
+deferred_load_library_wrong_args_test/01: StaticWarning
+deferred_load_library_wrong_args_test/none: StaticWarning
+deferred_load_inval_code_test: StaticWarning
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index c97ea0b..444de3c 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -3,7 +3,6 @@
 # BSD-style license that can be found in the LICENSE file.
 
 [ $compiler == dart2js || $compiler == dart2dart ]
-function_literals2_test: Fail # Issue 18102
 override_inheritance_mixed_test/08: Fail # Issue 18124
 override_inheritance_mixed_test/09: Fail # Issue 18124
 bad_constructor_test/05: CompileTimeError # Issue 13669
diff --git a/tests/language/parse_closures_in_initializers_test.dart b/tests/language/parse_closures_in_initializers_test.dart
new file mode 100644
index 0000000..4da7e98
--- /dev/null
+++ b/tests/language/parse_closures_in_initializers_test.dart
@@ -0,0 +1,45 @@
+// 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 'package:expect/expect.dart';
+
+// Test that function literals are parsed correctly in initializers.
+
+class A {
+  final x;
+  static foo(f) => f();
+
+  A.parenthesized(y) : x = (() => y);
+  A.stringLiteral(y) : x = "**${() => y}--";
+  A.listLiteral(y) : x = [() => y];
+  A.mapLiteral(y) : x = { "fun": () => y };
+  A.arg(y) : x = foo(() => y);
+}
+
+main() {
+  var a, f;
+  a = new A.parenthesized(499);
+  f = a.x;
+  Expect.isTrue(f is Function);
+  Expect.equals(499, f());
+
+  // The toString of closures is not specified. Just make sure that there is no
+  // crash.
+  a = new A.stringLiteral(42);
+  Expect.isTrue(a.x.startsWith("**"));
+  Expect.isTrue(a.x.endsWith("--"));
+
+  a = new A.listLiteral(99);
+  f = a.x[0];
+  Expect.isTrue(f is Function);
+  Expect.equals(99, f());
+
+  a = new A.mapLiteral(314);
+  f = a.x["fun"];
+  Expect.isTrue(f is Function);
+  Expect.equals(314, f());
+
+  a = new A.arg(123);
+  Expect.equals(123, a.x);
+}
diff --git a/tests/lib/async/future_test.dart b/tests/lib/async/future_test.dart
index ad6830a..df33697 100644
--- a/tests/lib/async/future_test.dart
+++ b/tests/lib/async/future_test.dart
@@ -745,6 +745,8 @@
 }
 
 main() {
+  asyncStart();
+
   testValue();
   testSync();
   testNeverComplete();
@@ -797,6 +799,8 @@
   testChainedFutureError();
 
   testSyncFuture_i13368();
+
+  asyncEnd();
 }
 
 /// A Future that isn't recognizable as a _Future.
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 83bc9ea..f5d65e2 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -136,6 +136,9 @@
 math/math2_test: RuntimeError
 
 [ $compiler == dart2js && $jscl ]
+mirrors/invoke_natives_fuzz_test: RuntimeError # Issue 15566
+
+[ $compiler == dart2js && $runtime == jsshell ]
 async/future_test: RuntimeError # Timer interface not supported; dartbug.com/7728.
 async/slow_consumer2_test: RuntimeError # Timer interface not supported; dartbug.com/7728.
 async/slow_consumer3_test: RuntimeError # Timer interface not supported; dartbug.com/7728.
@@ -188,9 +191,9 @@
 mirrors/globalized_closures2_test/00: RuntimeError # Issue 17118. Please remove the multi-test comments when this test starts succeeding.
 
 
-[ $compiler == dart2js && $browser ]
+[ $compiler == dart2js && ( $browser || $runtime == d8 ) ]
 async/timer_not_available_test: Fail, OK # only meant to test when there is no way to
-                                         # implement timer (currently only in d8)
+                                         # implement timer (currently only in jsshell)
 
 # 'js' tests import the dart:js library, so they only make sense in
 # a browser environment.
@@ -244,6 +247,7 @@
 
 [ $runtime == vm ]
 async/timer_not_available_test: Fail, OK
+mirrors/invoke_natives_fuzz_test: RuntimeError # Issue 15274
 mirrors/native_class_test: Fail, OK # This test is meant to run in a browser.
 
 [ $compiler == none  ]
@@ -307,7 +311,7 @@
 # Issue 13921: spawnFunction is not allowed on Dartium's DOM thread.
 async/deferred/deferred_in_isolate_test: Fail
 
-[ $compiler == dart2js ]
+[ $compiler == dart2js && $runtime != d8 ]
 async/schedule_microtask_test: Fail # Issue 9002
 
 [ $compiler == dartanalyzer || $compiler == dart2analyzer ]
@@ -316,8 +320,6 @@
 mirrors/redirecting_factory_test/01: StaticWarning # test issue X, The return type 'Class<T2, T1>' of the redirected constructor is not assignable to 'Class<T1, T2>'
 mirrors/redirecting_factory_test/none: StaticWarning # test issue X, The return type 'Class<T2, T1>' of the redirected constructor is not assignable to 'Class<T1, T2>
 
-mirrors/fake_function_without_call_test: StaticWarning, OK # Implements Function without defining call.
-mirrors/find_in_context_fake_function_test: StaticWarning, OK # Implements Function without defining call.
 mirrors/immutable_collections_test: StaticWarning, OK # Expect failure for any type of Iterable.
 mirrors/inference_and_no_such_method_test: StaticWarning, OK # Expect to trigger noSuchMethod.
 mirrors/repeated_private_anon_mixin_app_test: StaticWarning, OK # Intentional library name conflict.
diff --git a/tests/lib/mirrors/invoke_natives_fuzz_test.dart b/tests/lib/mirrors/invoke_natives_fuzz_test.dart
new file mode 100644
index 0000000..f9967ee
--- /dev/null
+++ b/tests/lib/mirrors/invoke_natives_fuzz_test.dart
@@ -0,0 +1,135 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test reflectively enumerates all the methods in the system and tries to
+// invoke them will all nulls. This may result in Dart exceptions or hangs, but
+// should never result in crashes or JavaScript exceptions.
+
+library test.invoke_natives;
+
+import 'dart:mirrors';
+import 'dart:async';
+import 'package:expect/expect.dart';
+
+// Methods to be skipped, by qualified name.
+var blacklist = [
+  // These prevent the test from exiting, typically by spawning another isolate.
+  'dart.async._scheduleAsyncCallback',
+  'dart.io._IOService.dispatch',
+  'dart.isolate.RawReceivePort.RawReceivePort',
+  'dart.isolate.ReceivePort.ReceivePort',
+  'dart.isolate.ReceivePort.ReceivePort.fromRawReceivePort',
+  'dart.isolate.ReceivePort.sendPort',
+  'dart.isolate.ReceivePort.close',
+  'dart.isolate.ReceivePort.listen',
+  'dart.isolate.RawReceivePort.sendPort',
+  'dart.isolate.RawReceivePort.close',
+  'dart.isolate.RawReceivePort.handler=',
+
+  // These "crash" the VM (throw uncatchable API errors).
+  // TODO(15274): Fill in this list to make the test pass and provide coverage
+  // against addition of new natives.
+];
+
+class Task {
+  var name;
+  var action;
+}
+var queue = new List();
+
+checkMethod(MethodMirror m, ObjectMirror target, [origin]) {
+  if (blacklist.contains(MirrorSystem.getName(m.qualifiedName))) return;
+
+  var task = new Task();
+  task.name = '${MirrorSystem.getName(m.qualifiedName)} from $origin';
+
+  if (m.isRegularMethod) {
+    task.action =
+        () => target.invoke(m.simpleName, new List(m.parameters.length));
+  } else if (m.isGetter) {
+    task.action =
+        () => target.getField(m.simpleName);
+  } else if (m.isSetter) {
+    task.action =
+        () => target.setField(m.simpleName, null);
+  } else if (m.isConstructor) {
+    return;
+  } else {
+    throw "Unexpected method kind";
+  }
+
+  queue.add(task);
+}
+
+checkInstance(instanceMirror, origin) {
+  instanceMirror.type.declarations.values
+      .where((d) => d is MethodMirror)
+      .forEach((m) => checkMethod(m, instanceMirror, origin));
+}
+
+checkClass(classMirror) {
+  classMirror.declarations.values
+      .where((d) => d is MethodMirror)
+      .forEach((m) => checkMethod(m, classMirror));
+
+  classMirror.declarations.values
+      .where((d) => d is MethodMirror)
+      .forEach((m) {
+    if (blacklist.contains(MirrorSystem.getName(m.qualifiedName))) return;
+    if (!m.isConstructor) return;
+    var task = new Task();
+    task.name = MirrorSystem.getName(m.qualifiedName);
+
+    task.action = () {
+      var instance = classMirror.newInstance(m.constructorName,
+                                             new List(m.parameters.length));
+      checkInstance(instance, task.name);
+    };
+    queue.add(task);
+  });
+}
+
+checkLibrary(libraryMirror) {
+  // Don't recurse on this test.
+  if (libraryMirror.simpleName == #test.invoke_natives) return;
+
+  libraryMirror.declarations.values
+      .where((d) => d is ClassMirror)
+      .forEach(checkClass);
+
+  libraryMirror.declarations.values
+      .where((d) => d is MethodMirror)
+      .forEach((m) => checkMethod(m, libraryMirror));
+}
+
+var testZone;
+var debug = true;
+
+doOneTask() {
+  if (queue.length == 0) {
+    if (debug) print('Done');
+    return;
+  }
+
+  var task = queue.removeLast();
+  if (debug) print(task.name);
+  try {
+    task.action();
+  } catch(e) {}
+  // Register the next task in a timer callback so as to yield to async code
+  // scheduled in the current task. This isn't necessary for the test itself,
+  // but is helpful when trying to figure out which function is responsible for
+  // a crash.
+  testZone.createTimer(Duration.ZERO, doOneTask);
+}
+
+main() {
+  currentMirrorSystem().libraries.values.forEach(checkLibrary);
+
+  uncaughtErrorHandler(self, parent, zone, error, stack) {};
+  var zoneSpec =
+     new ZoneSpecification(handleUncaughtError: uncaughtErrorHandler);
+  testZone = Zone.current.fork(specification: zoneSpec);
+  testZone.createTimer(Duration.ZERO, doOneTask);
+}
diff --git a/tests/lib/mirrors/invoke_natives_malicious_test.dart b/tests/lib/mirrors/invoke_natives_malicious_test.dart
new file mode 100644
index 0000000..c353354
--- /dev/null
+++ b/tests/lib/mirrors/invoke_natives_malicious_test.dart
@@ -0,0 +1,28 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.invoke_natives;
+
+import 'dart:mirrors';
+import 'package:expect/expect.dart';
+
+test(name, action) {
+  print(name);
+  Expect.throws(action, (e)=>true, name);
+  print("done");
+}
+
+main() {
+  LibraryMirror dartcore = reflectClass(Object).owner;
+
+  test('List_copyFromObjectArray', () {
+    var receiver = new List(3);
+    var selector = MirrorSystem.getSymbol('_copyFromObjectArray', dartcore);
+    var src = new List(3);
+    var srcStart = 10;
+    var dstStart = 10;
+    var count = 10;
+    reflect(receiver).invoke(selector, [src, srcStart, dstStart, count]);
+  });
+}
diff --git a/tests/lib/profiler/user_tags_test.dart b/tests/lib/profiler/user_tags_test.dart
new file mode 100644
index 0000000..4070d90
--- /dev/null
+++ b/tests/lib/profiler/user_tags_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+//
+
+import 'dart:profiler';
+import 'package:expect/expect.dart';
+
+// Test that the label property matches the constructor.
+void testLabel() {
+  var label = 'Hello World';
+  var tag = new UserTag(label);
+  Expect.equals(label, tag.label);
+}
+
+
+// Test that we canonicalize UserTag by name.
+void testCanonicalize(tag1) {
+  var label = 'Global Tag';
+  var tag = new UserTag(label);
+  Expect.isTrue(identical(tag, tag1));
+}
+
+
+// Test that we made the tag current.
+void testMakeCurrent(tag) {
+  tag.makeCurrent();
+  Expect.isTrue(identical(tag, getCurrentTag()));
+}
+
+
+// Test clearCurrentTag.
+void testClearCurrent(tag) {
+  tag.makeCurrent();
+  Expect.isTrue(identical(tag, getCurrentTag()));
+  var old_tag = clearCurrentTag();
+  Expect.isTrue(identical(tag, old_tag));
+  Expect.isNull(getCurrentTag());
+}
+
+
+// Test that we reach a limit of tags an exception is thrown.
+void testExhaust() {
+  var i = 0;
+  while (true) {
+    var label = i.toString();
+    var tag = new UserTag(label);
+    i++;
+  }
+}
+
+
+main() {
+  var label = 'Global Tag';
+  var tag = new UserTag(label);
+  testLabel();
+  testCanonicalize(tag);
+  for (var i = 0; i < 2000; i++) {
+    testMakeCurrent(tag);
+  }
+  testClearCurrent(tag);
+  Expect.throws(testExhaust);
+}
diff --git a/tests/standalone/io/file_input_stream_test.dart b/tests/standalone/io/file_input_stream_test.dart
index 923a44c..86cb355 100644
--- a/tests/standalone/io/file_input_stream_test.dart
+++ b/tests/standalone/io/file_input_stream_test.dart
@@ -209,15 +209,18 @@
     var file = new File('${temp.path}/input_stream_bad_offset.txt');
     var originalLength = writeLongFileSync(file);
     var streamedBytes = 0;
+    bool error = false;
     file.openRead(start, end).listen(
         (d) {
           streamedBytes += d.length;
         },
         onDone: () {
-          temp.delete(recursive: true);
+          Expect.isTrue(error);
+          temp.deleteSync(recursive: true);
+          asyncEnd();
         },
         onError: (e) {
-          asyncEnd();
+          error = true;
         });
   }
   test(-1, null);
@@ -230,7 +233,7 @@
   String fileName = getFilename("tests/standalone/io/$name");
   // File contains 10 lines.
   File file = new File(fileName);
-  Expect.equals(length, file.openSync().lengthSync());
+  Expect.equals(length, file.lengthSync());
   var lineStream = file.openRead()
     .transform(UTF8.decoder)
     .transform(new LineSplitter());
diff --git a/tests/standalone/io/file_invalid_arguments_test.dart b/tests/standalone/io/file_invalid_arguments_test.dart
index d66c458..58c6d7c 100644
--- a/tests/standalone/io/file_invalid_arguments_test.dart
+++ b/tests/standalone/io/file_invalid_arguments_test.dart
@@ -16,6 +16,7 @@
 
   Expect.throws(() => file.read(arg),
                 (e) => e is ArgumentError);
+  file.closeSync();
 }
 
 void testReadIntoInvalidArgs(buffer, start, end) {
@@ -26,6 +27,7 @@
 
   Expect.throws(() => file.readInto(buffer, start, end),
                 (e) => e is ArgumentError);
+  file.closeSync();
 }
 
 void testWriteByteInvalidArgs(value) {
@@ -36,6 +38,7 @@
 
   Expect.throws(() => file.writeByte(value),
                 (e) => e is ArgumentError);
+  file.closeSync();
 }
 
 void testWriteFromInvalidArgs(buffer, start, end) {
@@ -46,6 +49,7 @@
 
   Expect.throws(() => file.writeFrom(buffer, start, end),
                 (e) => e is ArgumentError);
+  file.closeSync();
 }
 
 void testWriteStringInvalidArgs(string, encoding) {
@@ -56,6 +60,7 @@
 
   Expect.throws(() => file.writeString(string, encoding: encoding),
                 (e) => e is ArgumentError);
+  file.closeSync();
 }
 
 Future futureThrows(Future result) {
diff --git a/tests/standalone/io/file_test.dart b/tests/standalone/io/file_test.dart
index f8e93c9..0ce18ae 100644
--- a/tests/standalone/io/file_test.dart
+++ b/tests/standalone/io/file_test.dart
@@ -262,17 +262,24 @@
     Expect.equals(103, buffer[9]);  // represents 'g' in the file.
     Expect.equals(104, buffer[10]);  // represents 'h' in the file.
     Expect.equals(116, buffer[11]);  // represents 't' in the file.
+    raf.closeSync();
 
     filename = getFilename("tests/vm/data/fixed_length_file");
     File file = new File(filename);
     int len = file.lengthSync();
-    Expect.equals(0, file.openSync().readSync(0).length);
-    Expect.equals(1, file.openSync().readSync(1).length);
-    Expect.equals(len - 1, file.openSync().readSync(len - 1).length);
-    Expect.equals(len, file.openSync().readSync(len).length);
-    Expect.equals(len, file.openSync().readSync(len + 1).length);
-    Expect.equals(len, file.openSync().readSync(len * 2).length);
-    Expect.equals(len, file.openSync().readSync(len * 10).length);
+    int read(int length) {
+      var f = file.openSync();
+      int res = f.readSync(length).length;
+      f.closeSync();
+      return res;
+    }
+    Expect.equals(0, read(0));
+    Expect.equals(1, read(1));
+    Expect.equals(len - 1, read(len - 1));
+    Expect.equals(len, read(len));
+    Expect.equals(len, read(len + 1));
+    Expect.equals(len, read(len * 2));
+    Expect.equals(len, read(len * 10));
   }
 
   // Test for file read and write functionality.
diff --git a/tests/standalone/io/http_client_connect_test.dart b/tests/standalone/io/http_client_connect_test.dart
index b6b8e37..8e18325 100644
--- a/tests/standalone/io/http_client_connect_test.dart
+++ b/tests/standalone/io/http_client_connect_test.dart
@@ -148,19 +148,58 @@
   });
 }
 
-void testPostEmptyRequest() {
-  HttpServer.bind("127.0.0.1", 0).then((server) {
-    server.listen((request) {
-      request.pipe(request.response);
-    });
 
-    var client = new HttpClient();
-    client.post("127.0.0.1", server.port, "/")
-      .then((request) => request.close())
-      .then((response) {
-        response.listen((data) {}, onDone: server.close);
+void testOpenEmptyRequest() {
+  var client = new HttpClient();
+  var methods = [
+    [client.get, 'GET'],
+    [client.post, 'POST'],
+    [client.put, 'PUT'],
+    [client.delete, 'DELETE'],
+    [client.patch, 'PATCH'],
+    [client.head, 'HEAD']];
+
+  for (var method in methods) {
+    HttpServer.bind("127.0.0.1", 0).then((server) {
+      server.listen((request) {
+        Expect.equals(method[1], request.method);
+        request.pipe(request.response);
       });
-  });
+
+      method[0]("127.0.0.1", server.port, "/")
+          .then((request) => request.close())
+          .then((response) {
+            response.listen((data) {}, onDone: server.close);
+          });
+    });
+  }
+}
+
+
+void testOpenUrlEmptyRequest() {
+  var client = new HttpClient();
+  var methods = [
+    [client.getUrl, 'GET'],
+    [client.postUrl, 'POST'],
+    [client.putUrl, 'PUT'],
+    [client.deleteUrl, 'DELETE'],
+    [client.patchUrl, 'PATCH'],
+    [client.headUrl, 'HEAD']];
+
+  for (var method in methods) {
+    HttpServer.bind("127.0.0.1", 0).then((server) {
+      server.listen((request) {
+        Expect.equals(method[1], request.method);
+        request.pipe(request.response);
+      });
+
+      method[0](Uri.parse("http://127.0.0.1:${server.port}/"))
+          .then((request) => request.close())
+          .then((response) {
+            response.listen((data) {}, onDone: server.close);
+          });
+    });
+  }
 }
 
 
@@ -218,6 +257,7 @@
   testGetServerCloseNoKeepAlive();
   testGetServerForceClose();
   testGetDataServerForceClose();
-  testPostEmptyRequest();
+  testOpenEmptyRequest();
+  testOpenUrlEmptyRequest();
   testNoBuffer();
 }
diff --git a/tests/standalone/io/http_server_test.dart b/tests/standalone/io/http_server_test.dart
index 4eb8df5..f664501 100644
--- a/tests/standalone/io/http_server_test.dart
+++ b/tests/standalone/io/http_server_test.dart
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import "dart:async";
+import "dart:typed_data";
 import "dart:io";
 
 import "package:async_helper/async_helper.dart";
@@ -110,8 +111,34 @@
 }
 
 
+void testHttpServerClientClose() {
+  HttpServer.bind("127.0.0.1", 0).then((server) {
+    runZoned(() {
+      server.listen((request) {
+        request.response.bufferOutput = false;
+        request.response.add(new Uint8List(64 * 1024));
+        new Timer(const Duration(milliseconds: 100), () {
+          request.response.close().then((_) {
+            server.close();
+          });
+        });
+      });
+    }, onError: (e, s) {
+      Expect.fail("Unexpected error: $e(${e.hashCode})\n$s");
+    });
+    var client = new HttpClient();
+    client.get("127.0.0.1", server.port, "/")
+        .then((request) => request.close())
+        .then((response) {
+          response.listen((_) {}).cancel();
+        });
+  });
+}
+
+
 void main() {
   testListenOn();
   testHttpServerZone();
   testHttpServerZoneError();
+  testHttpServerClientClose();
 }
diff --git a/tests/standalone/io/read_into_const_list_test.dart b/tests/standalone/io/read_into_const_list_test.dart
index 5bc1c6b..f010a83 100644
--- a/tests/standalone/io/read_into_const_list_test.dart
+++ b/tests/standalone/io/read_into_const_list_test.dart
@@ -27,5 +27,6 @@
       }
       Expect.equals(0, a[0]);
       Expect.equals(0, b[0]);
+      input.closeSync();
     });
 }
diff --git a/tests/standalone/issue14236_test.dart b/tests/standalone/issue14236_test.dart
index 467e13a..74a8f29 100644
--- a/tests/standalone/issue14236_test.dart
+++ b/tests/standalone/issue14236_test.dart
Binary files differ
diff --git a/tools/VERSION b/tools/VERSION
index ed1e417..71f97d8 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 1
 MINOR 4
 PATCH 0
-PRERELEASE 2
-PRERELEASE_PATCH 2
+PRERELEASE 3
+PRERELEASE_PATCH 0
diff --git a/tools/create_debian_packages.py b/tools/create_debian_packages.py
index d739dfd..0eba452 100755
--- a/tools/create_debian_packages.py
+++ b/tools/create_debian_packages.py
@@ -65,10 +65,6 @@
     print "Building source package"
     RunBuildPackage(['-S', '-us', '-uc'], join(temp_dir, tarroot));
 
-    # Build 32-bit binary package.
-    print "Building i386 package"
-    RunBuildPackage(['-B', '-ai386', '-us', '-uc'], join(temp_dir, tarroot));
-
     # Build 64-bit binary package.
     print "Building amd64 package"
     RunBuildPackage(['-B', '-aamd64', '-us', '-uc'], join(temp_dir, tarroot));
@@ -80,17 +76,12 @@
       '%s.orig.tar.gz' % debbase,
       '%s-1.debian.tar.gz' % debbase
     ]
-    i386_package = [
-      '%s-1_i386.deb' % debbase
-    ]
     amd64_package = [
       '%s-1_amd64.deb' % debbase
     ]
 
     for name in source_package:
       copyfile(join(temp_dir, name), join(out_dir, name))
-    for name in i386_package:
-      copyfile(join(temp_dir, name), join(out_dir, name))
     for name in amd64_package:
       copyfile(join(temp_dir, name), join(out_dir, name))
 
diff --git a/tools/create_editor.py b/tools/create_editor.py
index ba4f4fa5..d53d19c 100644
--- a/tools/create_editor.py
+++ b/tools/create_editor.py
@@ -80,7 +80,7 @@
   lines = f.readlines()
   f.close()
   lines[lines.index('-Xms40m\n')] = '-Xms256m\n'
-  lines[lines.index('-Xmx1000m\n')] = '-Xmx2000m\n'
+  lines[lines.index('-Xmx1850m\n')] = '-Xmx2000m\n'
   # Add -d64 to give better error messages to user in 64 bit mode.
   lines[lines.index('-vmargs\n')] = '-vmargs\n-d64\n'
   f = open(iniFilePath, 'w')
diff --git a/tools/create_sdk.py b/tools/create_sdk.py
index 62b9194..c8f79ed 100755
--- a/tools/create_sdk.py
+++ b/tools/create_sdk.py
@@ -203,7 +203,7 @@
                   join('html', 'dart2js'), join('html', 'dartium'),
                   join('html', 'html_common'),
                   join('indexed_db', 'dart2js'), join('indexed_db', 'dartium'),
-                  'js', 'math', 'mirrors', 'typed_data',
+                  'js', 'math', 'mirrors', 'typed_data', 'profiler',
                   join('svg', 'dart2js'), join('svg', 'dartium'),
                   join('web_audio', 'dart2js'), join('web_audio', 'dartium'),
                   join('web_gl', 'dart2js'), join('web_gl', 'dartium'),
diff --git a/tools/create_timestamp_file.py b/tools/create_timestamp_file.py
new file mode 100644
index 0000000..7a1d4d3
--- /dev/null
+++ b/tools/create_timestamp_file.py
@@ -0,0 +1,16 @@
+# 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.
+
+import sys
+import os
+
+def main(args):
+  for file_name in args[1:]:
+    dir_name = os.path.dirname(file_name)
+    if not os.path.exists(dir_name):
+      os.mkdir(dir_name)
+    open(file_name, 'w').close()
+
+if __name__ == '__main__':
+  sys.exit(main(sys.argv))
diff --git a/tools/dom/scripts/generator.py b/tools/dom/scripts/generator.py
index c6c9b57..62f093f 100644
--- a/tools/dom/scripts/generator.py
+++ b/tools/dom/scripts/generator.py
@@ -163,8 +163,6 @@
 
     'WheelEvent': 'WheelEvent,MouseWheelEvent,MouseScrollEvent',
 
-    'XMLHttpRequestUpload': 'XMLHttpRequestUpload,XMLHttpRequestEventTarget',
-
 }, dart2jsOnly=True)
 
 def IsRegisteredType(type_name):
diff --git a/tools/dom/scripts/htmlrenamer.py b/tools/dom/scripts/htmlrenamer.py
index 1b39357..f2cac7e 100644
--- a/tools/dom/scripts/htmlrenamer.py
+++ b/tools/dom/scripts/htmlrenamer.py
@@ -288,7 +288,6 @@
   'MutationEvent.initMutationEvent',
   'MutationObserver.observe',
   'Node.attributes',
-  'Node.baseURI',
   'Node.localName',
   'Node.namespaceURI',
   'Node.removeChild',
diff --git a/tools/dom/src/dart2js_CustomElementSupport.dart b/tools/dom/src/dart2js_CustomElementSupport.dart
index aa1c037..009f857 100644
--- a/tools/dom/src/dart2js_CustomElementSupport.dart
+++ b/tools/dom/src/dart2js_CustomElementSupport.dart
@@ -87,7 +87,7 @@
   if (extendsTagName == null) {
     if (baseClassName != 'HTMLElement') {
       throw new UnsupportedError('Class must provide extendsTag if base '
-          'native class is not HTMLElement');
+          'native class is not HtmlElement');
     }
   } else {
     if (!JS('bool', '(#.createElement(#) instanceof window[#])',
@@ -128,3 +128,60 @@
 void _initializeCustomElement(Element e) {
   // TODO(blois): Add validation that this is only in response to an upgrade.
 }
+
+/// Dart2JS implementation of ElementUpgrader
+class _JSElementUpgrader implements ElementUpgrader {
+  var _interceptor;
+  var _constructor;
+  var _nativeType;
+
+  _JSElementUpgrader(Document document, Type type, String extendsTag) {
+    var interceptorClass = findInterceptorConstructorForType(type);
+    if (interceptorClass == null) {
+      throw new ArgumentError(type);
+    }
+
+    _constructor = findConstructorForNativeSubclassType(type, 'created');
+    if (_constructor == null) {
+      throw new ArgumentError("$type has no constructor called 'created'");
+    }
+
+    // Workaround for 13190- use an article element to ensure that HTMLElement's
+    // interceptor is resolved correctly.
+    getNativeInterceptor(new Element.tag('article'));
+
+    var baseClassName = findDispatchTagForInterceptorClass(interceptorClass);
+    if (baseClassName == null) {
+      throw new ArgumentError(type);
+    }
+
+    if (extendsTag == null) {
+      if (baseClassName != 'HTMLElement') {
+        throw new UnsupportedError('Class must provide extendsTag if base '
+            'native class is not HtmlElement');
+      }
+      _nativeType = HtmlElement;
+    } else {
+      var element = document.createElement(extendsTag);
+      if (!JS('bool', '(# instanceof window[#])',
+          element, baseClassName)) {
+        throw new UnsupportedError(
+            'extendsTag does not match base native class');
+      }
+      _nativeType = element.runtimeType;
+    }
+
+    _interceptor = JS('=Object', '#.prototype', interceptorClass);
+  }
+
+  Element upgrade(Element element) {
+    // Only exact type matches are supported- cannot be a subclass.
+    if (element.runtimeType != _nativeType) {
+      throw new ArgumentError('element is not subclass of $_nativeType');
+    }
+
+    setNativeSubclassDispatchRecord(element, _interceptor);
+    JS('', '#(#)', _constructor, element);
+    return element;
+  }
+}
diff --git a/tools/dom/src/dart2js_LocationWrapper.dart b/tools/dom/src/dart2js_LocationWrapper.dart
deleted file mode 100644
index c943226..0000000
--- a/tools/dom/src/dart2js_LocationWrapper.dart
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-part of html;
-
-// On Firefox 11, the object obtained from 'window.location' is very strange.
-// It can't be monkey-patched and seems immune to putting methods on
-// Object.prototype.  We are forced to wrap the object.
-
-class _LocationWrapper implements Location {
-
-  final _ptr;  // Opaque reference to real location.
-
-  _LocationWrapper(this._ptr);
-
-  // TODO(sra): Replace all the _set and _get calls with 'JS' forms.
-
-  // final List<String> ancestorOrigins;
-  List<String> get ancestorOrigins => _get(_ptr, 'ancestorOrigins');
-
-  // String hash;
-  String get hash => _get(_ptr, 'hash');
-  void set hash(String value) {
-    _set(_ptr, 'hash', value);
-  }
-
-  // String host;
-  String get host => _get(_ptr, 'host');
-  void set host(String value) {
-    _set(_ptr, 'host', value);
-  }
-
-  // String hostname;
-  String get hostname => _get(_ptr, 'hostname');
-  void set hostname(String value) {
-    _set(_ptr, 'hostname', value);
-  }
-
-  // String href;
-  String get href => _get(_ptr, 'href');
-  void set href(String value) {
-    _set(_ptr, 'href', value);
-  }
-
-  // final String origin;
-  String get origin {
-    if (JS('bool', '("origin" in #)', _ptr)) {
-      return JS('String', '#.origin', _ptr);
-    }
-    return '${this.protocol}//${this.host}';
-  }
-
-  // String pathname;
-  String get pathname => _get(_ptr, 'pathname');
-  void set pathname(String value) {
-    _set(_ptr, 'pathname', value);
-  }
-
-  // String port;
-  String get port => _get(_ptr, 'port');
-  void set port(String value) {
-    _set(_ptr, 'port', value);
-  }
-
-  // String protocol;
-  String get protocol => _get(_ptr, 'protocol');
-  void set protocol(String value) {
-    _set(_ptr, 'protocol', value);
-  }
-
-  // String search;
-  String get search => _get(_ptr, 'search');
-  void set search(String value) {
-    _set(_ptr, 'search', value);
-  }
-
-  void assign(String url) => JS('void', '#.assign(#)', _ptr, url);
-
-  void reload() => JS('void', '#.reload()', _ptr);
-
-  void replace(String url) => JS('void', '#.replace(#)', _ptr, url);
-
-  String toString() => JS('String', '#.toString()', _ptr);
-
-
-  static _get(p, m) => JS('var', '#[#]', p, m);
-  static _set(p, m, v) => JS('void', '#[#] = #', p, m, v);
-}
diff --git a/tools/dom/src/dartium_CustomElementSupport.dart b/tools/dom/src/dartium_CustomElementSupport.dart
new file mode 100644
index 0000000..df29593
--- /dev/null
+++ b/tools/dom/src/dartium_CustomElementSupport.dart
@@ -0,0 +1,89 @@
+// 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.dom.html;
+
+/// Dartium ElementUpgrader implementation.
+class _VMElementUpgrader implements ElementUpgrader {
+  final Type _type;
+  final Type _nativeType;
+
+  _VMElementUpgrader(Document document, Type type, String extendsTag) :
+      _type = type,
+      _nativeType = _validateCustomType(type).reflectedType {
+
+    if (extendsTag == null) {
+      if (_nativeType != HtmlElement) {
+        throw new UnsupportedError('Class must provide extendsTag if base '
+              'native class is not HtmlElement');
+      }
+    } else {
+      if (document.createElement(extendsTag).runtimeType != _nativeType) {
+        throw new UnsupportedError(
+            'extendsTag does not match base native class');
+      }
+    }
+  }
+
+  Element upgrade(Element element) {
+    if (element.runtimeType != _nativeType) {
+      throw new UnsupportedError('Element is incorrect type');
+    }
+    return _Utils.changeElementWrapper(element, _type);
+    return null;
+  }
+}
+
+/// Validates that the custom type is properly formed-
+///
+/// * Is a user-defined class.
+/// * Has a created constructor with zero args.
+/// * Derives from an Element subclass.
+///
+/// Then returns the native base class.
+ClassMirror _validateCustomType(Type type) {
+  ClassMirror cls = reflectClass(type);
+  if (_isBuiltinType(cls)) {
+    throw new UnsupportedError('Invalid custom element from '
+        '${(cls.owner as LibraryMirror).uri}.');
+  }
+
+  var className = MirrorSystem.getName(cls.simpleName);
+  var createdConstructor = cls.declarations[new Symbol('$className.created')];
+  if (createdConstructor == null ||
+      createdConstructor is! MethodMirror ||
+      !createdConstructor.isConstructor) {
+    throw new UnsupportedError(
+        'Class is missing constructor $className.created');
+  }
+
+  if (createdConstructor.parameters.length > 0) {
+    throw new UnsupportedError(
+        'Constructor $className.created must take zero arguments');
+  }
+
+  Symbol objectName = reflectClass(Object).qualifiedName;
+  bool isRoot(ClassMirror cls) =>
+      cls == null || cls.qualifiedName == objectName;
+  Symbol elementName = reflectClass(HtmlElement).qualifiedName;
+  bool isElement(ClassMirror cls) =>
+      cls != null && cls.qualifiedName == elementName;
+  ClassMirror superClass = cls.superclass;
+  ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
+  while(!isRoot(superClass) && !isElement(superClass)) {
+    superClass = superClass.superclass;
+    if (nativeClass == null && _isBuiltinType(superClass)) {
+      nativeClass = superClass;
+    }
+  }
+  return nativeClass;
+}
+
+
+bool _isBuiltinType(ClassMirror cls) {
+  // TODO(vsm): Find a less hackish way to do this.
+  LibraryMirror lib = cls.owner;
+  String libName = lib.uri.toString();
+  return libName.startsWith('dart:');
+}
diff --git a/tools/dom/src/native_DOMImplementation.dart b/tools/dom/src/native_DOMImplementation.dart
index 8387a0f..d8c4edc 100644
--- a/tools/dom/src/native_DOMImplementation.dart
+++ b/tools/dom/src/native_DOMImplementation.dart
@@ -396,48 +396,10 @@
 
   static bool isNoSuchMethodError(obj) => obj is NoSuchMethodError;
 
-  static bool _isBuiltinType(ClassMirror cls) {
-    // TODO(vsm): Find a less hackish way to do this.
-    LibraryMirror lib = cls.owner;
-    String libName = lib.uri.toString();
-    return libName.startsWith('dart:');
-  }
-
   static void register(Document document, String tag, Type type,
       String extendsTagName) {
-    // TODO(vsm): Move these checks into native code.
-    ClassMirror cls = reflectClass(type);
-    if (_isBuiltinType(cls)) {
-      throw new UnsupportedError("Invalid custom element from ${(cls.owner as LibraryMirror).uri}.");
-    }
-    var className = MirrorSystem.getName(cls.simpleName);
-    var createdConstructor = cls.declarations[new Symbol('$className.created')];
-    if (createdConstructor == null ||
-        createdConstructor is! MethodMirror ||
-        !createdConstructor.isConstructor) {
-      throw new UnsupportedError(
-          'Class is missing constructor $className.created');
-    }
+    var nativeClass = _validateCustomType(type);
 
-    if (createdConstructor.parameters.length > 0) {
-      throw new UnsupportedError(
-          'Constructor $className.created must take zero arguments');
-    }
-
-    Symbol objectName = reflectClass(Object).qualifiedName;
-    bool isRoot(ClassMirror cls) =>
-        cls == null || cls.qualifiedName == objectName;
-    Symbol elementName = reflectClass(HtmlElement).qualifiedName;
-    bool isElement(ClassMirror cls) =>
-        cls != null && cls.qualifiedName == elementName;
-    ClassMirror superClass = cls.superclass;
-    ClassMirror nativeClass = _isBuiltinType(superClass) ? superClass : null;
-    while(!isRoot(superClass) && !isElement(superClass)) {
-      superClass = superClass.superclass;
-      if (nativeClass == null && _isBuiltinType(superClass)) {
-        nativeClass = superClass;
-      }
-    }
     if (extendsTagName == null) {
       if (nativeClass.reflectedType != HtmlElement) {
         throw new UnsupportedError('Class must provide extendsTag if base '
@@ -454,6 +416,8 @@
   static Element createElement(Document document, String tagName) native "Utils_createElement";
 
   static void initializeCustomElement(HtmlElement element) native "Utils_initializeCustomElement";
+
+  static void changeElementWrapper(HtmlElement element, Type type) native "Utils_changeElementWrapper";
 }
 
 class _DOMWindowCrossFrame extends NativeFieldWrapperClass2 implements
diff --git a/tools/dom/src/shared_html.dart b/tools/dom/src/shared_html.dart
index 7c4f696..dd67c48 100644
--- a/tools/dom/src/shared_html.dart
+++ b/tools/dom/src/shared_html.dart
@@ -68,3 +68,12 @@
  * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
  */
 ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+
+/// A utility for changing the Dart wrapper type for elements.
+abstract class ElementUpgrader {
+  /// Upgrade the specified element to be of the Dart type this was created for.
+  ///
+  /// After upgrading the element passed in is invalid and the returned value
+  /// should be used instead.
+  Element upgrade(Element element);
+}
diff --git a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
index 91d162f..1695b66 100644
--- a/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
+++ b/tools/dom/templates/html/dart2js/html_dart2js.darttemplate
@@ -89,7 +89,6 @@
 part '$AUXILIARY_DIR/dart2js_CustomElementSupport.dart';
 part '$AUXILIARY_DIR/dart2js_DOMImplementation.dart';
 part '$AUXILIARY_DIR/dart2js_KeyEvent.dart';
-part '$AUXILIARY_DIR/dart2js_LocationWrapper.dart';
 part '$AUXILIARY_DIR/dart2js_Platform.dart';
 part '$AUXILIARY_DIR/shared_html.dart';
 part '$AUXILIARY_DIR/Validators.dart';
diff --git a/tools/dom/templates/html/dartium/html_dartium.darttemplate b/tools/dom/templates/html/dartium/html_dartium.darttemplate
index 8c22b56..3e10354 100644
--- a/tools/dom/templates/html/dartium/html_dartium.darttemplate
+++ b/tools/dom/templates/html/dartium/html_dartium.darttemplate
@@ -78,6 +78,7 @@
 part '$AUXILIARY_DIR/WrappedList.dart';
 part '$AUXILIARY_DIR/_HttpRequestUtils.dart';
 part '$AUXILIARY_DIR/_ListIterators.dart';
+part '$AUXILIARY_DIR/dartium_CustomElementSupport.dart';
 part '$AUXILIARY_DIR/dartium_KeyEvent.dart';
 part '$AUXILIARY_DIR/dartium_Platform.dart';
 part '$AUXILIARY_DIR/shared_html.dart';
@@ -130,4 +131,4 @@
 Future<Isolate> spawnDomUri(Uri uri, List<String> args, message) {
   // TODO(17738): Plumb arguments and return value through.
   return _Utils.spawnDomUri(uri.toString());
-}
\ No newline at end of file
+}
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 7fd5abc..4155c8d 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -1223,7 +1223,7 @@
 
       // Workaround for Chrome bug 229142- URIs are not resolved in new doc.
       var base = _parseDocument.createElement('base');
-      base.href = document._baseUri;
+      base.href = document.baseUri;
       _parseDocument.head.append(base);
     }
     var contextElement;
diff --git a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
index 973c9d4..70662bd 100644
--- a/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
+++ b/tools/dom/templates/html/impl/impl_HTMLDocument.darttemplate
@@ -244,32 +244,6 @@
    * This custom element can also be instantiated via HTML using the syntax
    * `<input is="x-bar"></input>`
    *
-   * The [nativeTagName] parameter is needed by platforms without native support
-   * when subclassing a native type other than:
-   *
-   * * HtmlElement
-   * * SvgElement
-   * * AnchorElement
-   * * AudioElement
-   * * ButtonElement
-   * * CanvasElement
-   * * DivElement
-   * * ImageElement
-   * * InputElement
-   * * LIElement
-   * * LabelElement
-   * * MenuElement
-   * * MeterElement
-   * * OListElement
-   * * OptionElement
-   * * OutputElement
-   * * ParagraphElement
-   * * PreElement
-   * * ProgressElement
-   * * SelectElement
-   * * SpanElement
-   * * UListElement
-   * * VideoElement
    */
 $if DART2JS
   void register(String tag, Type customElementClass, {String extendsTag}) {
@@ -321,4 +295,21 @@
   @Experimental()
   Stream<Event> get onVisibilityChange =>
       visibilityChangeEvent.forTarget(this);
+
+  /// Creates an element upgrader which can be used to change the Dart wrapper
+  /// type for elements.
+  ///
+  /// The type specified must be a subclass of HtmlElement, when an element is
+  /// upgraded then the created constructor will be invoked on that element.
+  ///
+  /// If the type is not a direct subclass of HtmlElement then the extendsTag
+  /// parameter must be provided.
+  @Experimental()
+  ElementUpgrader createElementUpgrader(Type type, {String extendsTag}) {
+$if DART2JS
+    return new _JSElementUpgrader(this, type, extendsTag);
+$else
+    return new _VMElementUpgrader(this, type, extendsTag);
+$endif
+  }
 }
diff --git a/tools/dom/templates/html/impl/impl_Window.darttemplate b/tools/dom/templates/html/impl/impl_Window.darttemplate
index cdf44bf..94baa46 100644
--- a/tools/dom/templates/html/impl/impl_Window.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Window.darttemplate
@@ -70,59 +70,29 @@
   }
 
   // API level getter and setter for Location.
-  // TODO: The cross domain safe wrapper can be inserted here or folded into
-  // _LocationWrapper.
+  // TODO: The cross domain safe wrapper can be inserted here.
   /**
    * The current location of this window.
    *
    *     Location currentLocation = window.location;
    *     print(currentLocation.href); // 'http://www.example.com:80/'
    */
-  Location get location {
-    // Firefox work-around for Location.  The Firefox location object cannot be
-    // made to behave like a Dart object so must be wrapped.
-    var result = _location;
-    if (_isDartLocation(result)) return result;  // e.g. on Chrome.
-    if (null == _location_wrapper) {
-      _location_wrapper = new _LocationWrapper(result);
-    }
-    return _location_wrapper;
-  }
+  Location get location => _location;
 
   // TODO: consider forcing users to do: window.location.assign('string').
   /**
    * Sets the window's location, which causes the browser to navigate to the new
-   * location. [value] may be a Location object or a string.
+   * location. [value] may be a Location object or a String.
    */
   void set location(value) {
-    if (value is _LocationWrapper) {
-      _location = value._ptr;
-    } else {
-      _location = value;
-    }
+    _location = value;
   }
 
-  _LocationWrapper _location_wrapper;  // Cached wrapped Location object.
-
   // Native getter and setter to access raw Location object.
-  dynamic get _location => JS('Location|=Object', '#.location', this);
+  dynamic get _location => JS('Location|Null', '#.location', this);
   void set _location(value) {
     JS('void', '#.location = #', this, value);
   }
-  // Prevent compiled from thinking 'location' property is available for a Dart
-  // member.
-  @JSName('location')
-  _protect_location() native;
-
-  static _isDartLocation(thing) {
-    // On Firefox the code that implements 'is Location' fails to find the patch
-    // stub on Object.prototype and throws an exception.
-    try {
-      return thing is Location;
-    } catch (e) {
-      return false;
-    }
-  }
 
   /**
    * Called to draw an animation frame and then request the window to repaint
diff --git a/tools/gyp/configurations_make.gypi b/tools/gyp/configurations_make.gypi
index ec010c9..dbc7cb5 100644
--- a/tools/gyp/configurations_make.gypi
+++ b/tools/gyp/configurations_make.gypi
@@ -30,7 +30,7 @@
 
       'Dart_Linux_ia32_Base': {
         'abstract': 1,
-        'cflags': [ '-m32', '-msse2' ],
+        'cflags': [ '-m32', '-msse2', '-mfpmath=sse' ],
         'ldflags': [ '-m32', ],
       },
 
diff --git a/tools/testing/dart/browser_controller.dart b/tools/testing/dart/browser_controller.dart
index 92166e4..41a74ab 100644
--- a/tools/testing/dart/browser_controller.dart
+++ b/tools/testing/dart/browser_controller.dart
@@ -83,7 +83,7 @@
     const ['safari', 'ff', 'firefox', 'chrome', 'ie9', 'ie10',
            'ie11', 'dartium'];
 
-  static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = const [];
+  static const List<String> BROWSERS_WITH_WINDOW_SUPPORT = const ['ie11'];
 
   // TODO(kustermann): add standard support for chrome on android
   static bool supportedBrowser(String name) {
@@ -1376,11 +1376,10 @@
         if (use_iframe) {
           embedded_iframe.src = url;
         } else {
-          if (testing_window == undefined) {
-            testing_window = window.open(url);
-          } else {
-            testing_window.location = url;
+          if (typeof testing_window != 'undefined') {
+            testing_window.close();
           }
+          testing_window = window.open(url);
         }
       }
 
diff --git a/tools/testing/dart/browser_test.dart b/tools/testing/dart/browser_test.dart
index 42aa821..5e6be19 100644
--- a/tools/testing/dart/browser_test.dart
+++ b/tools/testing/dart/browser_test.dart
@@ -32,8 +32,6 @@
   </script>
   <script type="text/javascript"
           src="/root_dart/pkg/browser/lib/dart.js"></script>
-  <script type="text/javascript"
-          src="/root_dart/pkg/browser/lib/interop.js"></script>
 </body>
 </html>""";
 }
diff --git a/tools/testing/dart/multitest.dart b/tools/testing/dart/multitest.dart
index cc4445f..51468b2 100644
--- a/tools/testing/dart/multitest.dart
+++ b/tools/testing/dart/multitest.dart
@@ -272,7 +272,6 @@
              isNegativeIfChecked: isNegativeIfChecked,
              hasCompileErrorIfChecked: hasCompileErrorIfChecked,
              hasStaticWarning: hasStaticWarning,
-             multitestOutcome: outcome,
              multitestKey: key,
              originTestPath: filePath);
     }
diff --git a/tools/testing/dart/test_runner.dart b/tools/testing/dart/test_runner.dart
index a3d59b9..6dbc8af 100644
--- a/tools/testing/dart/test_runner.dart
+++ b/tools/testing/dart/test_runner.dart
@@ -576,10 +576,16 @@
 class CommandBuilder {
   static final CommandBuilder instance = new CommandBuilder._();
 
+  bool _cleared = false;
   final _cachedCommands = new Map<Command, Command>();
 
   CommandBuilder._();
 
+  void clearCommandCache() {
+    _cachedCommands.clear();
+    _cleared = true;
+  }
+
   ContentShellCommand getContentShellCommand(String executable,
                                              String htmlFile,
                                              List<String> options,
@@ -674,6 +680,10 @@
     // We check if this command has already been built.
     //  If so, we return the cached one. Otherwise we
     // store the one given as [command] argument.
+    if (_cleared) {
+      throw new Exception(
+          "CommandBuilder.get[type]Command called after cache cleared");
+    }
     var cachedCommand = _cachedCommands[command];
     if (cachedCommand != null) {
       return cachedCommand;
@@ -824,7 +834,7 @@
     return testOutput.contains("unittest-suite-wait-for-done");
   }
 
-  bool _isAsyncTestSuccessfull(String testOutput) {
+  bool _isAsyncTestSuccessful(String testOutput) {
     return testOutput.contains("unittest-suite-success");
   }
 
@@ -835,7 +845,7 @@
     // TODO: maybe we should introduce a AsyncIncomplete marker or so
     if (outcome == Expectation.PASS) {
       if (_isAsyncTest(testOutput) &&
-          !_isAsyncTestSuccessfull(testOutput)) {
+          !_isAsyncTestSuccessful(testOutput)) {
         return Expectation.FAIL;
       }
     }
@@ -1464,7 +1474,7 @@
       if (line.length == 0) continue;
       List<String> fields = splitMachineError(line);
       // We only consider errors/warnings for files of interest.
-      if (fields.length > FILENAME) {
+      if (fields.length > FORMATTED_ERROR) {
         if (fields[ERROR_LEVEL] == 'ERROR') {
           outErrors.add(fields[FORMATTED_ERROR]);
         } else if (fields[ERROR_LEVEL] == 'WARNING') {
@@ -2278,7 +2288,7 @@
  * [CommandQueue] will listen for nodes entering the NodeState.ENQUEUING state,
  * queue them up and run them. While nodes are processed they will be in the
  * NodeState.PROCESSING state. After running a command, the node will change
- * to a state of NodeState.Successfull or NodeState.Failed.
+ * to a state of NodeState.Successful or NodeState.Failed.
  *
  * It provides a synchronous stream [completedCommands] which provides the
  * [CommandOutputs] for the finished commands.
@@ -2322,7 +2332,7 @@
           }
     });
     // We're finished if the graph is sealed and all nodes are in a finished
-    // state (Successfull, Failed or UnableToRun).
+    // state (Successful, Failed or UnableToRun).
     // So we're calling '_checkDone()' to check whether that condition is met
     // and we can cleanup.
     graph.events.listen((dgraph.GraphEvent event) {
@@ -2651,7 +2661,7 @@
 
 /*
  * [TestCaseCompleter] will listen for
- * NodeState.Processing -> NodeState.{Successfull,Failed} state changes and
+ * NodeState.Processing -> NodeState.{Successful,Failed} state changes and
  * will complete a TestCase if it is finished.
  *
  * It provides a stream [finishedTestCases], which will stream all TestCases
@@ -2684,7 +2694,7 @@
       _checkDone();
     });
 
-    // Listen for NodeState.Processing -> NodeState.{Successfull,Failed}
+    // Listen for NodeState.Processing -> NodeState.{Successful,Failed}
     // changes.
     eventCondition((event) => event is dgraph.StateChangedEvent)
         .listen((dgraph.StateChangedEvent event) {
@@ -2755,8 +2765,6 @@
 class ProcessQueue {
   Map _globalConfiguration;
 
-  bool _allTestsWereEnqueued = false;
-
   bool _listTests;
   Function _allDone;
   final dgraph.Graph _graph = new dgraph.Graph();
@@ -2916,6 +2924,10 @@
     testCaseEnqueuer.enqueueTestSuites(testSuites);
   }
 
+  void freeEnqueueingStructures() {
+    CommandBuilder.instance.clearCommandCache();
+  }
+
   void eventFinishedTestCase(TestCase testCase) {
     for (var listener in _eventListener) {
       listener.done(testCase);
@@ -2929,6 +2941,7 @@
   }
 
   void eventAllTestsKnown() {
+    freeEnqueueingStructures();
     for (var listener in _eventListener) {
       listener.allTestsKnown();
     }
diff --git a/tools/testing/dart/test_suite.dart b/tools/testing/dart/test_suite.dart
index 4a12b7a..b3d3ed7 100644
--- a/tools/testing/dart/test_suite.dart
+++ b/tools/testing/dart/test_suite.dart
@@ -50,7 +50,6 @@
                         {bool isNegativeIfChecked,
                          bool hasCompileErrorIfChecked,
                          bool hasStaticWarning,
-                         Set<String> multitestOutcome,
                          String multitestKey,
                          Path originTestPath});
 
@@ -121,6 +120,9 @@
 abstract class TestSuite {
   final Map configuration;
   final String suiteName;
+  // This function is set by subclasses before enqueueing starts.
+  Function doTest;
+
 
   TestSuite(this.configuration, this.suiteName);
 
@@ -245,10 +247,6 @@
    */
   void forEachTest(TestCaseEvent onTest, Map testCache, [VoidFunction onDone]);
 
-
-  // This function is set by subclasses before enqueueing starts.
-  Function doTest;
-
   // This function will be called for every TestCase of this test suite.
   // It will
   //  - handle sharding
@@ -575,14 +573,12 @@
   bool isNegativeIfChecked;
   bool hasCompileErrorIfChecked;
   bool hasStaticWarning;
-  Set<String> multitestOutcome;
   String multitestKey;
 
   TestInformation(this.filePath, this.optionsFromFile,
                   this.hasCompileError, this.hasRuntimeError,
                   this.isNegativeIfChecked, this.hasCompileErrorIfChecked,
                   this.hasStaticWarning,
-                  this.multitestOutcome,
                   {this.multitestKey, this.originTestPath}) {
     assert(filePath.isAbsolute);
     if (originTestPath == null) originTestPath = filePath;
@@ -705,6 +701,9 @@
         });
       }
     }).then((_) {
+      testExpectations = null;
+      cachedTests = null;
+      doTest = null;
       if (onDone != null) onDone();
     });
   }
@@ -1025,7 +1024,6 @@
             {bool isNegativeIfChecked: false,
              bool hasCompileErrorIfChecked: false,
              bool hasStaticWarning: false,
-             Set<String> multitestOutcome: null,
              String multitestKey,
              Path originTestPath}) {
       // Cache the test information for each test case.
@@ -1036,7 +1034,6 @@
                                      isNegativeIfChecked,
                                      hasCompileErrorIfChecked,
                                      hasStaticWarning,
-                                     multitestOutcome,
                                      multitestKey: multitestKey,
                                      originTestPath: originTestPath);
       cachedTests.add(info);
diff --git a/utils/apidoc/docgen.gyp b/utils/apidoc/docgen.gyp
index 49af512..a6a9142 100644
--- a/utils/apidoc/docgen.gyp
+++ b/utils/apidoc/docgen.gyp
@@ -78,14 +78,13 @@
             '--package-root=<(PRODUCT_DIR)/packages',
             '../../pkg/docgen/bin/docgen.dart',
             '--out=<(PRODUCT_DIR)/api_docs/docgen',
-            '--json',
             '--include-sdk',
             '--no-include-dependent-packages',
             '--package-root=<(PRODUCT_DIR)/packages',
             '--exclude-lib=async_helper',
             '--exclude-lib=expect',
             '--exclude-lib=docgen',
-            '../../pkg',          
+            '../../pkg'
           ],
           'message': 'Running docgen: <(_action)',
         },
diff --git a/utils/compiler/compiler.gyp b/utils/compiler/compiler.gyp
index fc4b1cf..9be9644 100644
--- a/utils/compiler/compiler.gyp
+++ b/utils/compiler/compiler.gyp
@@ -13,6 +13,7 @@
       'dependencies': [
         '../../runtime/dart-runtime.gyp:dart',
         '../../pkg/pkg.gyp:pkg_packages',
+        'dart2js_files_stamp',
       ],
       'actions': [
         {
@@ -20,8 +21,9 @@
           'inputs': [
             '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)dart<(EXECUTABLE_SUFFIX)',
             '../../sdk/lib/_internal/libraries.dart',
-            '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../sdk/lib/_internal/compiler", "../../runtime/lib", "../../sdk/lib/_internal/dartdoc"])',
+            '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../runtime/lib", "../../sdk/lib/_internal/dartdoc"])',
             'create_snapshot.dart',
+            '<(SHARED_INTERMEDIATE_DIR)/dart2js_files.stamp',
             '<(SHARED_INTERMEDIATE_DIR)/packages.stamp',
             '../../tools/VERSION',
           ],
@@ -40,5 +42,30 @@
         },
       ],
     },
+    # Other targets depend on dart2js files, but have to many inputs,
+    # which causes issues on some platforms.
+    # This target lists all the files in sdk/lib/_internal/compiler,
+    # and creates a single dart2js_files.stamp
+    {
+      'target_name': 'dart2js_files_stamp',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'make_dart2js_files_stamp',
+          'inputs': [
+            '../../tools/create_timestamp_file.py',
+            '<!@(["python", "../../tools/list_files.py", "\\.dart$",'
+                ' "../../sdk/lib/_internal/compiler"])',
+          ],
+          'outputs': [
+            '<(SHARED_INTERMEDIATE_DIR)/dart2js_files.stamp',
+          ],
+          'action': [
+            'python', '../../tools/create_timestamp_file.py',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    }
   ],
 }
diff --git a/utils/pub/pub.gyp b/utils/pub/pub.gyp
index fd5e147..9bc9ad2 100644
--- a/utils/pub/pub.gyp
+++ b/utils/pub/pub.gyp
@@ -10,16 +10,19 @@
       'dependencies': [
         '../../runtime/dart-runtime.gyp:dart',
         '../../pkg/pkg.gyp:pkg_packages',
+        '../../pkg/pkg_files.gyp:pkg_files_stamp',
+        '../../utils/compiler/compiler.gyp:dart2js_files_stamp',
+        'pub_files_stamp'
       ],
       'actions': [
         {
           'action_name': 'generate_pub_snapshot',
           'inputs': [
             '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)dart<(EXECUTABLE_SUFFIX)',
-            '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../sdk/lib/_internal/pub"])',
             '../../sdk/lib/_internal/libraries.dart',
-            '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../sdk/lib/_internal/compiler"])',
-            '<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../pkg"])',
+            '<(SHARED_INTERMEDIATE_DIR)/pub_files.stamp',
+            '<(SHARED_INTERMEDIATE_DIR)/dart2js_files.stamp',
+            '<(SHARED_INTERMEDIATE_DIR)/pkg_files.stamp',
           ],
           'outputs': [
             '<(SHARED_INTERMEDIATE_DIR)/pub.dart.snapshot',
@@ -33,5 +36,32 @@
         },
       ],
     },
+    # Other targets depend on pub files, but have to many inputs, which causes
+    # issues on some platforms.
+    # This target lists all the files in sdk/lib/_internal/pub,
+    # and creates a single pub_files.stamp
+    {
+      'target_name': 'pub_files_stamp',
+      'type': 'none',
+      'actions': [
+        {
+          'action_name': 'make_pub_files_stamp',
+          'inputs': [
+            '../../tools/create_timestamp_file.py',
+            '<!@(["python", "../../tools/list_files.py", "\\.dart$",'
+                ' "../../sdk/lib/_internal/pub"])',
+          ],
+          'outputs': [
+            '<(SHARED_INTERMEDIATE_DIR)/pub_files.stamp',
+          ],
+          'action': [
+            'python', '../../tools/create_timestamp_file.py',
+            '<@(_outputs)',
+          ],
+        },
+      ],
+    }
+
+
   ],
 }