Version 1.9.0-dev.8.0

svn merge -r 43548:43712 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@43715 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 82af650..fe7de66 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -43,7 +43,7 @@
 </style></head>
   <body>
     <h1>Analysis Server API Specification</h1>
-    <h1 style="color:#999999">Version 1.0.0</h1>
+    <h1 style="color:#999999">Version 1.2.0</h1>
     <p>
       This document contains a specification of the API provided by the
       analysis server.  The API in this document is currently under
@@ -572,17 +572,33 @@
           </dd></dl></dd><dt class="request">analysis.reanalyze</dt><dd><div class="box"><pre>request: {
   "id": String
   "method": "analysis.reanalyze"
+  "params": {
+    "<b>roots</b>": <span style="color:#999999">optional</span> List&lt;<a href="#type_FilePath">FilePath</a>&gt;
+  }
 }</pre><br><pre>response: {
   "id": String
   "error": <span style="color:#999999">optional</span> <a href="#type_RequestError">RequestError</a>
 }</pre></div>
         <p>
-          Force the re-analysis of everything contained in the
-          existing analysis roots. This will cause all previously
-          computed analysis results to be discarded and recomputed,
-          and will cause all subscribed notifications to be re-sent.
+          Force the re-analysis of everything contained in the specified
+          analysis roots. This will cause all previously computed analysis
+          results to be discarded and recomputed, and will cause all subscribed
+          notifications to be re-sent.
         </p>
-      </dd><dt class="request">analysis.setAnalysisRoots</dt><dd><div class="box"><pre>request: {
+        <p>
+          If no analysis roots are provided, then all current analysis roots
+          will be re-analyzed. If an empty list of analysis roots is provided,
+          then nothing will be re-analyzed. If the list contains one or more
+          paths that are not currently analysis roots, then an error of type
+          <tt>INVALID_ANALYSIS_ROOT</tt> will be generated.
+        </p>
+        
+      <h4>Parameters</h4><dl><dt class="field"><b><i>roots ( <span style="color:#999999">optional</span> List&lt;<a href="#type_FilePath">FilePath</a>&gt; )</i></b></dt><dd>
+            
+            <p>
+              A list of the analysis roots that are to be re-analyzed.
+            </p>
+          </dd></dl></dd><dt class="request">analysis.setAnalysisRoots</dt><dd><div class="box"><pre>request: {
   "id": String
   "method": "analysis.setAnalysisRoots"
   "params": {
@@ -1501,7 +1517,9 @@
           text is passed in so that the selection can be preserved across the
           formatting operation. The updated selection will be as close to
           matching the original as possible, but whitespace at the beginning or
-          end of the selected region will be ignored.
+          end of the selected region will be ignored. If preserving selection
+          information is not required, zero (0) can be specified for both the
+          selection offset and selection length.
         </p>
         <p>
           If a request is made for a file which does not exist, or
@@ -1520,16 +1538,12 @@
           </dd><dt class="field"><b><i>selectionOffset ( int )</i></b></dt><dd>
             
             <p>
-              The offset of the current selection in the file. In case
-              preserving, selection information is not required, 0 can be
-              specified for both selection offset and length.
+              The offset of the current selection in the file.
             </p>
           </dd><dt class="field"><b><i>selectionLength ( int )</i></b></dt><dd>
             
             <p>
-              The length of the current selection in the file. In case
-              preserving, selection information is not required, 0 can be
-              specified for both selection offset and length.
+              The length of the current selection in the file.
             </p>
           </dd></dl><h4>Returns</h4><dl><dt class="field"><b><i>edits ( List&lt;<a href="#type_SourceEdit">SourceEdit</a>&gt; )</i></b></dt><dd>
             
@@ -1874,7 +1888,8 @@
       <h4>Parameters</h4><dl><dt class="field"><b><i>contextRoot ( <a href="#type_FilePath">FilePath</a> )</i></b></dt><dd>
             
             <p>
-              The path of the Dart or HTML file that will be launched.
+              The path of the Dart or HTML file that will be launched, or the
+              path of the directory containing the file.
             </p>
           </dd></dl><h4>Returns</h4><dl><dt class="field"><b><i>id ( <a href="#type_ExecutionContextId">ExecutionContextId</a> )</i></b></dt><dd>
             
@@ -1925,25 +1940,27 @@
           context.
         </p>
         <p>
-          Exactly one of the file and uri fields must be provided.
+          Exactly one of the file and uri fields must be provided. If both
+          fields are provided, then an error of type <tt>INVALID_PARAMETER</tt>
+          will be generated. Similarly, if neither field is provided, then an
+          error of type <tt>INVALID_PARAMETER</tt> will be generated.
         </p>
         <p>
           If the file field is provided and the value is not the path of a file
           (either the file does not exist or the path references something other
-          than a file), then an error of type <tt>MAP_URI_INVALID_FILE</tt> will
+          than a file), then an error of type <tt>INVALID_PARAMETER</tt> will
           be generated.
         </p>
         <p>
           If the uri field is provided and the value is not a valid URI or if
           the URI references something that is not a file (either a file that
           does not exist or something other than a file), then an error of type
-          <tt>MAP_URI_INVALID_URI</tt> will be generated.
+          <tt>INVALID_PARAMETER</tt> will be generated.
         </p>
         <p>
-          If the contextRoot used to create the execution context is not a file
-          (either the file does not exist or the path references something other
-          than a file), then an error of type <tt>INVALID_EXECUTION_CONTEXT</tt>
-          will be generated.
+          If the contextRoot used to create the execution context does not
+          exist, then an error of type <tt>INVALID_EXECUTION_CONTEXT</tt> will
+          be generated.
         </p>
         
         
@@ -3076,6 +3093,12 @@
               which does not match a file currently subject to
               analysis.
             </p>
+          </dd><dt class="value">INVALID_EXECUTION_CONTEXT</dt><dd>
+            
+            <p>
+              The context root used to create an execution context does not
+              exist.
+            </p>
           </dd><dt class="value">INVALID_OVERLAY_CHANGE</dt><dd>
             
             <p>
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index 9bac7d9..f5430ac 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -67,7 +67,7 @@
    * The version of the analysis server. The value should be replaced
    * automatically during the build.
    */
-  static final String VERSION = '1.0.0';
+  static final String VERSION = '1.1.0';
 
   /**
    * The number of milliseconds to perform operations before inserting
@@ -334,6 +334,21 @@
   }
 
   /**
+   * Performs all scheduled analysis operations.
+   */
+  void test_performAllAnalysisOperations() {
+    while (true) {
+      ServerOperation operation = operationQueue.takeIf((operation) {
+        return operation is PerformAnalysisOperation;
+      });
+      if (operation == null) {
+        break;
+      }
+      operation.perform(this);
+    }
+  }
+
+  /**
    * If the given notice applies to a file contained within an analysis root,
    * notify interested parties that the file has been (at least partially)
    * analyzed.
@@ -353,11 +368,23 @@
    */
   AnalysisContext getAnalysisContext(String path) {
     // try to find a containing context
+    Folder containingFolder = null;
     for (Folder folder in folderMap.keys) {
-      if (folder.contains(path)) {
-        return folderMap[folder];
+      if (folder.path == path || folder.contains(path)) {
+        if (containingFolder == null) {
+          containingFolder = folder;
+        } else if (containingFolder.path.length < folder.path.length) {
+          containingFolder = folder;
+        }
       }
     }
+    if (containingFolder != null) {
+      return folderMap[containingFolder];
+    }
+    Resource resource = resourceProvider.getResource(path);
+    if (resource is Folder) {
+      return null;
+    }
     // check if there is a context that analyzed this source
     return getAnalysisContextForSource(getSource(path));
   }
@@ -943,6 +970,8 @@
   void updateContent(String id, Map<String, dynamic> changes) {
     changes.forEach((file, change) {
       Source source = getSource(file);
+      operationQueue.sourceAboutToChange(source);
+      // Prepare the new contents.
       String oldContents = _overlayState.getContents(source);
       String newContents;
       if (change is AddContentOverlay) {
@@ -1204,12 +1233,11 @@
    * [packageUriResolver].
    */
   SourceFactory _createSourceFactory(UriResolver packageUriResolver) {
-    List<UriResolver> resolvers = <UriResolver>[
-        new DartUriResolver(analysisServer.defaultSdk),
-        new ResourceUriResolver(resourceProvider)];
-    if (packageUriResolver != null) {
-      resolvers.add(packageUriResolver);
-    }
+    UriResolver dartResolver = new DartUriResolver(analysisServer.defaultSdk);
+    UriResolver resourceResolver = new ResourceUriResolver(resourceProvider);
+    List<UriResolver> resolvers = packageUriResolver != null ?
+        <UriResolver>[dartResolver, packageUriResolver, resourceResolver] :
+        <UriResolver>[dartResolver, resourceResolver];
     return new SourceFactory(resolvers);
   }
 }
diff --git a/pkg/analysis_server/lib/src/computer/computer_hover.dart b/pkg/analysis_server/lib/src/computer/computer_hover.dart
index 213f16e..87bc87a 100644
--- a/pkg/analysis_server/lib/src/computer/computer_hover.dart
+++ b/pkg/analysis_server/lib/src/computer/computer_hover.dart
@@ -68,6 +68,9 @@
    */
   HoverInformation compute() {
     AstNode node = new NodeLocator.con1(_offset).searchWithin(_unit);
+    if (node == null) {
+      return null;
+    }
     if (node.parent is TypeName &&
         node.parent.parent is ConstructorName &&
         node.parent.parent.parent is InstanceCreationExpression) {
diff --git a/pkg/analysis_server/lib/src/domain_execution.dart b/pkg/analysis_server/lib/src/domain_execution.dart
index 586b659..e1654df 100644
--- a/pkg/analysis_server/lib/src/domain_execution.dart
+++ b/pkg/analysis_server/lib/src/domain_execution.dart
@@ -10,6 +10,7 @@
 import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/constants.dart';
 import 'package:analysis_server/src/protocol.dart';
+import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/source.dart';
 
@@ -98,19 +99,36 @@
           'There is no execution context with an id of $contextId');
     }
     AnalysisContext context = server.getAnalysisContext(path);
-    if (params.file != null) {
-      if (params.uri != null) {
+    if (context == null) {
+      return new Response.invalidExecutionContext(request, contextId);
+    }
+    String file = params.file;
+    String uri = params.uri;
+    if (file != null) {
+      if (uri != null) {
         return new Response.invalidParameter(
             request,
             'file',
             'Either file or uri must be provided, but not both');
       }
-      Source source = server.getSource(params.file);
-      String uri = context.sourceFactory.restoreUri(source).toString();
+      Resource resource = server.resourceProvider.getResource(file);
+      if (!resource.exists) {
+        return new Response.invalidParameter(request, 'file', 'Must exist');
+      } else if (resource is! File) {
+        return new Response.invalidParameter(
+            request,
+            'file',
+            'Must not refer to a directory');
+      }
+      Source source = server.getSource(file);
+      uri = context.sourceFactory.restoreUri(source).toString();
       return new ExecutionMapUriResult(uri: uri).toResponse(request.id);
-    } else if (params.uri != null) {
-      Source source = context.sourceFactory.forUri(params.uri);
-      String file = source.fullName;
+    } else if (uri != null) {
+      Source source = context.sourceFactory.forUri(uri);
+      if (source == null) {
+        return new Response.invalidParameter(request, 'uri', 'Invalid URI');
+      }
+      file = source.fullName;
       return new ExecutionMapUriResult(file: file).toResponse(request.id);
     }
     return new Response.invalidParameter(
diff --git a/pkg/analysis_server/lib/src/generated_protocol.dart b/pkg/analysis_server/lib/src/generated_protocol.dart
index a7b579f..f4292bb 100644
--- a/pkg/analysis_server/lib/src/generated_protocol.dart
+++ b/pkg/analysis_server/lib/src/generated_protocol.dart
@@ -1008,25 +1008,70 @@
     return _JenkinsSmiHash.finish(hash);
   }
 }
+
 /**
  * analysis.reanalyze params
+ *
+ * {
+ *   "roots": optional List<FilePath>
+ * }
  */
-class AnalysisReanalyzeParams {
-  Request toRequest(String id) {
-    return new Request(id, "analysis.reanalyze", null);
+class AnalysisReanalyzeParams implements HasToJson {
+  /**
+   * A list of the analysis roots that are to be re-analyzed.
+   */
+  List<String> roots;
+
+  AnalysisReanalyzeParams({this.roots});
+
+  factory AnalysisReanalyzeParams.fromJson(JsonDecoder jsonDecoder, String jsonPath, Object json) {
+    if (json == null) {
+      json = {};
+    }
+    if (json is Map) {
+      List<String> roots;
+      if (json.containsKey("roots")) {
+        roots = jsonDecoder._decodeList(jsonPath + ".roots", json["roots"], jsonDecoder._decodeString);
+      }
+      return new AnalysisReanalyzeParams(roots: roots);
+    } else {
+      throw jsonDecoder.mismatch(jsonPath, "analysis.reanalyze params");
+    }
   }
 
+  factory AnalysisReanalyzeParams.fromRequest(Request request) {
+    return new AnalysisReanalyzeParams.fromJson(
+        new RequestDecoder(request), "params", request._params);
+  }
+
+  Map<String, dynamic> toJson() {
+    Map<String, dynamic> result = {};
+    if (roots != null) {
+      result["roots"] = roots;
+    }
+    return result;
+  }
+
+  Request toRequest(String id) {
+    return new Request(id, "analysis.reanalyze", toJson());
+  }
+
+  @override
+  String toString() => JSON.encode(toJson());
+
   @override
   bool operator==(other) {
     if (other is AnalysisReanalyzeParams) {
-      return true;
+      return _listEqual(roots, other.roots, (String a, String b) => a == b);
     }
     return false;
   }
 
   @override
   int get hashCode {
-    return 613039876;
+    int hash = 0;
+    hash = _JenkinsSmiHash.combine(hash, roots.hashCode);
+    return _JenkinsSmiHash.finish(hash);
   }
 }
 /**
@@ -3483,16 +3528,12 @@
   String file;
 
   /**
-   * The offset of the current selection in the file. In case preserving,
-   * selection information is not required, 0 can be specified for both
-   * selection offset and length.
+   * The offset of the current selection in the file.
    */
   int selectionOffset;
 
   /**
-   * The length of the current selection in the file. In case preserving,
-   * selection information is not required, 0 can be specified for both
-   * selection offset and length.
+   * The length of the current selection in the file.
    */
   int selectionLength;
 
@@ -4583,7 +4624,8 @@
  */
 class ExecutionCreateContextParams implements HasToJson {
   /**
-   * The path of the Dart or HTML file that will be launched.
+   * The path of the Dart or HTML file that will be launched, or the path of
+   * the directory containing the file.
    */
   String contextRoot;
 
@@ -9208,6 +9250,7 @@
  *   CONTENT_MODIFIED
  *   FORMAT_INVALID_FILE
  *   GET_ERRORS_INVALID_FILE
+ *   INVALID_EXECUTION_CONTEXT
  *   INVALID_OVERLAY_CHANGE
  *   INVALID_PARAMETER
  *   INVALID_REQUEST
@@ -9242,6 +9285,11 @@
   static const GET_ERRORS_INVALID_FILE = const RequestErrorCode._("GET_ERRORS_INVALID_FILE");
 
   /**
+   * The context root used to create an execution context does not exist.
+   */
+  static const INVALID_EXECUTION_CONTEXT = const RequestErrorCode._("INVALID_EXECUTION_CONTEXT");
+
+  /**
    * An analysis.updateContent request contained a ChangeContentOverlay object
    * which can't be applied, due to an edit having an offset or length that is
    * out of range.
@@ -9318,7 +9366,7 @@
   /**
    * A list containing all of the enum values that are defined.
    */
-  static const List<RequestErrorCode> VALUES = const <RequestErrorCode>[CONTENT_MODIFIED, FORMAT_INVALID_FILE, GET_ERRORS_INVALID_FILE, INVALID_OVERLAY_CHANGE, INVALID_PARAMETER, INVALID_REQUEST, REFACTORING_REQUEST_CANCELLED, SERVER_ALREADY_STARTED, SERVER_ERROR, SORT_MEMBERS_INVALID_FILE, SORT_MEMBERS_PARSE_ERRORS, UNANALYZED_PRIORITY_FILES, UNKNOWN_REQUEST, UNSUPPORTED_FEATURE];
+  static const List<RequestErrorCode> VALUES = const <RequestErrorCode>[CONTENT_MODIFIED, FORMAT_INVALID_FILE, GET_ERRORS_INVALID_FILE, INVALID_EXECUTION_CONTEXT, INVALID_OVERLAY_CHANGE, INVALID_PARAMETER, INVALID_REQUEST, REFACTORING_REQUEST_CANCELLED, SERVER_ALREADY_STARTED, SERVER_ERROR, SORT_MEMBERS_INVALID_FILE, SORT_MEMBERS_PARSE_ERRORS, UNANALYZED_PRIORITY_FILES, UNKNOWN_REQUEST, UNSUPPORTED_FEATURE];
 
   final String name;
 
@@ -9332,6 +9380,8 @@
         return FORMAT_INVALID_FILE;
       case "GET_ERRORS_INVALID_FILE":
         return GET_ERRORS_INVALID_FILE;
+      case "INVALID_EXECUTION_CONTEXT":
+        return INVALID_EXECUTION_CONTEXT;
       case "INVALID_OVERLAY_CHANGE":
         return INVALID_OVERLAY_CHANGE;
       case "INVALID_PARAMETER":
diff --git a/pkg/analysis_server/lib/src/get_handler.dart b/pkg/analysis_server/lib/src/get_handler.dart
index 2e2cb3e..2dadde0 100644
--- a/pkg/analysis_server/lib/src/get_handler.dart
+++ b/pkg/analysis_server/lib/src/get_handler.dart
@@ -29,6 +29,7 @@
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/java_engine.dart';
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/utilities_general.dart';
 
 import 'analysis_server.dart';
 
@@ -44,9 +45,9 @@
  */
 class GetHandler {
   /**
-   * The path used to request the status of the analysis server as a whole.
+   * The path used to request overall performance information.
    */
-  static const String STATUS_PATH = '/status';
+  static const String ANALYSIS_PERFORMANCE_PATH = '/perf/analysis';
 
   /**
    * The path used to request information about a element model.
@@ -71,6 +72,11 @@
   static const String COMPLETION_PATH = '/completion';
 
   /**
+   * The path used to request communication performance information.
+   */
+  static const String COMMUNICATION_PERFORMANCE_PATH = '/perf/communication';
+
+  /**
    * The path used to request information about a specific context.
    */
   static const String CONTEXT_PATH = '/context';
@@ -96,15 +102,9 @@
   static const String OVERLAYS_PATH = '/overlays';
 
   /**
-   * The path used to request overall performance information.
+   * The path used to request the status of the analysis server as a whole.
    */
-  static const String PERFORMANCE_PATH = '/performance';
-
-  /**
-   * Query parameter used to represent the cache state to search for, when
-   * accessing [CACHE_STATE_PATH].
-   */
-  static const String STATE_QUERY_PARAM = 'state';
+  static const String STATUS_PATH = '/status';
 
   /**
    * Query parameter used to represent the context to search for, when
@@ -135,6 +135,12 @@
    */
   static const String SOURCE_QUERY_PARAM = 'entry';
 
+  /**
+   * Query parameter used to represent the cache state to search for, when
+   * accessing [CACHE_STATE_PATH].
+   */
+  static const String STATE_QUERY_PARAM = 'state';
+
   static final ContentType _htmlContent =
       new ContentType("text", "html", charset: "utf-8");
 
@@ -180,6 +186,8 @@
     String path = request.uri.path;
     if (path == STATUS_PATH) {
       _returnServerStatus(request);
+    } else if (path == ANALYSIS_PERFORMANCE_PATH) {
+      _returnAnalysisPerformance(request);
     } else if (path == AST_PATH) {
       _returnAst(request);
     } else if (path == CACHE_STATE_PATH) {
@@ -188,6 +196,8 @@
       _returnCacheEntry(request);
     } else if (path == COMPLETION_PATH) {
       _returnCompletionInfo(request);
+    } else if (path == COMMUNICATION_PERFORMANCE_PATH) {
+      _returnCommunicationPerformance(request);
     } else if (path == CONTEXT_PATH) {
       _returnContextInfo(request);
     } else if (path == ELEMENT_PATH) {
@@ -198,8 +208,6 @@
       _returnOverlayContents(request);
     } else if (path == OVERLAYS_PATH) {
       _returnOverlaysInfo(request);
-    } else if (path == PERFORMANCE_PATH) {
-      _returnPerformance(request);
     } else {
       _returnUnknownRequest(request);
     }
@@ -246,16 +254,67 @@
   }
 
   /**
-   * Create a link to [path] with query parameters [params], with inner HTML
-   * [innerHtml]. If [hasError] is `true`, then the link will have the class
-   * 'error'.
+   * Return a response displaying overall performance information.
    */
-  String _makeLink(String path, Map<String, String> params, String innerHtml,
-      [bool hasError = false]) {
-    Uri uri = new Uri(path: path, queryParameters: params);
-    String href = HTML_ESCAPE.convert(uri.toString());
-    String classAttribute = hasError ? ' class="error"' : '';
-    return '<a href="$href"$classAttribute>$innerHtml</a>';
+  void _returnAnalysisPerformance(HttpRequest request) {
+    AnalysisServer analysisServer = _server.analysisServer;
+    if (analysisServer == null) {
+      return _returnFailure(request, 'Analysis server is not running');
+    }
+    _writeResponse(request, (StringBuffer buffer) {
+      _writePage(
+          buffer,
+          'Analysis Server - Analysis Performance',
+          [],
+          (StringBuffer buffer) {
+        void writeRow(TimeCounter counter, String label) {
+          _writeRow(buffer, [counter.result, label], classes: ["right", null]);
+        }
+
+        buffer.write('<h3>Analysis Performance</h3>');
+        buffer.write('<p><b>Time spent in each phase of analysis</b></p>');
+        buffer.write(
+            '<table style="border-collapse: separate; border-spacing: 10px 5px;">');
+        _writeRow(buffer, ['Time (in ms)', 'Analysis Phase'], header: true);
+        writeRow(PerformanceStatistics.io, 'io');
+        writeRow(PerformanceStatistics.scan, 'scan');
+        writeRow(PerformanceStatistics.parse, 'parse');
+        writeRow(PerformanceStatistics.resolve, 'resolve');
+        writeRow(PerformanceStatistics.errors, 'errors');
+        writeRow(PerformanceStatistics.hints, 'hints');
+        writeRow(PerformanceStatistics.lint, 'lint');
+        buffer.write('</table>');
+
+        Map<DataDescriptor, Map<CacheState, int>> transitionMap = SourceEntry.transitionMap;
+        buffer.write('<p><b>Number of times a state transitioned to VALID (grouped by descriptor)</b></p>');
+        if (transitionMap.isEmpty) {
+          buffer.write('<p>none</p>');
+        } else {
+          List<DataDescriptor> descriptors = transitionMap.keys.toList();
+          descriptors.sort(
+              (DataDescriptor first, DataDescriptor second) =>
+                  first.toString().compareTo(second.toString()));
+          for (DataDescriptor key in descriptors) {
+            Map<CacheState, int> countMap = transitionMap[key];
+            List<CacheState> oldStates = countMap.keys.toList();
+            oldStates.sort(
+                (CacheState first, CacheState second) =>
+                    first.toString().compareTo(second.toString()));
+            buffer.write('<p>${key.toString()}</p>');
+            buffer.write(
+                '<table style="border-collapse: separate; border-spacing: 10px 5px;">');
+            _writeRow(buffer, ['Count', 'Previous State'], header: true);
+            for (CacheState state in oldStates) {
+              _writeRow(
+                  buffer,
+                  [countMap[state], state.toString()],
+                  classes: ["right", null]);
+            }
+            buffer.write('</table>');
+          }
+        }
+      });
+    });
   }
 
   /**
@@ -381,7 +440,7 @@
           if (analyzingContext == context) {
             buffer.write(folder.path);
           } else {
-            buffer.write(_makeLink(CACHE_ENTRY_PATH, {
+            buffer.write(makeLink(CACHE_ENTRY_PATH, {
               CONTEXT_QUERY_PARAM: folder.path,
               SOURCE_QUERY_PARAM: sourceUri
             }, HTML_ESCAPE.convert(folder.path)));
@@ -485,7 +544,7 @@
       if (state != stateFilter || rowDesc.toString() != descriptorFilter) {
         return;
       }
-      String link = _makeLink(CACHE_ENTRY_PATH, {
+      String link = makeLink(CACHE_ENTRY_PATH, {
         CONTEXT_QUERY_PARAM: folder.path,
         SOURCE_QUERY_PARAM: source.uri.toString()
       }, HTML_ESCAPE.convert(source.fullName));
@@ -512,6 +571,91 @@
   }
 
   /**
+   * Return a response displaying overall performance information.
+   */
+  void _returnCommunicationPerformance(HttpRequest request) {
+    AnalysisServer analysisServer = _server.analysisServer;
+    if (analysisServer == null) {
+      return _returnFailure(request, 'Analysis server is not running');
+    }
+    _writeResponse(request, (StringBuffer buffer) {
+      _writePage(
+          buffer,
+          'Analysis Server - Communication Performance',
+          [],
+          (StringBuffer buffer) {
+        buffer.write('<h3>Communication Performance</h3>');
+        _writeTwoColumns(buffer, (StringBuffer buffer) {
+          ServerPerformance perf = analysisServer.performanceDuringStartup;
+          int requestCount = perf.requestCount;
+          num averageLatency =
+              requestCount > 0 ? (perf.requestLatency / requestCount).round() : 0;
+          int maximumLatency = perf.maxLatency;
+          num slowRequestPercent =
+              requestCount > 0 ? (perf.slowRequestCount * 100 / requestCount).round() : 0;
+          buffer.write('<h4>Startup</h4>');
+          buffer.write('<table>');
+          _writeRow(
+              buffer,
+              [requestCount, 'requests'],
+              classes: ["right", null]);
+          _writeRow(
+              buffer,
+              [averageLatency, 'ms average latency'],
+              classes: ["right", null]);
+          _writeRow(
+              buffer,
+              [maximumLatency, 'ms maximum latency'],
+              classes: ["right", null]);
+          _writeRow(
+              buffer,
+              [slowRequestPercent, '% > 150 ms latency'],
+              classes: ["right", null]);
+          if (analysisServer.performanceAfterStartup != null) {
+            int startupTime =
+                analysisServer.performanceAfterStartup.startTime -
+                perf.startTime;
+            _writeRow(
+                buffer,
+                [startupTime, 'ms for initial analysis to complete']);
+          }
+          buffer.write('</table>');
+        }, (StringBuffer buffer) {
+          ServerPerformance perf = analysisServer.performanceAfterStartup;
+          if (perf == null) {
+            return;
+          }
+          int requestCount = perf.requestCount;
+          num averageLatency =
+              requestCount > 0 ? (perf.requestLatency * 10 / requestCount).round() / 10 : 0;
+          int maximumLatency = perf.maxLatency;
+          num slowRequestPercent =
+              requestCount > 0 ? (perf.slowRequestCount * 100 / requestCount).round() : 0;
+          buffer.write('<h4>Current</h4>');
+          buffer.write('<table>');
+          _writeRow(
+              buffer,
+              [requestCount, 'requests'],
+              classes: ["right", null]);
+          _writeRow(
+              buffer,
+              [averageLatency, 'ms average latency'],
+              classes: ["right", null]);
+          _writeRow(
+              buffer,
+              [maximumLatency, 'ms maximum latency'],
+              classes: ["right", null]);
+          _writeRow(
+              buffer,
+              [slowRequestPercent, '% > 150 ms latency'],
+              classes: ["right", null]);
+          buffer.write('</table>');
+        });
+      });
+    });
+  }
+
+  /**
    * Return a response displaying code completion information.
    */
   void _returnCompletionInfo(HttpRequest request) {
@@ -567,7 +711,7 @@
         if (exception != null) {
           exceptions.add(exception);
         }
-        String link = _makeLink(CACHE_ENTRY_PATH, {
+        String link = makeLink(CACHE_ENTRY_PATH, {
           CONTEXT_QUERY_PARAM: folder.path,
           SOURCE_QUERY_PARAM: source.uri.toString()
         }, sourceName, exception != null);
@@ -601,7 +745,7 @@
           buffer.write(links[fileName]);
           buffer.write('</td><td>');
           if (overlayMap.containsKey(fileName)) {
-            buffer.write(_makeLink(OVERLAY_PATH, {
+            buffer.write(makeLink(OVERLAY_PATH, {
               ID_PARAM: overlayMap[fileName].toString()
             }, 'overlay'));
           }
@@ -632,7 +776,7 @@
               CONTEXT_QUERY_PARAM: folder.path,
               DESCRIPTOR_QUERY_PARAM: row.name
             };
-            rowText.add(_makeLink(CACHE_STATE_PATH, params, text));
+            rowText.add(makeLink(CACHE_STATE_PATH, params, text));
           }
           _writeRow(buffer, rowText, classes: [null, "right"]);
         });
@@ -826,91 +970,6 @@
   }
 
   /**
-   * Return a response displaying overall performance information.
-   */
-  void _returnPerformance(HttpRequest request) {
-    AnalysisServer analysisServer = _server.analysisServer;
-    if (analysisServer == null) {
-      return _returnFailure(request, 'Analysis server is not running');
-    }
-    _writeResponse(request, (StringBuffer buffer) {
-      _writePage(
-          buffer,
-          'Analysis Server - Performance',
-          [],
-          (StringBuffer buffer) {
-        buffer.write('<h3>Communication Performance</h3>');
-        _writeTwoColumns(buffer, (StringBuffer buffer) {
-          ServerPerformance perf = analysisServer.performanceDuringStartup;
-          int requestCount = perf.requestCount;
-          num averageLatency =
-              requestCount > 0 ? (perf.requestLatency / requestCount).round() : 0;
-          int maximumLatency = perf.maxLatency;
-          num slowRequestPercent =
-              requestCount > 0 ? (perf.slowRequestCount * 100 / requestCount).round() : 0;
-          buffer.write('<h4>Startup</h4>');
-          buffer.write('<table>');
-          _writeRow(
-              buffer,
-              [requestCount, 'requests'],
-              classes: ["right", null]);
-          _writeRow(
-              buffer,
-              [averageLatency, 'ms average latency'],
-              classes: ["right", null]);
-          _writeRow(
-              buffer,
-              [maximumLatency, 'ms maximum latency'],
-              classes: ["right", null]);
-          _writeRow(
-              buffer,
-              [slowRequestPercent, '% > 150 ms latency'],
-              classes: ["right", null]);
-          if (analysisServer.performanceAfterStartup != null) {
-            int startupTime =
-                analysisServer.performanceAfterStartup.startTime -
-                perf.startTime;
-            _writeRow(
-                buffer,
-                [startupTime, 'ms for initial analysis to complete']);
-          }
-          buffer.write('</table>');
-        }, (StringBuffer buffer) {
-          ServerPerformance perf = analysisServer.performanceAfterStartup;
-          if (perf == null) {
-            return;
-          }
-          int requestCount = perf.requestCount;
-          num averageLatency =
-              requestCount > 0 ? (perf.requestLatency * 10 / requestCount).round() / 10 : 0;
-          int maximumLatency = perf.maxLatency;
-          num slowRequestPercent =
-              requestCount > 0 ? (perf.slowRequestCount * 100 / requestCount).round() : 0;
-          buffer.write('<h4>Current</h4>');
-          buffer.write('<table>');
-          _writeRow(
-              buffer,
-              [requestCount, 'requests'],
-              classes: ["right", null]);
-          _writeRow(
-              buffer,
-              [averageLatency, 'ms average latency'],
-              classes: ["right", null]);
-          _writeRow(
-              buffer,
-              [maximumLatency, 'ms maximum latency'],
-              classes: ["right", null]);
-          _writeRow(
-              buffer,
-              [slowRequestPercent, '% > 150 ms latency'],
-              classes: ["right", null]);
-          buffer.write('</table>');
-        });
-      });
-    });
-  }
-
-  /**
    * Return a response indicating the status of the analysis server.
    */
   void _returnServerStatus(HttpRequest request) {
@@ -935,13 +994,14 @@
       _writePage(buffer, 'Analysis Server', [], (StringBuffer buffer) {
         buffer.write('<h3>Pages</h3>');
         buffer.write('<p>');
-        buffer.write(_makeLink(COMPLETION_PATH, {}, 'Completion data'));
+        buffer.write(makeLink(COMPLETION_PATH, {}, 'Completion data'));
         buffer.write('</p>');
         buffer.write('<p>');
-        buffer.write(_makeLink(PERFORMANCE_PATH, {}, 'Performance'));
+        buffer.write(
+            makeLink(COMMUNICATION_PERFORMANCE_PATH, {}, 'Performance'));
         buffer.write('</p>');
         buffer.write('<p>');
-        buffer.write(_makeLink(STATUS_PATH, {}, 'Server status'));
+        buffer.write(makeLink(STATUS_PATH, {}, 'Server status'));
         buffer.write('</p>');
       });
     });
@@ -1000,7 +1060,7 @@
           buffer.write('<br>');
         }
         String key = folder.shortName;
-        buffer.write(_makeLink(CONTEXT_PATH, {
+        buffer.write(makeLink(CONTEXT_PATH, {
           CONTEXT_QUERY_PARAM: folder.path
         }, key, _hasException(folderMap[folder])));
       });
@@ -1030,6 +1090,11 @@
       int freq = AnalysisServer.performOperationDelayFreqency;
       String delay = freq > 0 ? '1 ms every $freq ms' : 'off';
       buffer.write('<p><b>perform operation delay:</b> $delay</p>');
+
+      buffer.write('<p><b>Performance Data</b></p>');
+      buffer.write('<p>');
+      buffer.write(makeLink(ANALYSIS_PERFORMANCE_PATH, {}, 'Task data'));
+      buffer.write('</p>');
     }, (StringBuffer buffer) {
       _writeSubscriptionMap(
           buffer,
@@ -1104,7 +1169,7 @@
         header: true);
     int index = 0;
     for (CompletionPerformance performance in handler.performanceList) {
-      String link = _makeLink(COMPLETION_PATH, {
+      String link = makeLink(COMPLETION_PATH, {
         'index': '$index'
       }, '${performance.startTimeAndMs}');
       _writeRow(
@@ -1177,7 +1242,7 @@
     _writeTwoColumns(buffer, (StringBuffer buffer) {
       buffer.write('<p><b>Performance Data</b></p>');
       buffer.write('<p>');
-      buffer.write(_makeLink(COMPLETION_PATH, {}, 'Completion data'));
+      buffer.write(makeLink(COMPLETION_PATH, {}, 'Completion data'));
       buffer.write('</p>');
     }, (StringBuffer buffer) {
     });
@@ -1418,7 +1483,7 @@
       buffer.write('<p><b>Performance Data</b></p>');
       buffer.write('<p>');
       buffer.write(
-          _makeLink(PERFORMANCE_PATH, {}, 'Communication performance'));
+          makeLink(COMMUNICATION_PERFORMANCE_PATH, {}, 'Communication performance'));
       buffer.write('</p>');
     }, (StringBuffer buffer) {
       _writeSubscriptionList(buffer, ServerService.VALUES, services);
@@ -1547,15 +1612,28 @@
       buffer.write('</ul>');
     } else if (value is AstNode) {
       String link =
-          _makeLink(AST_PATH, linkParameters, value.runtimeType.toString());
+          makeLink(AST_PATH, linkParameters, value.runtimeType.toString());
       buffer.write('<i>$link</i>');
     } else if (value is Element) {
       String link =
-          _makeLink(ELEMENT_PATH, linkParameters, value.runtimeType.toString());
+          makeLink(ELEMENT_PATH, linkParameters, value.runtimeType.toString());
       buffer.write('<i>$link</i>');
     } else {
       buffer.write(HTML_ESCAPE.convert(value.toString()));
       buffer.write(' <i>(${value.runtimeType.toString()})</i>');
     }
   }
+
+  /**
+   * Create a link to [path] with query parameters [params], with inner HTML
+   * [innerHtml]. If [hasError] is `true`, then the link will have the class
+   * 'error'.
+   */
+  static String makeLink(String path, Map<String, String> params,
+      String innerHtml, [bool hasError = false]) {
+    Uri uri = new Uri(path: path, queryParameters: params);
+    String href = HTML_ESCAPE.convert(uri.toString());
+    String classAttribute = hasError ? ' class="error"' : '';
+    return '<a href="$href"$classAttribute>$innerHtml</a>';
+  }
 }
diff --git a/pkg/analysis_server/lib/src/operation/operation.dart b/pkg/analysis_server/lib/src/operation/operation.dart
index 03fd913..e9ed101 100644
--- a/pkg/analysis_server/lib/src/operation/operation.dart
+++ b/pkg/analysis_server/lib/src/operation/operation.dart
@@ -5,6 +5,7 @@
 library operation;
 
 import 'package:analysis_server/src/analysis_server.dart';
+import 'package:analyzer/src/generated/source.dart';
 
 
 /**
@@ -58,3 +59,16 @@
   @override
   String toString() => name;
 }
+
+
+/**
+ * [SourceSensitiveOperation] can decide if the operation should be discarded
+ * before a change is applied to a [Source].
+ */
+abstract class SourceSensitiveOperation extends ServerOperation {
+  /**
+   * The given [source] is about to be changed.
+   * Check if this [SourceSensitiveOperation] should be discarded.
+   */
+  bool shouldBeDiscardedOnSourceChange(Source source);
+}
diff --git a/pkg/analysis_server/lib/src/operation/operation_analysis.dart b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
index ec612a5..7d77d4a 100644
--- a/pkg/analysis_server/lib/src/operation/operation_analysis.dart
+++ b/pkg/analysis_server/lib/src/operation/operation_analysis.dart
@@ -264,11 +264,12 @@
       return;
     }
     for (ChangeNotice notice in notices) {
+      String file = notice.source.fullName;
       // Dart
       try {
         CompilationUnit dartUnit = notice.resolvedDartUnit;
         if (dartUnit != null) {
-          server.addOperation(new _DartIndexOperation(context, dartUnit));
+          server.addOperation(new _DartIndexOperation(context, file, dartUnit));
         }
       } catch (exception, stackTrace) {
         server.sendServerErrorNotification(exception, stackTrace);
@@ -277,7 +278,7 @@
       try {
         HtmlUnit htmlUnit = notice.resolvedHtmlUnit;
         if (htmlUnit != null) {
-          server.addOperation(new _HtmlIndexOperation(context, htmlUnit));
+          server.addOperation(new _HtmlIndexOperation(context, file, htmlUnit));
         }
       } catch (exception, stackTrace) {
         server.sendServerErrorNotification(exception, stackTrace);
@@ -298,11 +299,11 @@
 }
 
 
-class _DartIndexOperation extends ServerOperation {
+class _DartIndexOperation extends _SingleFileOperation {
   final AnalysisContext context;
   final CompilationUnit unit;
 
-  _DartIndexOperation(this.context, this.unit);
+  _DartIndexOperation(this.context, String file, this.unit) : super(file);
 
   @override
   ServerOperationPriority get priority {
@@ -328,11 +329,10 @@
 }
 
 
-abstract class _DartNotificationOperation extends ServerOperation {
-  final String file;
+abstract class _DartNotificationOperation extends _SingleFileOperation {
   final CompilationUnit unit;
 
-  _DartNotificationOperation(this.file, this.unit);
+  _DartNotificationOperation(String file, this.unit) : super(file);
 
   @override
   ServerOperationPriority get priority {
@@ -376,11 +376,11 @@
 }
 
 
-class _HtmlIndexOperation extends ServerOperation {
+class _HtmlIndexOperation extends _SingleFileOperation {
   final AnalysisContext context;
   final HtmlUnit unit;
 
-  _HtmlIndexOperation(this.context, this.unit);
+  _HtmlIndexOperation(this.context, String file, this.unit) : super(file);
 
   @override
   ServerOperationPriority get priority {
@@ -395,12 +395,12 @@
 }
 
 
-class _NotificationErrorsOperation extends ServerOperation {
-  final String file;
+class _NotificationErrorsOperation extends _SingleFileOperation {
   final LineInfo lineInfo;
   final List<AnalysisError> errors;
 
-  _NotificationErrorsOperation(this.file, this.lineInfo, this.errors);
+  _NotificationErrorsOperation(String file, this.lineInfo, this.errors)
+      : super(file);
 
   @override
   ServerOperationPriority get priority {
@@ -412,3 +412,15 @@
     sendAnalysisNotificationErrors(server, file, lineInfo, errors);
   }
 }
+
+
+abstract class _SingleFileOperation extends SourceSensitiveOperation {
+  final String file;
+
+  _SingleFileOperation(this.file);
+
+  @override
+  bool shouldBeDiscardedOnSourceChange(Source source) {
+    return source.fullName == file;
+  }
+}
diff --git a/pkg/analysis_server/lib/src/operation/operation_queue.dart b/pkg/analysis_server/lib/src/operation/operation_queue.dart
index 67ca993..deaa512 100644
--- a/pkg/analysis_server/lib/src/operation/operation_queue.dart
+++ b/pkg/analysis_server/lib/src/operation/operation_queue.dart
@@ -8,6 +8,7 @@
 
 import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/operation/operation.dart';
+import 'package:analyzer/src/generated/source.dart';
 
 
 /**
@@ -51,8 +52,8 @@
   }
 
   /**
-   * Return the next operation to perform, or `null` if the queue is empty. This
-   * method does not change the queue.
+   * Return the next operation to perform, or `null` if the queue is empty.
+   * This method does not change the queue.
    */
   ServerOperation peek() {
     for (Queue<ServerOperation> queue in _queues) {
@@ -78,6 +79,20 @@
   }
 
   /**
+   * The given [source] if about to changed.
+   */
+  void sourceAboutToChange(Source source) {
+    for (Queue<ServerOperation> queue in _queues) {
+      queue.removeWhere((operation) {
+        if (operation is SourceSensitiveOperation) {
+          return operation.shouldBeDiscardedOnSourceChange(source);
+        }
+        return false;
+      });
+    }
+  }
+
+  /**
    * Returns the next operation to perform or `null` if empty.
    */
   ServerOperation take() {
@@ -88,4 +103,19 @@
     }
     return null;
   }
+
+  /**
+   * Returns an operation that satisfies the given [test] or `null`.
+   */
+  ServerOperation takeIf(bool test(ServerOperation operation)) {
+    for (Queue<ServerOperation> queue in _queues) {
+      for (var operation in queue) {
+        if (test(operation)) {
+          queue.remove(operation);
+          return operation;
+        }
+      }
+    }
+    return null;
+  }
 }
diff --git a/pkg/analysis_server/lib/src/protocol.dart b/pkg/analysis_server/lib/src/protocol.dart
index dfa810cd..10aea74 100644
--- a/pkg/analysis_server/lib/src/protocol.dart
+++ b/pkg/analysis_server/lib/src/protocol.dart
@@ -763,6 +763,18 @@
 
   /**
    * Initialize a newly created instance to represent an error condition caused
+   * by a [request] that specifies an execution context whose context root does
+   * not exist.
+   */
+  Response.invalidExecutionContext(Request request, String contextId)
+      : this(
+          request.id,
+          error: new RequestError(
+              RequestErrorCode.INVALID_EXECUTION_CONTEXT,
+              "Invalid execution context: $contextId"));
+
+  /**
+   * Initialize a newly created instance to represent an error condition caused
    * 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).
@@ -773,7 +785,7 @@
           request.id,
           error: new RequestError(
               RequestErrorCode.INVALID_PARAMETER,
-              "Expected parameter $path to $expectation"));
+              "Invalid parameter '$path'. $expectation."));
 
   /**
    * Initialize a newly created instance to represent an error condition caused
@@ -785,6 +797,17 @@
           error: new RequestError(RequestErrorCode.INVALID_REQUEST, 'Invalid request'));
 
   /**
+   * Initialize a newly created instance to represent the
+   * REFACTORING_REQUEST_CANCELLED error condition.
+   */
+  Response.refactoringRequestCancelled(Request request)
+      : this(
+          request.id,
+          error: new RequestError(
+              RequestErrorCode.REFACTORING_REQUEST_CANCELLED,
+              'The `edit.getRefactoring` request was cancelled.'));
+
+  /**
    * Initialize a newly created instance to represent the SERVER_ERROR error
    * condition.
    */
@@ -810,17 +833,6 @@
 
   /**
    * Initialize a newly created instance to represent the
-   * REFACTORING_REQUEST_CANCELLED error condition.
-   */
-  Response.refactoringRequestCancelled(Request request)
-      : this(
-          request.id,
-          error: new RequestError(
-              RequestErrorCode.REFACTORING_REQUEST_CANCELLED,
-              'The `edit.getRefactoring` request was cancelled.'));
-
-  /**
-   * Initialize a newly created instance to represent the
    * SORT_MEMBERS_PARSE_ERRORS error condition.
    */
   Response.sortMembersParseErrors(Request request, int numErrors)
diff --git a/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart b/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart
index 54bb27d..3c20785 100644
--- a/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart
+++ b/pkg/analysis_server/lib/src/services/completion/dart_completion_manager.dart
@@ -118,6 +118,9 @@
       request.unit = unit;
       request.node = new NodeLocator.con1(request.offset).searchWithin(unit);
       request.target = new CompletionTarget.forOffset(unit, request.offset);
+      if (request.node == null) {
+        return [];
+      }
 
       request.replacementOffset = request.offset;
       request.replacementLength = 0;
diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
index 71f55ba..31b0587 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -81,6 +81,7 @@
     }
     // try to add proposals
     _addProposal_addTypeAnnotation_DeclaredIdentifier();
+    _addProposal_addTypeAnnotation_SimpleFormalParameter();
     _addProposal_addTypeAnnotation_VariableDeclaration();
     _addProposal_assignToLocalVariable();
     _addProposal_convertToBlockFunctionBody();
@@ -314,6 +315,44 @@
     _addAssist(AssistKind.ADD_TYPE_ANNOTATION, []);
   }
 
+  void _addProposal_addTypeAnnotation_SimpleFormalParameter() {
+    AstNode node = this.node;
+    // should be the name of a simple parameter
+    if (node is! SimpleIdentifier || node.parent is! SimpleFormalParameter) {
+      _coverageMarker();
+      return;
+    }
+    SimpleIdentifier name = node;
+    SimpleFormalParameter parameter = node.parent;
+    // the parameter should not have a type
+    if (parameter.type != null) {
+      _coverageMarker();
+      return;
+    }
+    // prepare propagated type
+    DartType type = name.propagatedType;
+    // TODO(scheglov) If the parameter is in a method declaration, and if the
+    // method overrides a method that has a type for the corresponding
+    // parameter, it would be nice to copy down the type from the overridden
+    // method.
+    if (type is! InterfaceType) {
+      _coverageMarker();
+      return;
+    }
+    // prepare type source
+    String typeSource;
+    {
+      _configureTargetLocation(node);
+      Set<LibraryElement> librariesToImport = new Set<LibraryElement>();
+      typeSource = utils.getTypeSource(type, librariesToImport);
+      _addLibraryImports(librariesToImport);
+    }
+    // add edit
+    _addInsertEdit(name.offset, '$typeSource ');
+    // add proposal
+    _addAssist(AssistKind.ADD_TYPE_ANNOTATION, []);
+  }
+
   void _addProposal_assignToLocalVariable() {
     // prepare enclosing ExpressionStatement
     Statement statement = node.getAncestor((node) => node is Statement);
diff --git a/pkg/analysis_server/lib/src/services/correction/fix.dart b/pkg/analysis_server/lib/src/services/correction/fix.dart
index 4aa4c62..18094cf 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix.dart
@@ -47,6 +47,8 @@
  * An enumeration of possible quick fix kinds.
  */
 class FixKind {
+  static const ADD_ASYNC =
+      const FixKind('ADD_ASYNC', 50, "Add 'async' modifier");
   static const ADD_PACKAGE_DEPENDENCY =
       const FixKind('ADD_PACKAGE_DEPENDENCY', 50, "Add dependency on package '{0}'");
   static const ADD_SUPER_CONSTRUCTOR_INVOCATION = const FixKind(
diff --git a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
index c46ee05..05e1e61 100644
--- a/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/fix_internal.dart
@@ -192,18 +192,23 @@
       _addFix_createMissingOverrides(missingOverrides);
       _addFix_createNoSuchMethod();
     }
-    if (errorCode == StaticWarningCode.UNDEFINED_CLASS) {
+    if (errorCode == StaticWarningCode.CAST_TO_NON_TYPE ||
+        errorCode == StaticWarningCode.TYPE_TEST_WITH_UNDEFINED_NAME ||
+        errorCode == StaticWarningCode.UNDEFINED_CLASS) {
       _addFix_importLibrary_withType();
       _addFix_createClass();
       _addFix_undefinedClass_useSimilar();
     }
     if (errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER) {
-      _addFix_createField();
-      _addFix_createGetter();
-      _addFix_createFunction_forFunctionType();
-      _addFix_importLibrary_withType();
-      _addFix_importLibrary_withTopLevelVariable();
-      _addFix_createLocalVariable();
+      bool isAsync = _addFix_addAsync();
+      if (!isAsync) {
+        _addFix_createField();
+        _addFix_createGetter();
+        _addFix_createFunction_forFunctionType();
+        _addFix_importLibrary_withType();
+        _addFix_importLibrary_withTopLevelVariable();
+        _addFix_createLocalVariable();
+      }
     }
     if (errorCode == StaticTypeWarningCode.INSTANCE_ACCESS_TO_STATIC_MEMBER) {
       _addFix_useStaticAccess_method();
@@ -264,6 +269,22 @@
     exitPosition = null;
   }
 
+  /**
+   * Returns `true` if the `async` proposal was added.
+   */
+  bool _addFix_addAsync() {
+    AstNode node = this.node;
+    if (_isAwaitNode()) {
+      FunctionBody body = node.getAncestor((n) => n is FunctionBody);
+      if (body.keyword == null) {
+        _addReplaceEdit(rf.rangeStartLength(body, 0), 'async ');
+        _addFix(FixKind.ADD_ASYNC, []);
+        return true;
+      }
+    }
+    return false;
+  }
+
   void _addFix_boolInsteadOfBoolean() {
     SourceRange range = rf.rangeError(error);
     _addReplaceEdit(range, 'bool');
@@ -816,8 +837,13 @@
     SourceBuilder sb = new SourceBuilder(file, target.offset);
     {
       // append type
-      DartType fieldType = _inferUndefinedExpressionType(node);
-      _appendType(sb, fieldType, groupId: 'TYPE', orVar: true);
+      DartType type = _inferUndefinedExpressionType(node);
+      if (!(type == null ||
+          type is InterfaceType ||
+          type is FunctionType && type.element != null && !type.element.isSynthetic)) {
+        return;
+      }
+      _appendType(sb, type, groupId: 'TYPE', orVar: true);
       // append name
       {
         sb.startPosition('NAME');
@@ -1164,6 +1190,9 @@
 
   void _addFix_insertSemicolon() {
     if (error.message.contains("';'")) {
+      if (_isAwaitNode()) {
+        return;
+      }
       int insertOffset = error.offset + error.length;
       _addInsertEdit(insertOffset, ';');
       _addFix(FixKind.INSERT_SEMICOLON, []);
@@ -1609,6 +1638,9 @@
 
   void _addFixToElement(FixKind kind, List args, Element element) {
     Source source = element.source;
+    if (source.isInSystemLibrary) {
+      return;
+    }
     String file = source.fullName;
     int fileStamp = element.context.getModificationStamp(source);
     _addFix(kind, args, file: file, fileStamp: fileStamp);
@@ -2065,6 +2097,11 @@
     return method != null && method.isStatic;
   }
 
+  bool _isAwaitNode() {
+    AstNode node = this.node;
+    return node is SimpleIdentifier && node.name == 'await';
+  }
+
   _ConstructorLocation
       _prepareNewConstructorLocation(ClassDeclaration classDeclaration) {
     List<ClassMember> members = classDeclaration.members;
diff --git a/pkg/analysis_server/lib/src/services/index/store/codec.dart b/pkg/analysis_server/lib/src/services/index/store/codec.dart
index 8f6a2ed..ef36535b 100644
--- a/pkg/analysis_server/lib/src/services/index/store/codec.dart
+++ b/pkg/analysis_server/lib/src/services/index/store/codec.dart
@@ -93,8 +93,7 @@
     List<int> path = _indexToPath[index];
     List<String> components = _getLocationComponents(path);
     ElementLocation location = new ElementLocationImpl.con3(components);
-    Element element = context.getElement(location);
-    return element;
+    return context.getElement(location);
   }
 
   /**
diff --git a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
index bea040a..c6ed68c 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/convert_getter_to_method.dart
@@ -47,28 +47,28 @@
   }
 
   @override
-  Future<SourceChange> createChange() {
+  Future<SourceChange> createChange() async {
     change = new SourceChange(refactoringName);
     // function
     if (element.enclosingElement is CompilationUnitElement) {
       _updateElementDeclaration(element);
-      return _updateElementReferences(element).then((_) => change);
+      await _updateElementReferences(element);
     }
     // method
     if (element.enclosingElement is ClassElement) {
       FieldElement field = element.variable;
-      return getHierarchyMembers(searchEngine, field).then((elements) {
-        return Future.forEach(elements, (FieldElement field) {
-          PropertyAccessorElement getter = field.getter;
-          if (!getter.isSynthetic) {
-            _updateElementDeclaration(getter);
-            return _updateElementReferences(getter);
-          }
-        });
-      }).then((_) => change);
+      Set<ClassMemberElement> elements =
+          await getHierarchyMembers(searchEngine, field);
+      await Future.forEach(elements, (FieldElement field) {
+        PropertyAccessorElement getter = field.getter;
+        if (!getter.isSynthetic) {
+          _updateElementDeclaration(getter);
+          return _updateElementReferences(getter);
+        }
+      });
     }
-    // not reachable
-    return null;
+    // done
+    return change;
   }
 
   @override
@@ -106,16 +106,15 @@
     }
   }
 
-  Future _updateElementReferences(Element element) {
-    return searchEngine.searchReferences(element).then((matches) {
-      List<SourceReference> references = getSourceReferences(matches);
-      for (SourceReference reference in references) {
-        Element refElement = reference.element;
-        SourceRange refRange = reference.range;
-        // insert "()"
-        var edit = new SourceEdit(refRange.end, 0, "()");
-        doSourceChange_addElementEdit(change, refElement, edit);
-      }
-    });
+  Future _updateElementReferences(Element element) async {
+    List<SearchMatch> matches = await searchEngine.searchReferences(element);
+    List<SourceReference> references = getSourceReferences(matches);
+    for (SourceReference reference in references) {
+      Element refElement = reference.element;
+      SourceRange refRange = reference.range;
+      // insert "()"
+      var edit = new SourceEdit(refRange.end, 0, "()");
+      doSourceChange_addElementEdit(change, refElement, edit);
+    }
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart b/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
index 55cc6a7..e0a94cc 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/convert_method_to_getter.dart
@@ -46,25 +46,25 @@
   }
 
   @override
-  Future<SourceChange> createChange() {
+  Future<SourceChange> createChange() async {
     change = new SourceChange(refactoringName);
     // FunctionElement
     if (element is FunctionElement) {
       _updateElementDeclaration(element);
-      return _updateElementReferences(element).then((_) => change);
+      await _updateElementReferences(element);
     }
     // MethodElement
     if (element is MethodElement) {
       MethodElement method = element;
-      return getHierarchyMembers(searchEngine, method).then((elements) {
-        return Future.forEach(elements, (Element element) {
-          _updateElementDeclaration(element);
-          return _updateElementReferences(element);
-        });
-      }).then((_) => change);
+      Set<ClassMemberElement> elements =
+          await getHierarchyMembers(searchEngine, method);
+      await Future.forEach(elements, (Element element) {
+        _updateElementDeclaration(element);
+        return _updateElementReferences(element);
+      });
     }
-    // not reachable
-    return null;
+    // done
+    return change;
   }
 
   @override
@@ -114,27 +114,26 @@
     }
   }
 
-  Future _updateElementReferences(Element element) {
-    return searchEngine.searchReferences(element).then((matches) {
-      List<SourceReference> references = getSourceReferences(matches);
-      for (SourceReference reference in references) {
-        Element refElement = reference.element;
-        SourceRange refRange = reference.range;
-        // prepare invocation
-        MethodInvocation invocation;
-        {
-          CompilationUnit refUnit = refElement.unit;
-          AstNode refNode =
-              new NodeLocator.con1(refRange.offset).searchWithin(refUnit);
-          invocation = refNode.getAncestor((node) => node is MethodInvocation);
-        }
-        // we need invocation
-        if (invocation != null) {
-          SourceRange range = rangeEndEnd(refRange, invocation);
-          SourceEdit edit = newSourceEdit_range(range, '');
-          doSourceChange_addElementEdit(change, refElement, edit);
-        }
+  Future _updateElementReferences(Element element) async {
+    List<SearchMatch> matches = await searchEngine.searchReferences(element);
+    List<SourceReference> references = getSourceReferences(matches);
+    for (SourceReference reference in references) {
+      Element refElement = reference.element;
+      SourceRange refRange = reference.range;
+      // prepare invocation
+      MethodInvocation invocation;
+      {
+        CompilationUnit refUnit = refElement.unit;
+        AstNode refNode =
+            new NodeLocator.con1(refRange.offset).searchWithin(refUnit);
+        invocation = refNode.getAncestor((node) => node is MethodInvocation);
       }
-    });
+      // we need invocation
+      if (invocation != null) {
+        SourceRange range = rangeEndEnd(refRange, invocation);
+        SourceEdit edit = newSourceEdit_range(range, '');
+        doSourceChange_addElementEdit(change, refElement, edit);
+      }
+    }
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
index 963a6f4..c222ff0 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/extract_method.dart
@@ -25,6 +25,7 @@
 import 'package:analyzer/src/generated/java_core.dart';
 import 'package:analyzer/src/generated/scanner.dart';
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/resolver.dart' show ExitDetector;
 
 
 const String _TOKEN_SEPARATOR = '\uFFFF';
@@ -57,6 +58,10 @@
  */
 class ExtractMethodRefactoringImpl extends RefactoringImpl implements
     ExtractMethodRefactoring {
+  static const ERROR_EXITS =
+      'Selected statements contain a return statement, but not all possible '
+          'execuion flows exit. Semantics may not be preserved.';
+
   final SearchEngine searchEngine;
   final CompilationUnit unit;
   final int selectionOffset;
@@ -164,14 +169,13 @@
   }
 
   @override
-  Future<RefactoringStatus> checkFinalConditions() {
+  Future<RefactoringStatus> checkFinalConditions() async {
     RefactoringStatus result = new RefactoringStatus();
     result.addStatus(validateMethodName(name));
     result.addStatus(_checkParameterNames());
-    return _checkPossibleConflicts().then((status) {
-      result.addStatus(status);
-      return result;
-    });
+    RefactoringStatus status = await _checkPossibleConflicts();
+    result.addStatus(status);
+    return result;
   }
 
 
@@ -572,7 +576,14 @@
     if (_selectionExpression != null) {
       _returnType = _selectionExpression.bestType;
     }
-    // may be ends with "return" statement
+    // verify that none or all execution flows end with a "return"
+    if (_selectionStatements != null) {
+      bool hasReturn = _selectionStatements.any(_mayEndWithReturnStatement);
+      if (hasReturn && !ExitDetector.exits(_selectionStatements.last)) {
+        result.addError(ERROR_EXITS);
+      }
+    }
+    // maybe ends with "return" statement
     if (_selectionStatements != null) {
       _ReturnTypeComputer returnTypeComputer = new _ReturnTypeComputer();
       _selectionStatements.forEach((statement) {
@@ -580,7 +591,7 @@
       });
       _returnType = returnTypeComputer.returnType;
     }
-    // may be single variable to return
+    // maybe single variable to return
     if (assignedUsedVariables.length == 1) {
       // we cannot both return variable and have explicit return statement
       if (_returnType != null) {
@@ -698,6 +709,15 @@
     node.accept(visitor);
     return visitor.result;
   }
+
+  /**
+   * Returns `true` if the given [statement] may end with a [ReturnStatement].
+   */
+  static bool _mayEndWithReturnStatement(Statement statement) {
+    _HasReturnStatementVisitor visitor = new _HasReturnStatementVisitor();
+    statement.accept(visitor);
+    return visitor.hasReturn;
+  }
 }
 
 
@@ -865,6 +885,20 @@
 }
 
 
+class _HasReturnStatementVisitor extends RecursiveAstVisitor {
+  bool hasReturn = false;
+
+  @override
+  visitBlockFunctionBody(BlockFunctionBody node) {
+  }
+
+  @override
+  visitReturnStatement(ReturnStatement node) {
+    hasReturn = true;
+  }
+}
+
+
 class _InitializeOccurrencesVisitor extends GeneralizingAstVisitor<Object> {
   final ExtractMethodRefactoringImpl ref;
   final _SourcePattern selectionPattern;
@@ -972,7 +1006,6 @@
   }
 }
 
-
 class _InitializeParametersVisitor extends GeneralizingAstVisitor<Object> {
   final ExtractMethodRefactoringImpl ref;
   final List<VariableElement> assignedUsedVariables;
@@ -1031,6 +1064,7 @@
   }
 }
 
+
 class _IsUsedAfterSelectionVisitor extends GeneralizingAstVisitor {
   final ExtractMethodRefactoringImpl ref;
   final VariableElement element;
diff --git a/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart b/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
index 2c4e115..064021a 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/inline_local.dart
@@ -66,7 +66,7 @@
   }
 
   @override
-  Future<RefactoringStatus> checkInitialConditions() {
+  Future<RefactoringStatus> checkInitialConditions() async {
     RefactoringStatus result = new RefactoringStatus();
     // prepare variable
     {
@@ -96,22 +96,20 @@
       return new Future.value(result);
     }
     // prepare references
-    return searchEngine.searchReferences(_variableElement).then((references) {
-      this._references = references;
-      // should not have assignments
-      for (SearchMatch reference in _references) {
-        if (reference.kind != MatchKind.READ) {
-          String message = format(
-              "Local variable '{0}' is assigned more than once.",
-              [_variableElement.displayName]);
-          return new RefactoringStatus.fatal(
-              message,
-              newLocation_fromMatch(reference));
-        }
+    _references = await searchEngine.searchReferences(_variableElement);
+    // should not have assignments
+    for (SearchMatch reference in _references) {
+      if (reference.kind != MatchKind.READ) {
+        String message = format(
+            "Local variable '{0}' is assigned more than once.",
+            [_variableElement.displayName]);
+        return new RefactoringStatus.fatal(
+            message,
+            newLocation_fromMatch(reference));
       }
-      // done
-      return result;
-    });
+    }
+    // done
+    return result;
   }
 
   @override
diff --git a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
index af23849..dbbbe21 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/inline_method.dart
@@ -275,7 +275,7 @@
   }
 
   @override
-  Future<RefactoringStatus> checkInitialConditions() {
+  Future<RefactoringStatus> checkInitialConditions() async {
     RefactoringStatus result = new RefactoringStatus();
     // prepare method information
     result.addStatus(_prepareMethod());
@@ -290,16 +290,14 @@
     // analyze method body
     result.addStatus(_prepareMethodParts());
     // process references
-    return searchEngine.searchReferences(_methodElement).then((references) {
-      _referenceProcessors.clear();
-      for (SearchMatch reference in references) {
-        _ReferenceProcessor processor =
-            new _ReferenceProcessor(this, reference);
-        _referenceProcessors.add(processor);
-      }
-    }).then((_) {
-      return result;
-    });
+    List<SearchMatch> references =
+        await searchEngine.searchReferences(_methodElement);
+    _referenceProcessors.clear();
+    for (SearchMatch reference in references) {
+      _ReferenceProcessor processor = new _ReferenceProcessor(this, reference);
+      _referenceProcessors.add(processor);
+    }
+    return result;
   }
 
   @override
diff --git a/pkg/analysis_server/lib/src/services/refactoring/move_file.dart b/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
index b0e7197..0811b75 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/move_file.dart
@@ -56,10 +56,10 @@
   }
 
   @override
-  Future<SourceChange> createChange() {
+  Future<SourceChange> createChange() async {
     change = new SourceChange('Update File References');
     List<Source> librarySources = context.getLibrariesContaining(source);
-    return Future.forEach(librarySources, (Source librarySource) {
+    await Future.forEach(librarySources, (Source librarySource) async {
       CompilationUnitElement unitElement =
           context.getCompilationUnitElement(source, librarySource);
       if (unitElement != null) {
@@ -73,17 +73,16 @@
           _updateUriReferences(library.parts);
         }
         // update reference to the unit
-        return searchEngine.searchReferences(unitElement).then((matches) {
-          List<SourceReference> references = getSourceReferences(matches);
-          for (SourceReference reference in references) {
-            String newUri = _computeNewUri(reference);
-            reference.addEdit(change, "'$newUri'");
-          }
-        });
+        List<SearchMatch> matches =
+            await searchEngine.searchReferences(unitElement);
+        List<SourceReference> references = getSourceReferences(matches);
+        for (SourceReference reference in references) {
+          String newUri = _computeNewUri(reference);
+          reference.addEdit(change, "'$newUri'");
+        }
       }
-    }).then((_) {
-      return change;
     });
+    return change;
   }
 
   @override
diff --git a/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart b/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart
index 0654b95..ebb09d3 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/refactoring_internal.dart
@@ -45,18 +45,14 @@
   final List<String> potentialEditIds = <String>[];
 
   @override
-  Future<RefactoringStatus> checkAllConditions() {
+  Future<RefactoringStatus> checkAllConditions() async {
     RefactoringStatus result = new RefactoringStatus();
-    return checkInitialConditions().then((status) {
-      result.addStatus(status);
-      if (result.hasFatalError) {
-        return result;
-      }
-      return checkFinalConditions().then((status) {
-        result.addStatus(status);
-        return result;
-      });
-    });
+    result.addStatus(await checkInitialConditions());
+    if (result.hasFatalError) {
+      return result;
+    }
+    result.addStatus(await checkFinalConditions());
+    return result;
   }
 }
 
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename.dart b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
index 746a8d6..1da2200 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename.dart
@@ -157,9 +157,10 @@
   }
 
   @override
-  Future<SourceChange> createChange() {
+  Future<SourceChange> createChange() async {
     change = new SourceChange(refactoringName);
-    return fillChange().then((_) => change);
+    await fillChange();
+    return change;
   }
 
   /**
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
index 321785e..25e979e 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_class_member.dart
@@ -82,7 +82,7 @@
   }
 
   @override
-  Future fillChange() {
+  Future fillChange() async {
     // update declarations
     for (Element renameElement in _validator.elements) {
       if (renameElement.isSynthetic && renameElement is FieldElement) {
@@ -95,24 +95,24 @@
     // update references
     addReferenceEdits(_validator.references);
     // potential matches
-    return searchEngine.searchMemberReferences(oldName).then((nameMatches) {
-      List<SourceReference> nameRefs = getSourceReferences(nameMatches);
-      for (SourceReference reference in nameRefs) {
-        // ignore resolved reference, we have already updated it
-        if (reference.isResolved) {
+    List<SearchMatch> nameMatches =
+        await searchEngine.searchMemberReferences(oldName);
+    List<SourceReference> nameRefs = getSourceReferences(nameMatches);
+    for (SourceReference reference in nameRefs) {
+      // ignore resolved reference, we have already updated it
+      if (reference.isResolved) {
+        continue;
+      }
+      // check the element being renamed is accessible
+      {
+        LibraryElement whereLibrary = reference.element.library;
+        if (!element.isAccessibleIn(whereLibrary)) {
           continue;
         }
-        // check the element being renamed is accessible
-        {
-          LibraryElement whereLibrary = reference.element.library;
-          if (!element.isAccessibleIn(whereLibrary)) {
-            continue;
-          }
-        }
-        // add edit
-        reference.addEdit(change, newName, id: _newPotentialId());
       }
-    });
+      // add edit
+      reference.addEdit(change, newName, id: _newPotentialId());
+    }
   }
 
   String _newPotentialId() {
@@ -149,7 +149,7 @@
         elementClass = element.enclosingElement,
         elementKind = element.kind;
 
-  Future<RefactoringStatus> validate() {
+  Future<RefactoringStatus> validate() async {
     RefactoringStatus result = new RefactoringStatus();
     // check if there is a member with "newName" in the same ClassElement
     for (Element newNameMember in getChildren(elementClass, name)) {
@@ -163,93 +163,85 @@
     }
     // do chained computations
     Set<ClassElement> superClasses = getSuperClasses(elementClass);
-    Set<ClassElement> subClasses;
-    return _prepareReferences().then((_) {
-      return getSubClasses(searchEngine, elementClass).then((_subs) {
-        subClasses = _subs;
-      });
-    }).then((_) {
-      // check shadowing in hierarchy
-      return searchEngine.searchElementDeclarations(name).then((decls) {
-        for (SearchMatch decl in decls) {
-          Element nameElement = getSyntheticAccessorVariable(decl.element);
-          Element nameClass = nameElement.enclosingElement;
-          // renamed Element shadows member of superclass
-          if (superClasses.contains(nameClass)) {
-            result.addError(
-                format(
-                    isRename ?
-                        "Renamed {0} will shadow {1} '{2}'." :
-                        "Created {0} will shadow {1} '{2}'.",
-                    elementKind.displayName,
-                    getElementKindName(nameElement),
-                    getElementQualifiedName(nameElement)),
-                newLocation_fromElement(nameElement));
-          }
-          // renamed Element is shadowed by member of subclass
-          if (isRename && subClasses.contains(nameClass)) {
-            result.addError(
-                format(
-                    "Renamed {0} will be shadowed by {1} '{2}'.",
-                    elementKind.displayName,
-                    getElementKindName(nameElement),
-                    getElementQualifiedName(nameElement)),
-                newLocation_fromElement(nameElement));
-          }
-          // renamed Element is shadowed by local
-          if (nameElement is LocalElement) {
-            LocalElement localElement = nameElement;
-            ClassElement enclosingClass =
-                nameElement.getAncestor((element) => element is ClassElement);
-            if (enclosingClass == elementClass ||
-                subClasses.contains(enclosingClass)) {
-              for (SearchMatch reference in references) {
-                if (isReferenceInLocalRange(localElement, reference)) {
-                  result.addError(
-                      format(
-                          "Usage of renamed {0} will be shadowed by {1} '{2}'.",
-                          elementKind.displayName,
-                          getElementKindName(localElement),
-                          localElement.displayName),
-                      newLocation_fromMatch(reference));
-                }
-              }
+    await _prepareReferences();
+    Set<ClassElement> subClasses =
+        await getSubClasses(searchEngine, elementClass);
+    // check shadowing in hierarchy
+    List<SearchMatch> declarations =
+        await searchEngine.searchElementDeclarations(name);
+    for (SearchMatch declaration in declarations) {
+      Element nameElement = getSyntheticAccessorVariable(declaration.element);
+      Element nameClass = nameElement.enclosingElement;
+      // renamed Element shadows member of superclass
+      if (superClasses.contains(nameClass)) {
+        result.addError(
+            format(
+                isRename ?
+                    "Renamed {0} will shadow {1} '{2}'." :
+                    "Created {0} will shadow {1} '{2}'.",
+                elementKind.displayName,
+                getElementKindName(nameElement),
+                getElementQualifiedName(nameElement)),
+            newLocation_fromElement(nameElement));
+      }
+      // renamed Element is shadowed by member of subclass
+      if (isRename && subClasses.contains(nameClass)) {
+        result.addError(
+            format(
+                "Renamed {0} will be shadowed by {1} '{2}'.",
+                elementKind.displayName,
+                getElementKindName(nameElement),
+                getElementQualifiedName(nameElement)),
+            newLocation_fromElement(nameElement));
+      }
+      // renamed Element is shadowed by local
+      if (nameElement is LocalElement) {
+        LocalElement localElement = nameElement;
+        ClassElement enclosingClass =
+            nameElement.getAncestor((element) => element is ClassElement);
+        if (enclosingClass == elementClass ||
+            subClasses.contains(enclosingClass)) {
+          for (SearchMatch reference in references) {
+            if (isReferenceInLocalRange(localElement, reference)) {
+              result.addError(
+                  format(
+                      "Usage of renamed {0} will be shadowed by {1} '{2}'.",
+                      elementKind.displayName,
+                      getElementKindName(localElement),
+                      localElement.displayName),
+                  newLocation_fromMatch(reference));
             }
           }
         }
-      });
-    }).then((_) => result);
+      }
+    }
+    // done
+    return result;
   }
 
   /**
    * Fills [elements] with [Element]s to rename.
    */
-  Future _prepareElements() {
+  Future _prepareElements() async {
     if (element is ClassMemberElement) {
-      return getHierarchyMembers(
-          searchEngine,
-          element).then((Set<Element> elements) {
-        this.elements = elements;
-      });
+      elements = await getHierarchyMembers(searchEngine, element);
     } else {
       elements = new Set.from([element]);
-      return new Future.value();
     }
   }
 
   /**
    * Fills [references] with all references to [elements].
    */
-  Future _prepareReferences() {
+  Future _prepareReferences() async {
     if (!isRename) {
       return new Future.value();
     }
-    return _prepareElements().then((_) {
-      return Future.forEach(elements, (Element element) {
-        return searchEngine.searchReferences(element).then((references) {
-          this.references.addAll(references);
-        });
-      });
+    await _prepareElements();
+    await Future.forEach(elements, (Element element) async {
+      List<SearchMatch> elementReferences =
+          await searchEngine.searchReferences(element);
+      references.addAll(elementReferences);
     });
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart
index caecadd..544b79a 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_constructor.dart
@@ -50,17 +50,16 @@
   }
 
   @override
-  Future fillChange() {
+  Future fillChange() async {
     String replacement = newName.isEmpty ? '' : '.${newName}';
     // update references
-    return searchEngine.searchReferences(element).then((matches) {
-      List<SourceReference> references = getSourceReferences(matches);
-      if (!element.isSynthetic) {
-        for (SourceReference reference in references) {
-          reference.addEdit(change, replacement);
-        }
+    List<SearchMatch> matches = await searchEngine.searchReferences(element);
+    List<SourceReference> references = getSourceReferences(matches);
+    if (!element.isSynthetic) {
+      for (SourceReference reference in references) {
+        reference.addEdit(change, replacement);
       }
-    });
+    }
   }
 
   void _analyzePossibleConflicts(RefactoringStatus result) {
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
index 67c3968..8b8dfbf 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_import.dart
@@ -47,7 +47,7 @@
   }
 
   @override
-  Future fillChange() {
+  Future fillChange() async {
     // update declaration
     {
       PrefixElement prefix = element.prefix;
@@ -73,15 +73,14 @@
       }
     }
     // update references
-    return searchEngine.searchReferences(element).then((refMatches) {
-      List<SourceReference> references = getSourceReferences(refMatches);
-      for (SourceReference reference in references) {
-        if (newName.isEmpty) {
-          reference.addEdit(change, newName);
-        } else {
-          reference.addEdit(change, "${newName}.");
-        }
+    List<SearchMatch> matches = await searchEngine.searchReferences(element);
+    List<SourceReference> references = getSourceReferences(matches);
+    for (SourceReference reference in references) {
+      if (newName.isEmpty) {
+        reference.addEdit(change, newName);
+      } else {
+        reference.addEdit(change, "${newName}.");
       }
-    });
+    }
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
index 9a9d65e..1880928 100644
--- a/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
+++ b/pkg/analysis_server/lib/src/services/refactoring/rename_unit_member.dart
@@ -140,16 +140,13 @@
     elementKind = element.kind;
   }
 
-  Future<RefactoringStatus> validate() {
+  Future<RefactoringStatus> validate() async {
     _validateWillConflict();
-    List<Future> futures = <Future>[];
     if (isRename) {
-      futures.add(_validateWillBeShadowed());
+      await _validateWillBeShadowed();
     }
-    futures.add(_validateWillShadow());
-    return Future.wait(futures).then((_) {
-      return result;
-    });
+    await _validateWillShadow();
+    return result;
   }
 
   /**
@@ -179,29 +176,27 @@
   /**
    * Validates if any usage of [element] renamed to [name] will be shadowed.
    */
-  Future _validateWillBeShadowed() {
+  Future _validateWillBeShadowed() async {
     if (!isRename) {
-      return new Future.value();
+      return;
     }
-    return searchEngine.searchReferences(element).then((references) {
-      for (SearchMatch reference in references) {
-        Element refElement = reference.element;
-        ClassElement refClass =
-            refElement.getAncestor((e) => e is ClassElement);
-        if (refClass != null) {
-          visitChildren(refClass, (shadow) {
-            if (hasDisplayName(shadow, name)) {
-              String message = format(
-                  "Reference to renamed {0} will be shadowed by {1} '{2}'.",
-                  getElementKindName(element),
-                  getElementKindName(shadow),
-                  getElementQualifiedName(shadow));
-              result.addError(message, newLocation_fromElement(shadow));
-            }
-          });
-        }
+    List<SearchMatch> references = await searchEngine.searchReferences(element);
+    for (SearchMatch reference in references) {
+      Element refElement = reference.element;
+      ClassElement refClass = refElement.getAncestor((e) => e is ClassElement);
+      if (refClass != null) {
+        visitChildren(refClass, (shadow) {
+          if (hasDisplayName(shadow, name)) {
+            String message = format(
+                "Reference to renamed {0} will be shadowed by {1} '{2}'.",
+                getElementKindName(element),
+                getElementKindName(shadow),
+                getElementQualifiedName(shadow));
+            result.addError(message, newLocation_fromElement(shadow));
+          }
+        });
       }
-    });
+    }
   }
 
   /**
@@ -223,40 +218,40 @@
   /**
    * Validates if renamed [element] will shadow any [Element] named [name].
    */
-  Future _validateWillShadow() {
-    return searchEngine.searchMemberDeclarations(name).then((declarations) {
-      return Future.forEach(declarations, (SearchMatch declaration) {
-        Element member = declaration.element;
-        ClassElement declaringClass = member.enclosingElement;
-        return searchEngine.searchReferences(member).then((memberReferences) {
-          for (SearchMatch memberReference in memberReferences) {
-            Element refElement = memberReference.element;
-            // cannot be shadowed if qualified
-            if (memberReference.isQualified) {
-              continue;
-            }
-            // cannot be shadowed if declared in the same class as reference
-            ClassElement refClass =
-                refElement.getAncestor((e) => e is ClassElement);
-            if (refClass == declaringClass) {
-              continue;
-            }
-            // ignore if not visible
-            if (!_isVisibleAt(element, memberReference)) {
-              continue;
-            }
-            // OK, reference will be shadowed be the element being renamed
-            String message = format(
-                isRename ?
-                    "Renamed {0} will shadow {1} '{2}'." :
-                    "Created {0} will shadow {1} '{2}'.",
-                elementKind.displayName,
-                getElementKindName(member),
-                getElementQualifiedName(member));
-            result.addError(message, newLocation_fromMatch(memberReference));
-          }
-        });
-      });
-    });
+  Future _validateWillShadow() async {
+    List<SearchMatch> declarations =
+        await searchEngine.searchMemberDeclarations(name);
+    for (SearchMatch declaration in declarations) {
+      Element member = declaration.element;
+      ClassElement declaringClass = member.enclosingElement;
+      List<SearchMatch> memberReferences =
+          await searchEngine.searchReferences(member);
+      for (SearchMatch memberReference in memberReferences) {
+        Element refElement = memberReference.element;
+        // cannot be shadowed if qualified
+        if (memberReference.isQualified) {
+          continue;
+        }
+        // cannot be shadowed if declared in the same class as reference
+        ClassElement refClass =
+            refElement.getAncestor((e) => e is ClassElement);
+        if (refClass == declaringClass) {
+          continue;
+        }
+        // ignore if not visible
+        if (!_isVisibleAt(element, memberReference)) {
+          continue;
+        }
+        // OK, reference will be shadowed be the element being renamed
+        String message = format(
+            isRename ?
+                "Renamed {0} will shadow {1} '{2}'." :
+                "Created {0} will shadow {1} '{2}'.",
+            elementKind.displayName,
+            getElementKindName(member),
+            getElementQualifiedName(member));
+        result.addError(message, newLocation_fromMatch(memberReference));
+      }
+    }
   }
 }
diff --git a/pkg/analysis_server/lib/src/status/ast_writer.dart b/pkg/analysis_server/lib/src/status/ast_writer.dart
index 97e9fa4..99229a0 100644
--- a/pkg/analysis_server/lib/src/status/ast_writer.dart
+++ b/pkg/analysis_server/lib/src/status/ast_writer.dart
@@ -6,8 +6,11 @@
 
 import 'dart:convert';
 
+import 'package:analysis_server/src/get_handler.dart';
 import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
 import 'package:analyzer/src/generated/java_engine.dart';
+import 'package:analyzer/src/generated/source.dart';
 
 /**
  * A visitor that will produce an HTML representation of an AST structure.
@@ -140,6 +143,7 @@
       _writeProperty("element", node.element);
     } else if (node is ExportDirective) {
       _writeProperty("element", node.element);
+      _writeProperty("source", node.source);
     } else if (node is FunctionExpressionInvocation) {
       _writeProperty('static element', node.staticElement);
       _writeProperty('static type', node.staticType);
@@ -147,10 +151,12 @@
       _writeProperty('propagated type', node.propagatedType);
     } else if (node is ImportDirective) {
       _writeProperty("element", node.element);
+      _writeProperty("source", node.source);
     } else if (node is LibraryDirective) {
       _writeProperty("element", node.element);
     } else if (node is PartDirective) {
       _writeProperty("element", node.element);
+      _writeProperty("source", node.source);
     } else if (node is PartOfDirective) {
       _writeProperty("element", node.element);
     } else if (node is PostfixExpression) {
@@ -183,7 +189,11 @@
     if (value != null) {
       String valueString = null;
       try {
-        valueString = value.toString();
+        if (value is Source) {
+          valueString = 'Source (uri="${value.uri}", path="${value.fullName}")';
+        } else {
+          valueString = value.toString();
+        }
       } catch (exception, stackTrace) {
         exceptions.add(new CaughtException(exception, stackTrace));
       }
@@ -195,6 +205,16 @@
         buffer.write('</span>');
       } else {
         buffer.write(HTML_ESCAPE.convert(valueString));
+        if (value is Element && value is! LibraryElement) {
+          String name = value.name;
+          if (name != null) {
+            buffer.write('&nbsp;&nbsp;[');
+            buffer.write(GetHandler.makeLink(GetHandler.INDEX_ELEMENT_BY_NAME, {
+              'name': name
+            }, 'search index'));
+            buffer.write(']');
+          }
+        }
       }
       buffer.write('<br>');
     }
diff --git a/pkg/analysis_server/lib/src/status/element_writer.dart b/pkg/analysis_server/lib/src/status/element_writer.dart
index be7e59c..6625a43 100644
--- a/pkg/analysis_server/lib/src/status/element_writer.dart
+++ b/pkg/analysis_server/lib/src/status/element_writer.dart
@@ -6,6 +6,7 @@
 
 import 'dart:convert';
 
+import 'package:analysis_server/src/get_handler.dart';
 import 'package:analyzer/src/generated/element.dart';
 
 /**
@@ -42,7 +43,18 @@
     }
     buffer.write(' <span style="color:gray">(');
     buffer.write(element.runtimeType);
-    buffer.write(')</span><br>');
+    buffer.write(')</span>');
+    if (element is! LibraryElement) {
+      String name = element.name;
+      if (name != null) {
+        buffer.write('&nbsp;&nbsp;[');
+        buffer.write(GetHandler.makeLink(GetHandler.INDEX_ELEMENT_BY_NAME, {
+          'name': name
+        }, 'search index'));
+        buffer.write(']');
+      }
+    }
+    buffer.write('<br>');
     indentLevel++;
     try {
       element.visitChildren(this);
diff --git a/pkg/analysis_server/test/analysis/update_content_test.dart b/pkg/analysis_server/test/analysis/update_content_test.dart
index f4d5bca..5b780a7 100644
--- a/pkg/analysis_server/test/analysis/update_content_test.dart
+++ b/pkg/analysis_server/test/analysis/update_content_test.dart
@@ -21,6 +21,8 @@
 @reflectiveTest
 class UpdateContentTest extends AbstractAnalysisTest {
   Map<String, List<AnalysisError>> filesErrors = {};
+  int serverErrorCount = 0;
+  int navigationCount = 0;
 
   @override
   void processNotification(Notification notification) {
@@ -28,6 +30,37 @@
       var decoded = new AnalysisErrorsParams.fromNotification(notification);
       filesErrors[decoded.file] = decoded.errors;
     }
+    if (notification.event == ANALYSIS_NAVIGATION) {
+      navigationCount++;
+    }
+    if (notification.event == SERVER_ERROR) {
+      serverErrorCount++;
+    }
+  }
+
+  test_discardNotifications_onSourceChange() async {
+    createProject();
+    addTestFile('');
+    await server.onAnalysisComplete;
+    server.setAnalysisSubscriptions({
+      AnalysisService.NAVIGATION: [testFile].toSet()
+    });
+    // update file, analyze, but don't sent notifications
+    navigationCount = 0;
+    server.updateContent('1', {
+      testFile: new AddContentOverlay('foo() {}')
+    });
+    server.test_performAllAnalysisOperations();
+    expect(serverErrorCount, 0);
+    expect(navigationCount, 0);
+    // replace the file contents,
+    // should discard any pending notification operations
+    server.updateContent('2', {
+      testFile: new AddContentOverlay('bar() {}')
+    });
+    await server.onAnalysisComplete;
+    expect(serverErrorCount, 0);
+    expect(navigationCount, 1);
   }
 
   test_illegal_ChangeContentOverlay() {
diff --git a/pkg/analysis_server/test/analysis_server_test.dart b/pkg/analysis_server/test/analysis_server_test.dart
index 90ab3ca..2a8a68c 100644
--- a/pkg/analysis_server/test/analysis_server_test.dart
+++ b/pkg/analysis_server/test/analysis_server_test.dart
@@ -136,6 +136,34 @@
     });
   }
 
+  test_getAnalysisContext_nested() {
+    String dir1Path = '/dir1';
+    String dir2Path = dir1Path + '/dir2';
+    String filePath = dir2Path + '/file.dart';
+    Folder dir1 = resourceProvider.newFolder(dir1Path);
+    Folder dir2 = resourceProvider.newFolder(dir2Path);
+    resourceProvider.newFile(filePath, 'library lib;');
+
+    AnalysisContext context1 = AnalysisEngine.instance.createAnalysisContext();
+    AnalysisContext context2 = AnalysisEngine.instance.createAnalysisContext();
+    server.folderMap[dir1] = context1;
+    server.folderMap[dir2] = context2;
+
+    expect(server.getAnalysisContext(filePath), context2);
+  }
+
+  test_getAnalysisContext_simple() {
+    String dirPath = '/dir';
+    String filePath = dirPath + '/file.dart';
+    Folder dir = resourceProvider.newFolder(dirPath);
+    resourceProvider.newFile(filePath, 'library lib;');
+
+    AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
+    server.folderMap[dir] = context;
+
+    expect(server.getAnalysisContext(filePath), context);
+  }
+
   Future test_contextsChangedEvent() {
     resourceProvider.newFolder('/foo');
 
diff --git a/pkg/analysis_server/test/domain_completion_test.dart b/pkg/analysis_server/test/domain_completion_test.dart
index 08fa7e6..21307d5 100644
--- a/pkg/analysis_server/test/domain_completion_test.dart
+++ b/pkg/analysis_server/test/domain_completion_test.dart
@@ -364,46 +364,6 @@
     });
   }
 
-  test_partFile() {
-    addFile('/project/bin/testA.dart', '''
-      library libA;
-      part "$testFile";
-      import 'dart:html';
-      class A { }
-    ''');
-    addTestFile('''
-      part of libA;
-      main() {^}''');
-    return getSuggestions().then((_) {
-      expect(replacementOffset, equals(completionOffset));
-      expect(replacementLength, equals(0));
-      assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object');
-      assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement');
-      assertHasResult(CompletionSuggestionKind.INVOCATION, 'A');
-      assertNoResult('test');
-    });
-  }
-
-  test_partFile2() {
-    addFile('/testA.dart', '''
-      part of libA;
-      class A { }''');
-    addTestFile('''
-      library libA;
-      part "/testA.dart";
-      import 'dart:html';
-      main() {^}
-    ''');
-    return getSuggestions().then((_) {
-      expect(replacementOffset, equals(completionOffset));
-      expect(replacementLength, equals(0));
-      assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object');
-      assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement');
-      assertHasResult(CompletionSuggestionKind.INVOCATION, 'A');
-      assertNoResult('test');
-    });
-  }
-
   test_imports_prefixed() {
     addTestFile('''
       import 'dart:html' as foo;
@@ -484,6 +444,61 @@
     });
   }
 
+  test_partFile() {
+    addFile('/project/bin/testA.dart', '''
+      library libA;
+      part "$testFile";
+      import 'dart:html';
+      class A { }
+    ''');
+    addTestFile('''
+      part of libA;
+      main() {^}''');
+    return getSuggestions().then((_) {
+      expect(replacementOffset, equals(completionOffset));
+      expect(replacementLength, equals(0));
+      assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object');
+      assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement');
+      assertHasResult(CompletionSuggestionKind.INVOCATION, 'A');
+      assertNoResult('test');
+    });
+  }
+
+  test_partFile2() {
+    addFile('/testA.dart', '''
+      part of libA;
+      class A { }''');
+    addTestFile('''
+      library libA;
+      part "/testA.dart";
+      import 'dart:html';
+      main() {^}
+    ''');
+    return getSuggestions().then((_) {
+      expect(replacementOffset, equals(completionOffset));
+      expect(replacementLength, equals(0));
+      assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object');
+      assertHasResult(CompletionSuggestionKind.INVOCATION, 'HtmlElement');
+      assertHasResult(CompletionSuggestionKind.INVOCATION, 'A');
+      assertNoResult('test');
+    });
+  }
+
+  test_simple() {
+    addTestFile('''
+      void main() {
+        ^
+      }
+    ''');
+    return getSuggestions().then((_) {
+      expect(replacementOffset, equals(completionOffset));
+      expect(replacementLength, equals(0));
+      assertHasResult(CompletionSuggestionKind.INVOCATION, 'Object');
+      assertNoResult('HtmlElement');
+      assertNoResult('test');
+    });
+  }
+
   test_topLevel() {
     addTestFile('''
       typedef foo();
diff --git a/pkg/analysis_server/test/domain_execution_test.dart b/pkg/analysis_server/test/domain_execution_test.dart
index a77ccf8..60c2454 100644
--- a/pkg/analysis_server/test/domain_execution_test.dart
+++ b/pkg/analysis_server/test/domain_execution_test.dart
@@ -10,10 +10,12 @@
 import 'package:analysis_server/src/constants.dart';
 import 'package:analysis_server/src/domain_execution.dart';
 import 'package:analysis_server/src/protocol.dart';
+import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/file_system/memory_file_system.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:analyzer/src/generated/engine.dart';
 import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/source_io.dart';
 import 'package:typed_mock/typed_mock.dart';
 import 'package:unittest/unittest.dart';
 
@@ -84,64 +86,103 @@
       });
     });
 
-//    group('mapUri', () {
-//      String contextId;
-//
-//      setUp(() {
-//        Request request =
-//            new ExecutionCreateContextParams('/a/b.dart').toRequest('0');
-//        Response response = handler.handleRequest(request);
-//        expect(response, isResponseSuccess('0'));
-//        ExecutionCreateContextResult result =
-//            new ExecutionCreateContextResult.fromResponse(response);
-//        contextId = result.id;
-//      });
-//
-//      tearDown(() {
-//        Request request =
-//            new ExecutionDeleteContextParams(contextId).toRequest('1');
-//        Response response = handler.handleRequest(request);
-//        expect(response, isResponseSuccess('1'));
-//      });
-//
-//      test('file to URI', () {
-//        Request request =
-//            new ExecutionMapUriParams(contextId, file: '/a/b.dart').toRequest('2');
-//        Response response = handler.handleRequest(request);
-//        expect(response, isResponseSuccess('2'));
-//        ExecutionMapUriResult result =
-//            new ExecutionMapUriResult.fromResponse(response);
-//        expect(result.file, isNull);
-//        expect(result.uri, isNotNull);
-//        // TODO(brianwilkerson) Test for the correct result.
-//      });
-//
-//      test('URI to file', () {
-//        Request request =
-//            new ExecutionMapUriParams(contextId, uri: '/a/b.dart').toRequest('3');
-//        Response response = handler.handleRequest(request);
-//        expect(response, isResponseSuccess('3'));
-//        ExecutionMapUriResult result =
-//            new ExecutionMapUriResult.fromResponse(response);
-//        expect(result.file, isNotNull);
-//        expect(result.uri, isNull);
-//        // TODO(brianwilkerson) Test for the correct result.
-//      });
-//
-//      test('invalid context id', () {
-//        Request request =
-//            new ExecutionMapUriParams('xxx', uri: '').toRequest('4');
-//        Response response = handler.handleRequest(request);
-//        expect(response, isResponseFailure('4'));
-//      });
-//
-//      test('both file and uri', () {
-//        Request request =
-//            new ExecutionMapUriParams('xxx', file: '', uri: '').toRequest('5');
-//        Response response = handler.handleRequest(request);
-//        expect(response, isResponseFailure('5'));
-//      });
-//    });
+    group('mapUri', () {
+      String contextId;
+
+      setUp(() {
+        Folder folder = provider.newFile('/a/b.dart', '').parent;
+        server.folderMap.putIfAbsent(folder, () {
+          SourceFactory factory =
+              new SourceFactory([new ResourceUriResolver(provider)]);
+          AnalysisContext context =
+              AnalysisEngine.instance.createAnalysisContext();
+          context.sourceFactory = factory;
+          return context;
+        });
+        Request request =
+            new ExecutionCreateContextParams('/a/b.dart').toRequest('0');
+        Response response = handler.handleRequest(request);
+        expect(response, isResponseSuccess('0'));
+        ExecutionCreateContextResult result =
+            new ExecutionCreateContextResult.fromResponse(response);
+        contextId = result.id;
+      });
+
+      tearDown(() {
+        Request request =
+            new ExecutionDeleteContextParams(contextId).toRequest('1');
+        Response response = handler.handleRequest(request);
+        expect(response, isResponseSuccess('1'));
+      });
+
+      group('file to URI', () {
+        test('does not exist', () {
+          Request request =
+              new ExecutionMapUriParams(contextId, file: '/a/c.dart').toRequest('2');
+          Response response = handler.handleRequest(request);
+          expect(response, isResponseFailure('2'));
+        });
+
+        test('directory', () {
+          provider.newFolder('/a/d');
+          Request request =
+              new ExecutionMapUriParams(contextId, file: '/a/d').toRequest('2');
+          Response response = handler.handleRequest(request);
+          expect(response, isResponseFailure('2'));
+        });
+
+        test('valid', () {
+          Request request =
+              new ExecutionMapUriParams(contextId, file: '/a/b.dart').toRequest('2');
+          Response response = handler.handleRequest(request);
+          expect(response, isResponseSuccess('2'));
+          ExecutionMapUriResult result =
+              new ExecutionMapUriResult.fromResponse(response);
+          expect(result.file, isNull);
+          expect(result.uri, 'file:///a/b.dart');
+        });
+      });
+
+      group('URI to file', () {
+        test('invalid', () {
+          Request request =
+              new ExecutionMapUriParams(contextId, uri: 'foo:///a/b.dart').toRequest('2');
+          Response response = handler.handleRequest(request);
+          expect(response, isResponseFailure('2'));
+        });
+
+        test('valid', () {
+          Request request =
+              new ExecutionMapUriParams(contextId, uri: 'file:///a/b.dart').toRequest('2');
+          Response response = handler.handleRequest(request);
+          expect(response, isResponseSuccess('2'));
+          ExecutionMapUriResult result =
+              new ExecutionMapUriResult.fromResponse(response);
+          expect(result.file, '/a/b.dart');
+          expect(result.uri, isNull);
+        });
+      });
+
+      test('invalid context id', () {
+        Request request =
+            new ExecutionMapUriParams('xxx', uri: '').toRequest('4');
+        Response response = handler.handleRequest(request);
+        expect(response, isResponseFailure('4'));
+      });
+
+      test('both file and uri', () {
+        Request request =
+            new ExecutionMapUriParams('xxx', file: '', uri: '').toRequest('5');
+        Response response = handler.handleRequest(request);
+        expect(response, isResponseFailure('5'));
+      });
+
+      test('neither file nor uri', () {
+        Request request = new ExecutionMapUriParams('xxx').toRequest('6');
+        Response response = handler.handleRequest(request);
+        expect(response, isResponseFailure('6'));
+      });
+    });
 
     group('setSubscriptions', () {
       test('failure - invalid service name', () {
diff --git a/pkg/analysis_server/test/domain_server_test.dart b/pkg/analysis_server/test/domain_server_test.dart
index 2b3cad1..dc76e84 100644
--- a/pkg/analysis_server/test/domain_server_test.dart
+++ b/pkg/analysis_server/test/domain_server_test.dart
@@ -40,7 +40,7 @@
       expect(response.toJson(), equals({
         Response.ID: '0',
         Response.RESULT: {
-          VERSION: '1.0.0'
+          VERSION: AnalysisServer.VERSION
         }
       }));
     });
diff --git a/pkg/analysis_server/test/integration/integration_test_methods.dart b/pkg/analysis_server/test/integration/integration_test_methods.dart
index 4850c83..f49ae4a 100644
--- a/pkg/analysis_server/test/integration/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/integration_test_methods.dart
@@ -314,13 +314,26 @@
   }
 
   /**
-   * Force the re-analysis of everything contained in the existing analysis
+   * Force the re-analysis of everything contained in the specified analysis
    * roots. This will cause all previously computed analysis results to be
    * discarded and recomputed, and will cause all subscribed notifications to
    * be re-sent.
+   *
+   * If no analysis roots are provided, then all current analysis roots will be
+   * re-analyzed. If an empty list of analysis roots is provided, then nothing
+   * will be re-analyzed. If the list contains one or more paths that are not
+   * currently analysis roots, then an error of type INVALID_ANALYSIS_ROOT will
+   * be generated.
+   *
+   * Parameters
+   *
+   * roots ( optional List<FilePath> )
+   *
+   *   A list of the analysis roots that are to be re-analyzed.
    */
-  Future sendAnalysisReanalyze() {
-    return server.send("analysis.reanalyze", null)
+  Future sendAnalysisReanalyze({List<String> roots}) {
+    var params = new AnalysisReanalyzeParams(roots: roots).toJson();
+    return server.send("analysis.reanalyze", params)
         .then((result) {
       expect(result, isNull);
       return null;
@@ -1047,7 +1060,9 @@
    * text is passed in so that the selection can be preserved across the
    * formatting operation. The updated selection will be as close to matching
    * the original as possible, but whitespace at the beginning or end of the
-   * selected region will be ignored.
+   * selected region will be ignored. If preserving selection information is
+   * not required, zero (0) can be specified for both the selection offset and
+   * selection length.
    *
    * If a request is made for a file which does not exist, or which is not
    * currently subject to analysis (e.g. because it is not associated with any
@@ -1062,15 +1077,11 @@
    *
    * selectionOffset ( int )
    *
-   *   The offset of the current selection in the file. In case preserving,
-   *   selection information is not required, 0 can be specified for both
-   *   selection offset and length.
+   *   The offset of the current selection in the file.
    *
    * selectionLength ( int )
    *
-   *   The length of the current selection in the file. In case preserving,
-   *   selection information is not required, 0 can be specified for both
-   *   selection offset and length.
+   *   The length of the current selection in the file.
    *
    * Returns
    *
@@ -1325,7 +1336,8 @@
    *
    * contextRoot ( FilePath )
    *
-   *   The path of the Dart or HTML file that will be launched.
+   *   The path of the Dart or HTML file that will be launched, or the path of
+   *   the directory containing the file.
    *
    * Returns
    *
@@ -1366,22 +1378,22 @@
    * Map a URI from the execution context to the file that it corresponds to,
    * or map a file to the URI that it corresponds to in the execution context.
    *
-   * Exactly one of the file and uri fields must be provided.
+   * Exactly one of the file and uri fields must be provided. If both fields
+   * are provided, then an error of type INVALID_PARAMETER will be generated.
+   * Similarly, if neither field is provided, then an error of type
+   * INVALID_PARAMETER will be generated.
    *
    * If the file field is provided and the value is not the path of a file
    * (either the file does not exist or the path references something other
-   * than a file), then an error of type MAP_URI_INVALID_FILE will be
-   * generated.
+   * than a file), then an error of type INVALID_PARAMETER will be generated.
    *
    * If the uri field is provided and the value is not a valid URI or if the
    * URI references something that is not a file (either a file that does not
    * exist or something other than a file), then an error of type
-   * MAP_URI_INVALID_URI will be generated.
+   * INVALID_PARAMETER will be generated.
    *
-   * If the contextRoot used to create the execution context is not a file
-   * (either the file does not exist or the path references something other
-   * than a file), then an error of type INVALID_EXECUTION_CONTEXT will be
-   * generated.
+   * If the contextRoot used to create the execution context does not exist,
+   * then an error of type INVALID_EXECUTION_CONTEXT will be generated.
    *
    * Parameters
    *
diff --git a/pkg/analysis_server/test/integration/protocol_matchers.dart b/pkg/analysis_server/test/integration/protocol_matchers.dart
index 3605eee..6072aec 100644
--- a/pkg/analysis_server/test/integration/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/protocol_matchers.dart
@@ -198,8 +198,15 @@
 
 /**
  * analysis.reanalyze params
+ *
+ * {
+ *   "roots": optional List<FilePath>
+ * }
  */
-final Matcher isAnalysisReanalyzeParams = isNull;
+final Matcher isAnalysisReanalyzeParams = new LazyMatcher(() => new MatchesJsonObject(
+  "analysis.reanalyze params", null, optionalFields: {
+    "roots": isListOf(isFilePath)
+  }));
 
 /**
  * analysis.reanalyze result
@@ -1799,6 +1806,7 @@
  *   CONTENT_MODIFIED
  *   FORMAT_INVALID_FILE
  *   GET_ERRORS_INVALID_FILE
+ *   INVALID_EXECUTION_CONTEXT
  *   INVALID_OVERLAY_CHANGE
  *   INVALID_PARAMETER
  *   INVALID_REQUEST
@@ -1816,6 +1824,7 @@
   "CONTENT_MODIFIED",
   "FORMAT_INVALID_FILE",
   "GET_ERRORS_INVALID_FILE",
+  "INVALID_EXECUTION_CONTEXT",
   "INVALID_OVERLAY_CHANGE",
   "INVALID_PARAMETER",
   "INVALID_REQUEST",
diff --git a/pkg/analysis_server/test/operation/operation_queue_test.dart b/pkg/analysis_server/test/operation/operation_queue_test.dart
index 0a4f13e..2fd7100 100644
--- a/pkg/analysis_server/test/operation/operation_queue_test.dart
+++ b/pkg/analysis_server/test/operation/operation_queue_test.dart
@@ -9,108 +9,17 @@
 import 'package:analysis_server/src/operation/operation_analysis.dart';
 import 'package:analysis_server/src/operation/operation_queue.dart';
 import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/source.dart';
 import 'package:typed_mock/typed_mock.dart';
 import 'package:unittest/unittest.dart';
+
 import '../mocks.dart';
-import 'package:analyzer/src/generated/source.dart';
+import '../reflective_tests.dart';
+
 
 main() {
   groupSep = ' | ';
-
-  group('ServerOperationQueue', () {
-    ServerOperationQueue queue;
-
-    setUp(() {
-      queue = new ServerOperationQueue();
-    });
-
-    test('clear', () {
-      var operationA = mockOperation(ServerOperationPriority.ANALYSIS);
-      var operationB = mockOperation(ServerOperationPriority.ANALYSIS_CONTINUE);
-      queue.add(operationA);
-      queue.add(operationB);
-      // there are some operations
-      expect(queue.isEmpty, false);
-      // clear - no operations
-      queue.clear();
-      expect(queue.isEmpty, true);
-    });
-
-    group('isEmpty', () {
-      test('true', () {
-        expect(queue.isEmpty, isTrue);
-      });
-
-      test('false', () {
-        var operation = mockOperation(ServerOperationPriority.ANALYSIS);
-        queue.add(operation);
-        expect(queue.isEmpty, isFalse);
-      });
-    });
-
-    group('take', () {
-      test('empty', () {
-        expect(queue.take(), isNull);
-      });
-
-      test('use operation priorities', () {
-        var operationA = mockOperation(ServerOperationPriority.ANALYSIS);
-        var operationB =
-            mockOperation(ServerOperationPriority.ANALYSIS_CONTINUE);
-        var operationC =
-            mockOperation(ServerOperationPriority.PRIORITY_ANALYSIS);
-        queue.add(operationA);
-        queue.add(operationB);
-        queue.add(operationC);
-        expect(queue.take(), operationC);
-        expect(queue.take(), operationB);
-        expect(queue.take(), operationA);
-        expect(queue.take(), isNull);
-      });
-
-      test('continue analysis first', () {
-        var analysisContext = new AnalysisContextMock();
-        var operationA = new PerformAnalysisOperation(analysisContext, false);
-        var operationB = new PerformAnalysisOperation(analysisContext, true);
-        queue.add(operationA);
-        queue.add(operationB);
-        expect(queue.take(), operationB);
-        expect(queue.take(), operationA);
-        expect(queue.take(), isNull);
-      });
-
-      test('priority context first', () {
-        var prioritySource = new MockSource();
-        var analysisContextA = new AnalysisContextMock();
-        var analysisContextB = new AnalysisContextMock();
-        analysisContextB.prioritySources = [prioritySource];
-        var operationA = new PerformAnalysisOperation(analysisContextA, false);
-        var operationB = new PerformAnalysisOperation(analysisContextB, false);
-        queue.add(operationA);
-        queue.add(operationB);
-        expect(queue.take(), operationB);
-        expect(queue.take(), operationA);
-        expect(queue.take(), isNull);
-      });
-
-      test('reschedule', () {
-        var prioritySource = new MockSource();
-        var analysisContextA = new AnalysisContextMock();
-        var analysisContextB = new AnalysisContextMock();
-        var operationA = new PerformAnalysisOperation(analysisContextA, false);
-        var operationB = new PerformAnalysisOperation(analysisContextB, false);
-        queue.add(operationA);
-        queue.add(operationB);
-        // update priority sources and reschedule
-        analysisContextB.prioritySources = [prioritySource];
-        queue.reschedule();
-        // verify order
-        expect(queue.take(), operationB);
-        expect(queue.take(), operationA);
-        expect(queue.take(), isNull);
-      });
-    });
-  });
+  runReflectiveTests(ServerOperationQueueTest);
 }
 
 
@@ -118,7 +27,7 @@
  *  Return a [ServerOperation] mock with the given priority.
  */
 ServerOperation mockOperation(ServerOperationPriority priority) {
-  ServerOperation operation = new ServerOperationMock();
+  ServerOperation operation = new _ServerOperationMock();
   when(operation.priority).thenReturn(priority);
   return operation;
 }
@@ -130,15 +39,144 @@
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
 
+
 class AnalysisServerMock extends TypedMock implements AnalysisServer {
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
 
+
 class ServerContextManagerMock extends TypedMock implements ServerContextManager
     {
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
 
-class ServerOperationMock extends TypedMock implements ServerOperation {
+
+@reflectiveTest
+class ServerOperationQueueTest {
+  ServerOperationQueue queue = new ServerOperationQueue();
+
+  void test_clear() {
+    var operationA = mockOperation(ServerOperationPriority.ANALYSIS);
+    var operationB = mockOperation(ServerOperationPriority.ANALYSIS_CONTINUE);
+    queue.add(operationA);
+    queue.add(operationB);
+    // there are some operations
+    expect(queue.isEmpty, false);
+    // clear - no operations
+    queue.clear();
+    expect(queue.isEmpty, true);
+  }
+
+  void test_isEmpty_false() {
+    var operation = mockOperation(ServerOperationPriority.ANALYSIS);
+    queue.add(operation);
+    expect(queue.isEmpty, isFalse);
+  }
+
+  void test_isEmpty_true() {
+    expect(queue.isEmpty, isTrue);
+  }
+
+  void test_reschedule() {
+    var prioritySource = new MockSource();
+    var analysisContextA = new AnalysisContextMock();
+    var analysisContextB = new AnalysisContextMock();
+    var operationA = new PerformAnalysisOperation(analysisContextA, false);
+    var operationB = new PerformAnalysisOperation(analysisContextB, false);
+    queue.add(operationA);
+    queue.add(operationB);
+    // update priority sources and reschedule
+    analysisContextB.prioritySources = [prioritySource];
+    queue.reschedule();
+    // verify order
+    expect(queue.take(), operationB);
+    expect(queue.take(), operationA);
+    expect(queue.take(), isNull);
+  }
+
+  void test_sourceAboutToChange() {
+    Source sourceA = new _SourceMock();
+    Source sourceB = new _SourceMock();
+    var opA1 = new _SourceSensitiveOperationMock(sourceA);
+    var opA2 = new _SourceSensitiveOperationMock(sourceA);
+    var opB1 = new _SourceSensitiveOperationMock(sourceB);
+    var opB2 = new _SourceSensitiveOperationMock(sourceB);
+    queue.add(opA1);
+    queue.add(opB1);
+    queue.add(opA2);
+    queue.add(opB2);
+    queue.sourceAboutToChange(sourceA);
+    expect(queue.take(), same(opB1));
+    expect(queue.take(), same(opB2));
+  }
+
+  void test_take_continueAnalysisFirst() {
+    var analysisContext = new AnalysisContextMock();
+    var operationA = new PerformAnalysisOperation(analysisContext, false);
+    var operationB = new PerformAnalysisOperation(analysisContext, true);
+    queue.add(operationA);
+    queue.add(operationB);
+    expect(queue.take(), operationB);
+    expect(queue.take(), operationA);
+    expect(queue.take(), isNull);
+  }
+
+  void test_take_empty() {
+    expect(queue.take(), isNull);
+  }
+
+  void test_take_priorityContextFirst() {
+    var prioritySource = new MockSource();
+    var analysisContextA = new AnalysisContextMock();
+    var analysisContextB = new AnalysisContextMock();
+    analysisContextB.prioritySources = [prioritySource];
+    var operationA = new PerformAnalysisOperation(analysisContextA, false);
+    var operationB = new PerformAnalysisOperation(analysisContextB, false);
+    queue.add(operationA);
+    queue.add(operationB);
+    expect(queue.take(), operationB);
+    expect(queue.take(), operationA);
+    expect(queue.take(), isNull);
+  }
+
+  void test_take_useOperationPriorities() {
+    var operationA = mockOperation(ServerOperationPriority.ANALYSIS);
+    var operationB = mockOperation(ServerOperationPriority.ANALYSIS_CONTINUE);
+    var operationC = mockOperation(ServerOperationPriority.PRIORITY_ANALYSIS);
+    queue.add(operationA);
+    queue.add(operationB);
+    queue.add(operationC);
+    expect(queue.take(), operationC);
+    expect(queue.take(), operationB);
+    expect(queue.take(), operationA);
+    expect(queue.take(), isNull);
+  }
+}
+
+
+class _ServerOperationMock extends TypedMock implements ServerOperation {
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
+
+class _SourceMock extends TypedMock implements Source {
+  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+}
+
+class _SourceSensitiveOperationMock extends TypedMock implements
+    SourceSensitiveOperation {
+  final Source source;
+
+  _SourceSensitiveOperationMock(this.source);
+
+  @override
+  ServerOperationPriority get priority {
+    return ServerOperationPriority.ANALYSIS_NOTIFICATION;
+  }
+
+  noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
+
+  @override
+  bool shouldBeDiscardedOnSourceChange(Source source) {
+    return source == this.source;
+  }
+}
diff --git a/pkg/analysis_server/test/services/correction/assist_test.dart b/pkg/analysis_server/test/services/correction/assist_test.dart
index 1b252f3..5db7631 100644
--- a/pkg/analysis_server/test/services/correction/assist_test.dart
+++ b/pkg/analysis_server/test/services/correction/assist_test.dart
@@ -531,6 +531,41 @@
     assertNoAssistAt('var ', AssistKind.ADD_TYPE_ANNOTATION);
   }
 
+  void test_addTypeAnnotation_parameter_BAD_hasExplicitType() {
+    _indexTestUnit('''
+foo(f(int p)) {}
+main() {
+  foo((num test) {});
+}
+''');
+    assertNoAssistAt('test', AssistKind.ADD_TYPE_ANNOTATION);
+  }
+
+  void test_addTypeAnnotation_parameter_BAD_noPropagatedType() {
+    _indexTestUnit('''
+foo(f(p)) {}
+main() {
+  foo((test) {});
+}
+''');
+    assertNoAssistAt('test', AssistKind.ADD_TYPE_ANNOTATION);
+  }
+
+  void test_addTypeAnnotation_parameter_OK() {
+    _indexTestUnit('''
+foo(f(int p)) {}
+main() {
+  foo((test) {});
+}
+''');
+    assertHasAssistAt('test', AssistKind.ADD_TYPE_ANNOTATION, '''
+foo(f(int p)) {}
+main() {
+  foo((int test) {});
+}
+''');
+  }
+
   void test_addTypeAnnotation_topLevelField_OK_int() {
     _indexTestUnit('''
 var V = 0;
diff --git a/pkg/analysis_server/test/services/correction/fix_test.dart b/pkg/analysis_server/test/services/correction/fix_test.dart
index 3ca13a6..9e33386 100644
--- a/pkg/analysis_server/test/services/correction/fix_test.dart
+++ b/pkg/analysis_server/test/services/correction/fix_test.dart
@@ -12,6 +12,7 @@
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/source/package_map_resolver.dart';
 import 'package:analyzer/src/generated/error.dart';
+import 'package:analyzer/src/generated/parser.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:unittest/unittest.dart';
 
@@ -26,12 +27,15 @@
 }
 
 
+typedef bool AnalysisErrorFilter(AnalysisError error);
+
+
 @reflectiveTest
 class FixProcessorTest extends AbstractSingleUnitTest {
   Index index;
   SearchEngineImpl searchEngine;
 
-  bool checkHasSingleError = true;
+  AnalysisErrorFilter errorFilter = null;
 
   Fix fix;
   SourceChange change;
@@ -104,6 +108,59 @@
     verifyNoTestUnitErrors = false;
   }
 
+  void test_addSync_blockFunctionBody() {
+    _indexTestUnit('''
+foo() {}
+main() {
+  await foo();
+}
+''');
+    List<AnalysisError> errors = context.computeErrors(testSource);
+    expect(errors, hasLength(2));
+    // ParserError: Expected to find ';'
+    {
+      AnalysisError error = errors[0];
+      expect(error.message, "Expected to find ';'");
+      List<Fix> fixes = computeFixes(searchEngine, testUnit, error);
+      expect(fixes, isEmpty);
+    }
+    // Undefined name 'await'
+    {
+      AnalysisError error = errors[1];
+      expect(error.message, "Undefined name 'await'");
+      List<Fix> fixes = computeFixes(searchEngine, testUnit, error);
+      // has exactly one fix
+      expect(fixes, hasLength(1));
+      Fix fix = fixes[0];
+      expect(fix.kind, FixKind.ADD_ASYNC);
+      // apply to "file"
+      List<SourceFileEdit> fileEdits = fix.change.edits;
+      expect(fileEdits, hasLength(1));
+      resultCode = SourceEdit.applySequence(testCode, fileEdits[0].edits);
+      // verify
+      expect(resultCode, '''
+foo() {}
+main() async {
+  await foo();
+}
+''');
+    }
+  }
+
+  void test_addSync_expressionFunctionBody() {
+    errorFilter = (AnalysisError error) {
+      return error.errorCode == StaticWarningCode.UNDEFINED_IDENTIFIER;
+    };
+    _indexTestUnit('''
+foo() {}
+main() => await foo();
+''');
+    assertHasFix(FixKind.ADD_ASYNC, '''
+foo() {}
+main() async => await foo();
+''');
+  }
+
   void test_boolean() {
     _indexTestUnit('''
 main() {
@@ -395,6 +452,15 @@
     assertNoFix(FixKind.CREATE_CONSTRUCTOR_SUPER);
   }
 
+  void test_createField_BAD_inSDK() {
+    _indexTestUnit('''
+main(List p) {
+  p.foo = 1;
+}
+''');
+    assertNoFix(FixKind.CREATE_FIELD);
+  }
+
   void test_createField_getter_multiLevel() {
     _indexTestUnit('''
 class A {
@@ -694,6 +760,15 @@
     expect(fileEdit.edits[0].replacement, contains('library my.file;'));
   }
 
+  void test_createGetter_BAD_inSDK() {
+    _indexTestUnit('''
+main(List p) {
+  int v = p.foo;
+}
+''');
+    assertNoFix(FixKind.CREATE_GETTER);
+  }
+
   void test_createGetter_multiLevel() {
     _indexTestUnit('''
 class A {
@@ -836,7 +911,6 @@
   }
 }
 ''');
-    // TODO
     assertHasFix(FixKind.CREATE_GETTER, '''
 class A {
   get test => null;
@@ -848,6 +922,34 @@
 ''');
   }
 
+  void test_createLocalVariable_functionType_named() {
+    _indexTestUnit('''
+typedef MY_FUNCTION(int p);
+foo(MY_FUNCTION f) {}
+main() {
+  foo(bar);
+}
+''');
+    assertHasFix(FixKind.CREATE_LOCAL_VARIABLE, '''
+typedef MY_FUNCTION(int p);
+foo(MY_FUNCTION f) {}
+main() {
+  MY_FUNCTION bar;
+  foo(bar);
+}
+''');
+  }
+
+  void test_createLocalVariable_functionType_synthetic() {
+    _indexTestUnit('''
+foo(f(int p)) {}
+main() {
+  foo(bar);
+}
+''');
+    assertNoFix(FixKind.CREATE_LOCAL_VARIABLE);
+  }
+
   void test_createLocalVariable_read_typeAssignment() {
     _indexTestUnit('''
 main() {
@@ -1744,6 +1846,21 @@
 ''');
   }
 
+  void test_importLibrarySdk_withType_AsExpression() {
+    _indexTestUnit('''
+main(p) {
+  p as Future;
+}
+''');
+    assertHasFix(FixKind.IMPORT_LIBRARY_SDK, '''
+import 'dart:async';
+
+main(p) {
+  p as Future;
+}
+''');
+  }
+
   void test_importLibrarySdk_withType_invocationTarget() {
     _indexTestUnit('''
 main() {
@@ -1759,6 +1876,21 @@
 ''');
   }
 
+  void test_importLibrarySdk_withType_IsExpression() {
+    _indexTestUnit('''
+main(p) {
+  p is Future;
+}
+''');
+    assertHasFix(FixKind.IMPORT_LIBRARY_SDK, '''
+import 'dart:async';
+
+main(p) {
+  p is Future;
+}
+''');
+  }
+
   void test_importLibrarySdk_withType_typeAnnotation() {
     _indexTestUnit('''
 main() {
@@ -1992,7 +2124,9 @@
   }
 
   void test_replaceVarWithDynamic() {
-    checkHasSingleError = false;
+    errorFilter = (AnalysisError error) {
+      return error.errorCode == ParserErrorCode.VAR_AS_TYPE_NAME;
+    };
     _indexTestUnit('''
 class A {
   Map<String, var> m;
@@ -2289,6 +2423,15 @@
 ''');
   }
 
+  void test_undefinedMethod_create_BAD_inSDK() {
+    _indexTestUnit('''
+main() {
+  List.foo();
+}
+''');
+    assertNoFix(FixKind.CREATE_METHOD);
+  }
+
   void test_undefinedMethod_create_generic_BAD() {
     _indexTestUnit('''
 class A<T> {
@@ -2714,9 +2857,10 @@
           error.errorCode == HintCode.UNUSED_FIELD ||
           error.errorCode == HintCode.UNUSED_LOCAL_VARIABLE;
     });
-    if (checkHasSingleError) {
-      expect(errors, hasLength(1));
+    if (errorFilter != null) {
+      errors = errors.where(errorFilter).toList();
     }
+    expect(errors, hasLength(1));
     return errors[0];
   }
 
diff --git a/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart b/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart
index 8f11a33..b748ea7 100644
--- a/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart
+++ b/pkg/analysis_server/test/services/refactoring/abstract_refactoring.dart
@@ -74,22 +74,19 @@
   /**
    * Asserts that [refactoring] initial/final conditions status is OK.
    */
-  Future assertRefactoringConditionsOK() {
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatusOK(status);
-      return refactoring.checkFinalConditions().then((status) {
-        assertRefactoringStatusOK(status);
-      });
-    });
+  Future assertRefactoringConditionsOK() async {
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatusOK(status);
+    status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
   /**
    * Asserts that [refactoring] final conditions status is OK.
    */
-  Future assertRefactoringFinalConditionsOK() {
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+  Future assertRefactoringFinalConditionsOK() async {
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
   /**
@@ -129,13 +126,11 @@
    * Checks that all conditions of [refactoring] are OK and the result of
    * applying the [Change] to [testUnit] is [expectedCode].
    */
-  Future assertSuccessfulRefactoring(String expectedCode) {
-    return assertRefactoringConditionsOK().then((_) {
-      return refactoring.createChange().then((SourceChange change) {
-        this.refactoringChange = change;
-        assertTestChangeResult(expectedCode);
-      });
-    });
+  Future assertSuccessfulRefactoring(String expectedCode) async {
+    await assertRefactoringConditionsOK();
+    SourceChange change = await refactoring.createChange();
+    this.refactoringChange = change;
+    assertTestChangeResult(expectedCode);
   }
 
   /**
diff --git a/pkg/analysis_server/test/services/refactoring/abstract_rename.dart b/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
index 75d922c..871f806 100644
--- a/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
+++ b/pkg/analysis_server/test/services/refactoring/abstract_rename.dart
@@ -75,25 +75,4 @@
     }
     return null;
   }
-
-//  /**
-//   * Asserts result of applying [change] to [testCode].
-//   */
-//  void assertTestChangeResult(Change change, String expected)
-//      {
-//    assertChangeResult(change, testSource, expected);
-//  }
-//
-//  /**
-//   * Asserts result of applying [change] to [source].
-//   */
-//  void assertChangeResult(Change change, Source source, String expected)
-//       {
-//    SourceChange sourceChange = getSourceChange(compositeChange, source);
-//    assertNotNull("No change for: " + source.toString(), sourceChange);
-//    String sourceResult = getChangeResult(context, source, sourceChange);
-//    assertEquals(expected, sourceResult);
-////    AnalysisContext context = getAnalysisContext();
-////    assertChangeResult(context, compositeChange, source, expected);
-//  }
 }
diff --git a/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart b/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
index 643079a..4b9f82d 100644
--- a/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/convert_getter_to_method_test.dart
@@ -7,6 +7,7 @@
 import 'dart:async';
 
 import 'package:analysis_server/src/protocol.dart' hide ElementKind;
+import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/src/generated/element.dart';
 import 'package:unittest/unittest.dart';
@@ -131,26 +132,23 @@
         'Only explicit getters can be converted to methods.');
   }
 
-  Future _assertInitialConditions_fatal(String message) {
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: message);
-    });
+  Future _assertInitialConditions_fatal(String message) async {
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: message);
   }
 
   /**
    * Checks that all conditions are OK and the result of applying [refactoring]
    * change to [testUnit] is [expectedCode].
    */
-  Future _assertSuccessfulRefactoring(String expectedCode) {
-    return assertRefactoringConditionsOK().then((_) {
-      return refactoring.createChange().then((SourceChange refactoringChange) {
-        this.refactoringChange = refactoringChange;
-        assertTestChangeResult(expectedCode);
-      });
-    });
+  Future _assertSuccessfulRefactoring(String expectedCode) async {
+    await assertRefactoringConditionsOK();
+    SourceChange refactoringChange = await refactoring.createChange();
+    this.refactoringChange = refactoringChange;
+    assertTestChangeResult(expectedCode);
   }
 
   void _createRefactoring(String elementName) {
diff --git a/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart b/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
index a4216f0..32780d8 100644
--- a/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/convert_method_to_getter_test.dart
@@ -7,6 +7,7 @@
 import 'dart:async';
 
 import 'package:analysis_server/src/protocol.dart' hide ElementKind;
+import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/src/generated/element.dart';
 import 'package:unittest/unittest.dart';
@@ -172,26 +173,23 @@
         'Only class methods or top-level functions can be converted to getters.');
   }
 
-  Future _assertInitialConditions_fatal(String message) {
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: message);
-    });
+  Future _assertInitialConditions_fatal(String message) async {
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: message);
   }
 
   /**
    * Checks that all conditions are OK and the result of applying the [Change]
    * to [testUnit] is [expectedCode].
    */
-  Future _assertSuccessfulRefactoring(String expectedCode) {
-    return assertRefactoringConditionsOK().then((_) {
-      return refactoring.createChange().then((SourceChange refactoringChange) {
-        this.refactoringChange = refactoringChange;
-        assertTestChangeResult(expectedCode);
-      });
-    });
+  Future _assertSuccessfulRefactoring(String expectedCode) async {
+    await assertRefactoringConditionsOK();
+    SourceChange refactoringChange = await refactoring.createChange();
+    this.refactoringChange = refactoringChange;
+    assertTestChangeResult(expectedCode);
   }
 
   void _createRefactoring(String elementName) {
diff --git a/pkg/analysis_server/test/services/refactoring/extract_local_test.dart b/pkg/analysis_server/test/services/refactoring/extract_local_test.dart
index efe7091..75ad10b 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_local_test.dart
@@ -7,6 +7,7 @@
 import 'dart:async';
 
 import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/extract_local.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:unittest/unittest.dart';
@@ -25,7 +26,7 @@
 class ExtractLocalTest extends RefactoringTest {
   ExtractLocalRefactoringImpl refactoring;
 
-  test_checkFinalConditions_sameVariable_after() {
+  test_checkFinalConditions_sameVariable_after() async {
     indexTestUnit('''
 main() {
   int a = 1 + 2;
@@ -34,16 +35,15 @@
 ''');
     _createRefactoringForString('1 + 2');
     // conflicting name
-    return refactoring.checkAllConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.WARNING,
-          expectedMessage:
-              "A variable with name 'res' is already defined in the visible scope.");
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.WARNING,
+        expectedMessage:
+            "A variable with name 'res' is already defined in the visible scope.");
   }
 
-  test_checkFinalConditions_sameVariable_before() {
+  test_checkFinalConditions_sameVariable_before() async {
     indexTestUnit('''
 main() {
   var res;
@@ -52,16 +52,15 @@
 ''');
     _createRefactoringForString('1 + 2');
     // conflicting name
-    return refactoring.checkAllConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.WARNING,
-          expectedMessage:
-              "A variable with name 'res' is already defined in the visible scope.");
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.WARNING,
+        expectedMessage:
+            "A variable with name 'res' is already defined in the visible scope.");
   }
 
-  test_checkInitialConditions_assignmentLeftHandSize() {
+  test_checkInitialConditions_assignmentLeftHandSize() async {
     indexTestUnit('''
 main() {
   var v = 0;
@@ -70,15 +69,14 @@
 ''');
     _createRefactoringWithSuffix('v', ' = 1;');
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: 'Cannot extract the left-hand side of an assignment.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: 'Cannot extract the left-hand side of an assignment.');
   }
 
-  test_checkInitialConditions_methodName_reference() {
+  test_checkInitialConditions_methodName_reference() async {
     indexTestUnit('''
 main() {
   main();
@@ -86,15 +84,14 @@
 ''');
     _createRefactoringWithSuffix('main', '();');
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: 'Cannot extract a single method name.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: 'Cannot extract a single method name.');
   }
 
-  test_checkInitialConditions_nameOfProperty_prefixedIdentifier() {
+  test_checkInitialConditions_nameOfProperty_prefixedIdentifier() async {
     indexTestUnit('''
 main(p) {
   p.value; // marker
@@ -102,15 +99,14 @@
 ''');
     _createRefactoringWithSuffix('value', '; // marker');
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: 'Cannot extract name part of a property access.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: 'Cannot extract name part of a property access.');
   }
 
-  test_checkInitialConditions_nameOfProperty_propertyAccess() {
+  test_checkInitialConditions_nameOfProperty_propertyAccess() async {
     indexTestUnit('''
 main() {
   foo().length; // marker
@@ -119,15 +115,14 @@
 ''');
     _createRefactoringWithSuffix('length', '; // marker');
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: 'Cannot extract name part of a property access.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: 'Cannot extract name part of a property access.');
   }
 
-  test_checkInitialConditions_namePartOfDeclaration_variable() {
+  test_checkInitialConditions_namePartOfDeclaration_variable() async {
     indexTestUnit('''
 main() {
   int vvv = 0;
@@ -135,30 +130,28 @@
 ''');
     _createRefactoringWithSuffix('vvv', ' = 0;');
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: 'Cannot extract the name part of a declaration.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: 'Cannot extract the name part of a declaration.');
   }
 
-  test_checkInitialConditions_notPartOfFunction() {
+  test_checkInitialConditions_notPartOfFunction() async {
     indexTestUnit('''
 int a = 1 + 2;
 ''');
     _createRefactoringForString('1 + 2');
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage:
-              'Expression inside of function must be selected to activate this refactoring.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage:
+            'Expression inside of function must be selected to activate this refactoring.');
   }
 
-  test_checkInitialConditions_stringSelection_leadingQuote() {
+  test_checkInitialConditions_stringSelection_leadingQuote() async {
     indexTestUnit('''
 main() {
   var vvv = 'abc';
@@ -166,16 +159,15 @@
 ''');
     _createRefactoringForString("'a");
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage:
-              'Cannot extract only leading or trailing quote of string literal.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage:
+            'Cannot extract only leading or trailing quote of string literal.');
   }
 
-  test_checkInitialConditions_stringSelection_trailingQuote() {
+  test_checkInitialConditions_stringSelection_trailingQuote() async {
     indexTestUnit('''
 main() {
   var vvv = 'abc';
@@ -183,13 +175,12 @@
 ''');
     _createRefactoringForString("c'");
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage:
-              'Cannot extract only leading or trailing quote of string literal.');
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage:
+            'Cannot extract only leading or trailing quote of string literal.');
   }
 
   test_checkLocalName() {
@@ -469,7 +460,7 @@
 ''');
   }
 
-  test_guessNames_fragmentExpression() {
+  test_guessNames_fragmentExpression() async {
     indexTestUnit('''
 main() {
   var a = 111 + 222 + 333 + 444;
@@ -477,12 +468,11 @@
 ''');
     _createRefactoringForString('222 + 333');
     // check guesses
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.names, isEmpty);
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.names, isEmpty);
   }
 
-  test_guessNames_singleExpression() {
+  test_guessNames_singleExpression() async {
     indexTestUnit('''
 class TreeItem {}
 TreeItem getSelectedItem() => null;
@@ -493,14 +483,13 @@
 ''');
     _createRefactoringWithSuffix('getSelectedItem()', '); // marker');
     // check guesses
-    return refactoring.checkInitialConditions().then((_) {
-      expect(
-          refactoring.names,
-          unorderedEquals(['selectedItem', 'item', 'my', 'treeItem']));
-    });
+    await refactoring.checkInitialConditions();
+    expect(
+        refactoring.names,
+        unorderedEquals(['selectedItem', 'item', 'my', 'treeItem']));
   }
 
-  test_guessNames_stringPart() {
+  test_guessNames_stringPart() async {
     indexTestUnit('''
 main() {
   var s = 'Hello Bob... welcome to Dart!';
@@ -508,9 +497,8 @@
 ''');
     _createRefactoringForString('Hello Bob');
     // check guesses
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.names, unorderedEquals(['helloBob', 'bob']));
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.names, unorderedEquals(['helloBob', 'bob']));
   }
 
   test_occurences_differentVariable() {
@@ -692,7 +680,7 @@
 ''');
   }
 
-  test_offsets_lengths() {
+  test_offsets_lengths() async {
     indexTestUnit('''
 int foo() => 42;
 main() {
@@ -701,13 +689,12 @@
 }
 ''');
     _createRefactoringWithSuffix('foo()', '; // marker');
-    // apply refactoring
-    return refactoring.checkInitialConditions().then((_) {
-      expect(
-          refactoring.offsets,
-          unorderedEquals([findOffset('foo();'), findOffset('foo( );')]));
-      expect(refactoring.lengths, unorderedEquals([5, 6]));
-    });
+    // check offsets
+    await refactoring.checkInitialConditions();
+    expect(
+        refactoring.offsets,
+        unorderedEquals([findOffset('foo();'), findOffset('foo( );')]));
+    expect(refactoring.lengths, unorderedEquals([5, 6]));
   }
 
   test_singleExpression() {
@@ -955,26 +942,23 @@
 ''');
   }
 
-  Future _assertInitialConditions_fatal_selection() {
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: 'Expression must be selected to activate this refactoring.');
-    });
+  Future _assertInitialConditions_fatal_selection() async {
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: 'Expression must be selected to activate this refactoring.');
   }
 
   /**
    * Checks that all conditions are OK and the result of applying the [Change]
    * to [testUnit] is [expectedCode].
    */
-  Future _assertSuccessfulRefactoring(String expectedCode) {
-    return assertRefactoringConditionsOK().then((_) {
-      return refactoring.createChange().then((SourceChange refactoringChange) {
-        this.refactoringChange = refactoringChange;
-        assertTestChangeResult(expectedCode);
-      });
-    });
+  Future _assertSuccessfulRefactoring(String expectedCode) async {
+    await assertRefactoringConditionsOK();
+    SourceChange refactoringChange = await refactoring.createChange();
+    this.refactoringChange = refactoringChange;
+    assertTestChangeResult(expectedCode);
   }
 
   void _createRefactoring(int offset, int length) {
diff --git a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
index c77bd77..95c05a5 100644
--- a/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/extract_method_test.dart
@@ -7,6 +7,7 @@
 import 'dart:async';
 
 import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analysis_server/src/services/refactoring/extract_method.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:unittest/unittest.dart';
@@ -362,7 +363,7 @@
         'Not all selected statements are enclosed by the same parent statement.');
   }
 
-  test_bad_parameterName_duplicate() {
+  test_bad_parameterName_duplicate() async {
     indexTestUnit('''
 main() {
   int v1 = 1;
@@ -374,19 +375,18 @@
 ''');
     _createRefactoringForStartEndComments();
     // update parameters
-    return refactoring.checkInitialConditions().then((_) {
-      {
-        var parameters = _getParametersCopy();
-        expect(parameters, hasLength(2));
-        parameters[0].name = 'dup';
-        parameters[1].name = 'dup';
-        refactoring.parameters = parameters;
-      }
-      return _assertFinalConditionsError("Parameter 'dup' already exists");
-    });
+    await refactoring.checkInitialConditions();
+    {
+      List<RefactoringMethodParameter> parameters = _getParametersCopy();
+      expect(parameters, hasLength(2));
+      parameters[0].name = 'dup';
+      parameters[1].name = 'dup';
+      refactoring.parameters = parameters;
+    }
+    return _assertFinalConditionsError("Parameter 'dup' already exists");
   }
 
-  test_bad_parameterName_inUse() {
+  test_bad_parameterName_inUse() async {
     indexTestUnit('''
 main() {
   int v1 = 1;
@@ -398,16 +398,15 @@
 ''');
     _createRefactoringForStartEndComments();
     // update parameters
-    return refactoring.checkInitialConditions().then((_) {
-      {
-        var parameters = _getParametersCopy();
-        expect(parameters, hasLength(2));
-        parameters[0].name = 'a';
-        refactoring.parameters = parameters;
-      }
-      return _assertFinalConditionsError(
-          "'a' is already used as a name in the selected code");
-    });
+    await refactoring.checkInitialConditions();
+    {
+      List<RefactoringMethodParameter> parameters = _getParametersCopy();
+      expect(parameters, hasLength(2));
+      parameters[0].name = 'a';
+      refactoring.parameters = parameters;
+    }
+    return _assertFinalConditionsError(
+        "'a' is already used as a name in the selected code");
   }
 
   test_bad_selectionEndsInSomeNode() {
@@ -425,6 +424,21 @@
             "Extend selection to a valid range.");
   }
 
+  test_bad_statements_exit_notAllExecutionFlows() {
+    indexTestUnit('''
+main(int p) {
+// start
+  if (p == 0) {
+    return;
+  }
+// end
+  print(p);
+}
+''');
+    _createRefactoringForStartEndComments();
+    return _assertConditionsError(ExtractMethodRefactoringImpl.ERROR_EXITS);
+  }
+
   test_bad_statements_return_andAssignsVariable() {
     indexTestUnit('''
 main() {
@@ -611,7 +625,7 @@
         "Operation not applicable to a while statement's expression and body.");
   }
 
-  test_canExtractGetter_false_fieldAssignment() {
+  test_canExtractGetter_false_fieldAssignment() async {
     indexTestUnit('''
 class A {
   var f;
@@ -624,13 +638,12 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.canCreateGetter, false);
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.canCreateGetter, false);
+    expect(refactoring.createGetter, false);
   }
 
-  test_canExtractGetter_false_hasParameters() {
+  test_canExtractGetter_false_hasParameters() async {
     indexTestUnit('''
 main(int p) {
   int a = p + 1;
@@ -638,13 +651,12 @@
 ''');
     _createRefactoringForString('p + 1');
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.canCreateGetter, false);
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.canCreateGetter, false);
+    expect(refactoring.createGetter, false);
   }
 
-  test_canExtractGetter_false_returnNotUsed_assignment() {
+  test_canExtractGetter_false_returnNotUsed_assignment() async {
     indexTestUnit('''
 var topVar = 0;
 f(int p) {
@@ -653,13 +665,12 @@
 ''');
     _createRefactoringForString('topVar = 5');
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.canCreateGetter, false);
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.canCreateGetter, false);
+    expect(refactoring.createGetter, false);
   }
 
-  test_canExtractGetter_false_returnNotUsed_noReturn() {
+  test_canExtractGetter_false_returnNotUsed_noReturn() async {
     indexTestUnit('''
 var topVar = 0;
 main() {
@@ -672,13 +683,12 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.canCreateGetter, false);
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.canCreateGetter, false);
+    expect(refactoring.createGetter, false);
   }
 
-  test_canExtractGetter_true() {
+  test_canExtractGetter_true() async {
     indexTestUnit('''
 main() {
   int a = 1 + 2;
@@ -686,10 +696,9 @@
 ''');
     _createRefactoringForString('1 + 2');
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.canCreateGetter, true);
-      expect(refactoring.createGetter, true);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.canCreateGetter, true);
+    expect(refactoring.createGetter, true);
   }
 
   test_checkName() {
@@ -791,7 +800,7 @@
 ''');
   }
 
-  test_closure_bad_referencesLocalVariable() {
+  test_closure_bad_referencesLocalVariable() async {
     indexTestUnit('''
 process(f(x)) {}
 main() {
@@ -801,16 +810,15 @@
 ''');
     _createRefactoringForString('(x) => x * k');
     // check
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage:
-              'Cannot extract closure as method, it references 1 external variable(s).');
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage:
+            'Cannot extract closure as method, it references 1 external variable(s).');
   }
 
-  test_closure_bad_referencesParameter() {
+  test_closure_bad_referencesParameter() async {
     indexTestUnit('''
 process(f(x)) {}
 main(int k) {
@@ -819,13 +827,12 @@
 ''');
     _createRefactoringForString('(x) => x * k');
     // check
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage:
-              'Cannot extract closure as method, it references 1 external variable(s).');
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage:
+            'Cannot extract closure as method, it references 1 external variable(s).');
   }
 
   test_fromTopLevelVariableInitializerClosure() {
@@ -849,7 +856,7 @@
 ''');
   }
 
-  test_getExtractGetter_false_do() {
+  test_getExtractGetter_false_do() async {
     indexTestUnit('''
 main() {
 // start
@@ -863,12 +870,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, false);
   }
 
-  test_getExtractGetter_false_for() {
+  test_getExtractGetter_false_for() async {
     indexTestUnit('''
 main() {
 // start
@@ -882,12 +888,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, false);
   }
 
-  test_getExtractGetter_false_forEach() {
+  test_getExtractGetter_false_forEach() async {
     indexTestUnit('''
 main() {
 // start
@@ -901,12 +906,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, false);
   }
 
-  test_getExtractGetter_false_methodInvocation_expression() {
+  test_getExtractGetter_false_methodInvocation_expression() async {
     indexTestUnit('''
 main() {
   int v = calculateSomething() + 5;
@@ -915,12 +919,11 @@
 ''');
     _createRefactoringForString('calculateSomething() + 5');
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, false);
   }
 
-  test_getExtractGetter_false_methodInvocation_statements() {
+  test_getExtractGetter_false_methodInvocation_statements() async {
     indexTestUnit('''
 main() {
 // start
@@ -932,12 +935,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, false);
   }
 
-  test_getExtractGetter_false_while() {
+  test_getExtractGetter_false_while() async {
     indexTestUnit('''
 main() {
 // start
@@ -951,12 +953,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, false);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, false);
   }
 
-  test_getExtractGetter_true_simpleBlock() {
+  test_getExtractGetter_true_simpleBlock() async {
     indexTestUnit('''
 main() {
 // start
@@ -967,12 +968,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, true);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, true);
   }
 
-  test_getExtractGetter_true_singleExpression() {
+  test_getExtractGetter_true_singleExpression() async {
     indexTestUnit('''
 main() {
 // start
@@ -983,9 +983,8 @@
 ''');
     _createRefactoringForString('1 + 2');
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.createGetter, true);
-    });
+    await assertRefactoringConditionsOK();
+    expect(refactoring.createGetter, true);
   }
 
   test_getRefactoringName_function() {
@@ -1010,7 +1009,7 @@
     expect(refactoring.refactoringName, 'Extract Method');
   }
 
-  test_names_singleExpression() {
+  test_names_singleExpression() async {
     indexTestUnit('''
 class TreeItem {}
 TreeItem getSelectedItem() => null;
@@ -1022,14 +1021,13 @@
 ''');
     _createRefactoringWithSuffix('getSelectedItem()', '); // marker');
     // check names
-    return refactoring.checkInitialConditions().then((_) {
-      expect(
-          refactoring.names,
-          unorderedEquals(['selectedItem', 'item', 'my', 'treeItem2']));
-    });
+    await refactoring.checkInitialConditions();
+    expect(
+        refactoring.names,
+        unorderedEquals(['selectedItem', 'item', 'my', 'treeItem2']));
   }
 
-  test_offsets_lengths() {
+  test_offsets_lengths() async {
     indexTestUnit('''
 main() {
   int a = 1 + 2;
@@ -1038,15 +1036,14 @@
 ''');
     _createRefactoringForString('1 +  2');
     // apply refactoring
-    return refactoring.checkInitialConditions().then((_) {
-      expect(
-          refactoring.offsets,
-          unorderedEquals([findOffset('1 + 2'), findOffset('1 +  2')]));
-      expect(refactoring.lengths, unorderedEquals([5, 6]));
-    });
+    await refactoring.checkInitialConditions();
+    expect(
+        refactoring.offsets,
+        unorderedEquals([findOffset('1 + 2'), findOffset('1 +  2')]));
+    expect(refactoring.lengths, unorderedEquals([5, 6]));
   }
 
-  test_returnType_expression() {
+  test_returnType_expression() async {
     indexTestUnit('''
 main() {
   int a = 1 + 2;
@@ -1054,12 +1051,11 @@
 ''');
     _createRefactoringForString('1 + 2');
     // do check
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.returnType, 'int');
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.returnType, 'int');
   }
 
-  test_returnType_statements() {
+  test_returnType_statements() async {
     indexTestUnit('''
 main() {
 // start
@@ -1070,12 +1066,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // do check
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.returnType, 'double');
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.returnType, 'double');
   }
 
-  test_returnType_statements_nullMix() {
+  test_returnType_statements_nullMix() async {
     indexTestUnit('''
 main(bool p) {
 // start
@@ -1088,12 +1083,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // do check
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.returnType, 'int');
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.returnType, 'int');
   }
 
-  test_returnType_statements_void() {
+  test_returnType_statements_void() async {
     indexTestUnit('''
 main() {
 // start
@@ -1103,12 +1097,11 @@
 ''');
     _createRefactoringForStartEndComments();
     // do check
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.returnType, 'void');
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.returnType, 'void');
   }
 
-  test_setExtractGetter() {
+  test_setExtractGetter() async {
     indexTestUnit('''
 main() {
   int a = 1 + 2;
@@ -1116,20 +1109,17 @@
 ''');
     _createRefactoringForString('1 + 2');
     // apply refactoring
-    return assertRefactoringConditionsOK().then((_) {
-      expect(refactoring.canCreateGetter, true);
-      expect(refactoring.createGetter, true);
-      return refactoring.createChange().then((SourceChange refactoringChange) {
-        this.refactoringChange = refactoringChange;
-        assertTestChangeResult('''
+    await assertRefactoringConditionsOK();
+    expect(refactoring.canCreateGetter, true);
+    expect(refactoring.createGetter, true);
+    refactoringChange = await refactoring.createChange();
+    assertTestChangeResult('''
 main() {
   int a = res;
 }
 
 int get res => 1 + 2;
 ''');
-      });
-    });
   }
 
   test_singleExpression() {
@@ -1609,7 +1599,7 @@
 ''');
   }
 
-  test_singleExpression_withVariables_doRename() {
+  test_singleExpression_withVariables_doRename() async {
     indexTestUnit('''
 main() {
   int v1 = 1;
@@ -1621,19 +1611,19 @@
 ''');
     _createRefactoringForString('v1 + v2 + v1');
     // apply refactoring
-    return refactoring.checkInitialConditions().then((_) {
-      {
-        var parameters = _getParametersCopy();
-        expect(parameters, hasLength(2));
-        expect(parameters[0].name, 'v1');
-        expect(parameters[1].name, 'v2');
-        parameters[0].name = 'par1';
-        parameters[1].name = 'param2';
-        refactoring.parameters = parameters;
-      }
-      return assertRefactoringFinalConditionsOK().then((_) {
-        refactoring.createGetter = false;
-        return _assertRefactoringChange('''
+    await refactoring.checkInitialConditions();
+    {
+      List<RefactoringMethodParameter> parameters = _getParametersCopy();
+      expect(parameters, hasLength(2));
+      expect(parameters[0].name, 'v1');
+      expect(parameters[1].name, 'v2');
+      parameters[0].name = 'par1';
+      parameters[1].name = 'param2';
+      refactoring.parameters = parameters;
+    }
+    await assertRefactoringFinalConditionsOK();
+    refactoring.createGetter = false;
+    return _assertRefactoringChange('''
 main() {
   int v1 = 1;
   int v2 = 2;
@@ -1644,11 +1634,9 @@
 
 int res(int par1, int param2) => par1 + param2 + par1;
 ''');
-      });
-    });
   }
 
-  test_singleExpression_withVariables_doReorder() {
+  test_singleExpression_withVariables_doReorder() async {
     indexTestUnit('''
 main() {
   int v1 = 1;
@@ -1660,19 +1648,19 @@
 ''');
     _createRefactoringForString('v1 + v2');
     // apply refactoring
-    return refactoring.checkInitialConditions().then((_) {
-      {
-        var parameters = _getParametersCopy();
-        expect(parameters, hasLength(2));
-        expect(parameters[0].name, 'v1');
-        expect(parameters[1].name, 'v2');
-        var parameter = parameters.removeAt(1);
-        parameters.insert(0, parameter);
-        refactoring.parameters = parameters;
-      }
-      return assertRefactoringFinalConditionsOK().then((_) {
-        refactoring.createGetter = false;
-        return _assertRefactoringChange('''
+    await refactoring.checkInitialConditions();
+    {
+      List<RefactoringMethodParameter> parameters = _getParametersCopy();
+      expect(parameters, hasLength(2));
+      expect(parameters[0].name, 'v1');
+      expect(parameters[1].name, 'v2');
+      var parameter = parameters.removeAt(1);
+      parameters.insert(0, parameter);
+      refactoring.parameters = parameters;
+    }
+    await assertRefactoringFinalConditionsOK();
+    refactoring.createGetter = false;
+    return _assertRefactoringChange('''
 main() {
   int v1 = 1;
   int v2 = 2;
@@ -1683,8 +1671,6 @@
 
 int res(int v2, int v1) => v1 + v2;
 ''');
-      });
-    });
   }
 
   test_singleExpression_withVariables_namedExpression() {
@@ -1710,7 +1696,7 @@
 ''');
   }
 
-  test_singleExpression_withVariables_newType() {
+  test_singleExpression_withVariables_newType() async {
     indexTestUnit('''
 main() {
   int v1 = 1;
@@ -1721,21 +1707,21 @@
 ''');
     _createRefactoringForString('v1 + v2 + v3');
     // apply refactoring
-    return refactoring.checkInitialConditions().then((_) {
-      {
-        var parameters = _getParametersCopy();
-        expect(parameters, hasLength(3));
-        expect(parameters[0].name, 'v1');
-        expect(parameters[1].name, 'v2');
-        expect(parameters[2].name, 'v3');
-        parameters[0].type = 'num';
-        parameters[1].type = 'dynamic';
-        parameters[2].type = '';
-        refactoring.parameters = parameters;
-      }
-      return assertRefactoringFinalConditionsOK().then((_) {
-        refactoring.createGetter = false;
-        return _assertRefactoringChange('''
+    await refactoring.checkInitialConditions();
+    {
+      List<RefactoringMethodParameter> parameters = _getParametersCopy();
+      expect(parameters, hasLength(3));
+      expect(parameters[0].name, 'v1');
+      expect(parameters[1].name, 'v2');
+      expect(parameters[2].name, 'v3');
+      parameters[0].type = 'num';
+      parameters[1].type = 'dynamic';
+      parameters[2].type = '';
+      refactoring.parameters = parameters;
+    }
+    await assertRefactoringFinalConditionsOK();
+    refactoring.createGetter = false;
+    return _assertRefactoringChange('''
 main() {
   int v1 = 1;
   int v2 = 2;
@@ -1745,8 +1731,6 @@
 
 int res(num v1, v2, v3) => v1 + v2 + v3;
 ''');
-      });
-    });
   }
 
   test_singleExpression_withVariables_useBestType() {
@@ -1971,7 +1955,7 @@
 ''');
   }
 
-  test_statements_definesVariable_twoUsedOutside() {
+  test_statements_definesVariable_twoUsedOutside() async {
     indexTestUnit('''
 main() {
 // start
@@ -1983,9 +1967,8 @@
 ''');
     _createRefactoringForStartEndComments();
     // check conditions
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
   }
 
   test_statements_duplicate_absolutelySame() {
@@ -2112,6 +2095,21 @@
 ''');
   }
 
+  test_statements_exit_throws() async {
+    indexTestUnit('''
+main(int p) {
+// start
+  if (p == 0) {
+    return;
+  }
+  throw 'boo!';
+// end
+}
+''');
+    _createRefactoringForStartEndComments();
+    await assertRefactoringConditionsOK();
+  }
+
   test_statements_inSwitchMember() {
     indexTestUnit('''
 class A {
@@ -2324,8 +2322,10 @@
 main(bool b) {
 // start
   if (b) {
+    print(true);
     return <int>[];
   } else {
+    print(false);
     return <String>[];
   }
 // end
@@ -2342,8 +2342,10 @@
 
 List res(bool b) {
   if (b) {
+    print(true);
     return <int>[];
   } else {
+    print(false);
     return <String>[];
   }
 }
@@ -2404,49 +2406,44 @@
 ''');
   }
 
-  Future _assertConditionsError(String message) {
-    return refactoring.checkAllConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: message);
-    });
+  Future _assertConditionsError(String message) async {
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: message);
   }
 
-  Future _assertConditionsFatal(String message) {
-    return refactoring.checkAllConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: message);
-    });
+  Future _assertConditionsFatal(String message) async {
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: message);
   }
 
-  Future _assertFinalConditionsError(String message) {
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: message);
-    });
+  Future _assertFinalConditionsError(String message) async {
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: message);
   }
 
-  Future _assertRefactoringChange(String expectedCode) {
-    return refactoring.createChange().then((SourceChange refactoringChange) {
-      this.refactoringChange = refactoringChange;
-      assertTestChangeResult(expectedCode);
-    });
+  Future _assertRefactoringChange(String expectedCode) async {
+    SourceChange refactoringChange = await refactoring.createChange();
+    this.refactoringChange = refactoringChange;
+    assertTestChangeResult(expectedCode);
   }
 
   /**
    * Checks that all conditions are OK and the result of applying the [Change]
    * to [testUnit] is [expectedCode].
    */
-  Future _assertSuccessfulRefactoring(String expectedCode) {
-    return assertRefactoringConditionsOK().then((_) {
-      refactoring.createGetter = false;
-      return _assertRefactoringChange(expectedCode);
-    });
+  Future _assertSuccessfulRefactoring(String expectedCode) async {
+    await assertRefactoringConditionsOK();
+    refactoring.createGetter = false;
+    return _assertRefactoringChange(expectedCode);
   }
 
   void _createRefactoring(int offset, int length) {
diff --git a/pkg/analysis_server/test/services/refactoring/inline_local_test.dart b/pkg/analysis_server/test/services/refactoring/inline_local_test.dart
index b4646f0..335387b 100644
--- a/pkg/analysis_server/test/services/refactoring/inline_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/inline_local_test.dart
@@ -24,7 +24,7 @@
 class InlineLocalTest extends RefactoringTest {
   InlineLocalRefactoringImpl refactoring;
 
-  test_access() {
+  test_access() async {
     indexTestUnit('''
 main() {
   int test = 1 + 2;
@@ -35,35 +35,32 @@
     _createRefactoring('test =');
     expect(refactoring.refactoringName, 'Inline Local Variable');
     // check initial conditions and access
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.variableName, 'test');
-      expect(refactoring.referenceCount, 2);
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.variableName, 'test');
+    expect(refactoring.referenceCount, 2);
   }
 
-  test_bad_selectionMethod() {
+  test_bad_selectionMethod() async {
     indexTestUnit(r'''
 main() {
 }
 ''');
     _createRefactoring('main() {');
-    return refactoring.checkInitialConditions().then((status) {
-      _assert_fatalError_selection(status);
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    _assert_fatalError_selection(status);
   }
 
-  test_bad_selectionParameter() {
+  test_bad_selectionParameter() async {
     indexTestUnit(r'''
 main(int test) {
 }
 ''');
     _createRefactoring('test) {');
-    return refactoring.checkInitialConditions().then((status) {
-      _assert_fatalError_selection(status);
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    _assert_fatalError_selection(status);
   }
 
-  test_bad_selectionVariable_hasAssignments_1() {
+  test_bad_selectionVariable_hasAssignments_1() async {
     indexTestUnit(r'''
 main() {
   int test = 0;
@@ -71,15 +68,14 @@
 }
 ''');
     _createRefactoring('test = 0');
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedContextSearch: 'test = 1');
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedContextSearch: 'test = 1');
   }
 
-  test_bad_selectionVariable_hasAssignments_2() {
+  test_bad_selectionVariable_hasAssignments_2() async {
     indexTestUnit(r'''
 main() {
   int test = 0;
@@ -87,15 +83,14 @@
 }
 ''');
     _createRefactoring('test = 0');
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedContextSearch: 'test += 1');
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedContextSearch: 'test += 1');
   }
 
-  test_bad_selectionVariable_notInBlock() {
+  test_bad_selectionVariable_notInBlock() async {
     indexTestUnit(r'''
 main() {
   if (true)
@@ -103,21 +98,19 @@
 }
 ''');
     _createRefactoring('test = 0');
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
   }
 
-  test_bad_selectionVariable_notInitialized() {
+  test_bad_selectionVariable_notInitialized() async {
     indexTestUnit(r'''
 main() {
   int test;
 }
 ''');
     _createRefactoring('test;');
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
   }
 
   test_OK_cascade_intoCascade() {
diff --git a/pkg/analysis_server/test/services/refactoring/inline_method_test.dart b/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
index 01dfc52..8a93a7f 100644
--- a/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/inline_method_test.dart
@@ -14,6 +14,7 @@
 
 import '../../reflective_tests.dart';
 import 'abstract_refactoring.dart';
+import 'package:analysis_server/src/services/correction/status.dart';
 
 
 main() {
@@ -28,7 +29,7 @@
   bool deleteSource;
   bool inlineAll;
 
-  test_access_FunctionElement() {
+  test_access_FunctionElement() async {
     indexTestUnit(r'''
 test(a, b) {
   return a + b;
@@ -39,15 +40,14 @@
 ''');
     _createRefactoring('test(1, 2)');
     // validate state
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.refactoringName, 'Inline Function');
-      expect(refactoring.className, isNull);
-      expect(refactoring.methodName, 'test');
-      expect(refactoring.isDeclaration, isFalse);
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.refactoringName, 'Inline Function');
+    expect(refactoring.className, isNull);
+    expect(refactoring.methodName, 'test');
+    expect(refactoring.isDeclaration, isFalse);
   }
 
-  test_access_MethodElement() {
+  test_access_MethodElement() async {
     indexTestUnit(r'''
 class A {
   test(a, b) {
@@ -60,15 +60,14 @@
 ''');
     _createRefactoring('test(a, b)');
     // validate state
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.refactoringName, 'Inline Method');
-      expect(refactoring.className, 'A');
-      expect(refactoring.methodName, 'test');
-      expect(refactoring.isDeclaration, isTrue);
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.refactoringName, 'Inline Method');
+    expect(refactoring.className, 'A');
+    expect(refactoring.methodName, 'test');
+    expect(refactoring.isDeclaration, isTrue);
   }
 
-  test_bad_cascadeInvocation() {
+  test_bad_cascadeInvocation() async {
     indexTestUnit(r'''
 class A {
   foo() {}
@@ -82,14 +81,13 @@
 ''');
     _createRefactoring('test() {');
     // error
-    return refactoring.checkAllConditions().then((status) {
-      var location = new SourceRange(findOffset('..test()'), '..test()'.length);
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: 'Cannot inline cascade invocation.',
-          expectedContextRange: location);
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    var location = new SourceRange(findOffset('..test()'), '..test()'.length);
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: 'Cannot inline cascade invocation.',
+        expectedContextRange: location);
   }
 
   test_bad_constructor() {
@@ -103,7 +101,7 @@
     return _assertInvalidSelection();
   }
 
-  test_bad_deleteSource_inlineOne() {
+  test_bad_deleteSource_inlineOne() async {
     indexTestUnit(r'''
 test(a, b) {
   return a + b;
@@ -114,18 +112,17 @@
 }
 ''');
     _createRefactoring('test(1, 2)');
-    // error
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatusOK(status);
-      refactoring.deleteSource = true;
-      refactoring.inlineAll = false;
-      return refactoring.checkFinalConditions().then((status) {
-        assertRefactoringStatus(
-            status,
-            RefactoringProblemSeverity.ERROR,
-            expectedMessage: 'All references must be inlined to remove the source.');
-      });
-    });
+    // initial conditions
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatusOK(status);
+    refactoring.deleteSource = true;
+    refactoring.inlineAll = false;
+    // final conditions
+    status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: 'All references must be inlined to remove the source.');
   }
 
   test_bad_notExecutableElement() {
@@ -776,7 +773,7 @@
 ''');
   }
 
-  test_initialMode_all() {
+  test_initialMode_all() async {
     indexTestUnit(r'''
 test(a, b) {
   return a + b;
@@ -787,13 +784,12 @@
 ''');
     _createRefactoring('test(a, b)');
     // validate state
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.deleteSource, true);
-      expect(refactoring.inlineAll, true);
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.deleteSource, true);
+    expect(refactoring.inlineAll, true);
   }
 
-  test_initialMode_single() {
+  test_initialMode_single() async {
     indexTestUnit(r'''
 test(a, b) {
   return a + b;
@@ -806,10 +802,9 @@
     _createRefactoring('test(1, 2)');
     deleteSource = false;
     // validate state
-    return refactoring.checkInitialConditions().then((_) {
-      expect(refactoring.deleteSource, false);
-      expect(refactoring.inlineAll, false);
-    });
+    await refactoring.checkInitialConditions();
+    expect(refactoring.deleteSource, false);
+    expect(refactoring.inlineAll, false);
   }
 
   test_method_emptyBody() {
@@ -1104,7 +1099,7 @@
 ''');
   }
 
-  test_noArgument_required() {
+  test_noArgument_required() async {
     verifyNoTestUnitErrors = false;
     indexTestUnit(r'''
 test(a) {
@@ -1116,14 +1111,13 @@
 ''');
     _createRefactoring('test();');
     // error
-    return refactoring.checkAllConditions().then((status) {
-      var location = new SourceRange(findOffset('test();'), 'test()'.length);
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: 'No argument for the parameter "a".',
-          expectedContextRange: location);
-    });
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    var location = new SourceRange(findOffset('test();'), 'test()'.length);
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: 'No argument for the parameter "a".',
+        expectedContextRange: location);
   }
 
   test_reference_expressionBody() {
@@ -1393,22 +1387,20 @@
 ''');
   }
 
-  Future _assertConditionsError(String message) {
-    return refactoring.checkAllConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: message);
-    });
+  Future _assertConditionsError(String message) async {
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: message);
   }
 
-  Future _assertConditionsFatal(String message) {
-    return refactoring.checkAllConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.FATAL,
-          expectedMessage: message);
-    });
+  Future _assertConditionsFatal(String message) async {
+    RefactoringStatus status = await refactoring.checkAllConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.FATAL,
+        expectedMessage: message);
   }
 
   Future _assertInvalidSelection() {
@@ -1416,23 +1408,23 @@
         'Method declaration or reference must be selected to activate this refactoring.');
   }
 
-  Future _assertSuccessfulRefactoring(String expectedCode) {
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatusOK(status);
-      if (deleteSource != null) {
-        refactoring.deleteSource = deleteSource;
-      }
-      if (inlineAll != null) {
-        refactoring.inlineAll = inlineAll;
-      }
-      return refactoring.checkFinalConditions().then((status) {
-        assertRefactoringStatusOK(status);
-        return refactoring.createChange().then((SourceChange change) {
-          this.refactoringChange = change;
-          assertTestChangeResult(expectedCode);
-        });
-      });
-    });
+  Future _assertSuccessfulRefactoring(String expectedCode) async {
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatusOK(status);
+    // configure
+    if (deleteSource != null) {
+      refactoring.deleteSource = deleteSource;
+    }
+    if (inlineAll != null) {
+      refactoring.inlineAll = inlineAll;
+    }
+    // final conditions
+    status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
+    // change
+    SourceChange change = await refactoring.createChange();
+    this.refactoringChange = change;
+    assertTestChangeResult(expectedCode);
   }
 
   void _createRefactoring(String search) {
diff --git a/pkg/analysis_server/test/services/refactoring/move_file_test.dart b/pkg/analysis_server/test/services/refactoring/move_file_test.dart
index 9b42a64..3772650 100644
--- a/pkg/analysis_server/test/services/refactoring/move_file_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/move_file_test.dart
@@ -6,7 +6,6 @@
 
 import 'dart:async';
 
-import 'package:analysis_server/src/protocol.dart';
 import 'package:analysis_server/src/services/refactoring/refactoring.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/source/package_map_resolver.dart';
@@ -29,7 +28,7 @@
 class MoveFileTest extends RefactoringTest {
   MoveFileRefactoring refactoring;
 
-  test_definingUnit() {
+  test_definingUnit() async {
     String pathA = '/project/000/1111/a.dart';
     String pathB = '/project/000/1111/b.dart';
     String pathC = '/project/000/1111/22/c.dart';
@@ -51,11 +50,11 @@
     _performAnalysis();
     // perform refactoring
     _createRefactoring('/project/000/1111/22/new_name.dart');
-    return _assertSuccessfulRefactoring().then((_) {
-      assertNoFileChange(pathA);
-      assertFileChangeResult(pathB, "import '22/new_name.dart';");
-      assertNoFileChange(pathC);
-      assertFileChangeResult(testFile, '''
+    await _assertSuccessfulRefactoring();
+    assertNoFileChange(pathA);
+    assertFileChangeResult(pathB, "import '22/new_name.dart';");
+    assertNoFileChange(pathC);
+    assertFileChangeResult(testFile, '''
 library lib;
 import 'dart:math';
 import 'c.dart';
@@ -63,10 +62,9 @@
 part '../a.dart';
 part '/absolute/uri.dart';
 ''');
-    });
   }
 
-  test_importedLibrary() {
+  test_importedLibrary() async {
     String pathA = '/project/000/1111/a.dart';
     testFile = '/project/000/1111/sub/folder/test.dart';
     addSource(pathA, '''
@@ -76,15 +74,14 @@
     _performAnalysis();
     // perform refactoring
     _createRefactoring('/project/000/new/folder/name/new_name.dart');
-    return _assertSuccessfulRefactoring().then((_) {
-      assertFileChangeResult(pathA, '''
+    await _assertSuccessfulRefactoring();
+    assertFileChangeResult(pathA, '''
 import '../new/folder/name/new_name.dart';
 ''');
-      assertNoFileChange(testFile);
-    });
+    assertNoFileChange(testFile);
   }
 
-  test_importedLibrary_down() {
+  test_importedLibrary_down() async {
     String pathA = '/project/000/1111/a.dart';
     testFile = '/project/000/1111/test.dart';
     addSource(pathA, '''
@@ -94,15 +91,14 @@
     _performAnalysis();
     // perform refactoring
     _createRefactoring('/project/000/1111/22/new_name.dart');
-    return _assertSuccessfulRefactoring().then((_) {
-      assertFileChangeResult(pathA, '''
+    await _assertSuccessfulRefactoring();
+    assertFileChangeResult(pathA, '''
 import '22/new_name.dart';
 ''');
-      assertNoFileChange(testFile);
-    });
+    assertNoFileChange(testFile);
   }
 
-  test_importedLibrary_package() {
+  test_importedLibrary_package() async {
     // configure packages
     testFile = '/packages/my_pkg/aaa/test.dart';
     provider.newFile(testFile, '');
@@ -123,15 +119,14 @@
     _performAnalysis();
     // perform refactoring
     _createRefactoring('/packages/my_pkg/bbb/ccc/new_name.dart');
-    return _assertSuccessfulRefactoring().then((_) {
-      assertFileChangeResult(pathA, '''
+    await _assertSuccessfulRefactoring();
+    assertFileChangeResult(pathA, '''
 import 'package:my_pkg/bbb/ccc/new_name.dart';
 ''');
-      assertNoFileChange(testFile);
-    });
+    assertNoFileChange(testFile);
   }
 
-  test_importedLibrary_up() {
+  test_importedLibrary_up() async {
     String pathA = '/project/000/1111/a.dart';
     testFile = '/project/000/1111/22/test.dart';
     addSource(pathA, '''
@@ -141,15 +136,14 @@
     _performAnalysis();
     // perform refactoring
     _createRefactoring('/project/000/1111/new_name.dart');
-    return _assertSuccessfulRefactoring().then((_) {
-      assertFileChangeResult(pathA, '''
+    await _assertSuccessfulRefactoring();
+    assertFileChangeResult(pathA, '''
 import 'new_name.dart';
 ''');
-      assertNoFileChange(testFile);
-    });
+    assertNoFileChange(testFile);
   }
 
-  test_sourcedUnit() {
+  test_sourcedUnit() async {
     String pathA = '/project/000/1111/a.dart';
     testFile = '/project/000/1111/22/test.dart';
     addSource(pathA, '''
@@ -159,15 +153,14 @@
     _performAnalysis();
     // perform refactoring
     _createRefactoring('/project/000/1111/22/new_name.dart');
-    return _assertSuccessfulRefactoring().then((_) {
-      assertFileChangeResult(pathA, '''
+    await _assertSuccessfulRefactoring();
+    assertFileChangeResult(pathA, '''
 part '22/new_name.dart';
 ''');
-      assertNoFileChange(testFile);
-    });
+    assertNoFileChange(testFile);
   }
 
-  test_sourcedUnit_multipleLibraries() {
+  test_sourcedUnit_multipleLibraries() async {
     String pathA = '/project/000/1111/a.dart';
     String pathB = '/project/000/b.dart';
     testFile = '/project/000/1111/22/test.dart';
@@ -181,26 +174,22 @@
     _performAnalysis();
     // perform refactoring
     _createRefactoring('/project/000/1111/22/new_name.dart');
-    return _assertSuccessfulRefactoring().then((_) {
-      assertFileChangeResult(pathA, '''
+    await _assertSuccessfulRefactoring();
+    assertFileChangeResult(pathA, '''
 part '22/new_name.dart';
 ''');
-      assertFileChangeResult(pathB, '''
+    assertFileChangeResult(pathB, '''
 part '1111/22/new_name.dart';
 ''');
-      assertNoFileChange(testFile);
-    });
+    assertNoFileChange(testFile);
   }
 
   /**
    * Checks that all conditions are OK.
    */
-  Future _assertSuccessfulRefactoring() {
-    return assertRefactoringConditionsOK().then((_) {
-      return refactoring.createChange().then((SourceChange refactoringChange) {
-        this.refactoringChange = refactoringChange;
-      });
-    });
+  Future _assertSuccessfulRefactoring() async {
+    await assertRefactoringConditionsOK();
+    refactoringChange = await refactoring.createChange();
   }
 
   void _createRefactoring(String newName) {
diff --git a/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart b/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
index 63b8f83..fe91fd6 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_class_member_test.dart
@@ -5,6 +5,7 @@
 library test.services.refactoring.rename_class_member;
 
 import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:unittest/unittest.dart';
 
 import '../../reflective_tests.dart';
@@ -19,7 +20,7 @@
 
 @reflectiveTest
 class RenameClassMemberTest extends RenameRefactoringTest {
-  test_checkFinalConditions_hasMember_MethodElement() {
+  test_checkFinalConditions_hasMember_MethodElement() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -29,16 +30,15 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Class 'A' already declares method with name 'newName'.",
-          expectedContextSearch: 'newName() {} // existing');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Class 'A' already declares method with name 'newName'.",
+        expectedContextSearch: 'newName() {} // existing');
   }
 
-  test_checkFinalConditions_OK_noShadow() {
+  test_checkFinalConditions_OK_noShadow() async {
     indexTestUnit('''
 class A {
   int newName;
@@ -55,12 +55,11 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
-  test_checkFinalConditions_shadowed_byLocal_inSameClass() {
+  test_checkFinalConditions_shadowed_byLocal_inSameClass() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -73,17 +72,16 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage:
-              "Usage of renamed method will be shadowed by local variable 'newName'.",
-          expectedContextSearch: 'test(); // marker');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage:
+            "Usage of renamed method will be shadowed by local variable 'newName'.",
+        expectedContextSearch: 'test(); // marker');
   }
 
-  test_checkFinalConditions_shadowed_byLocal_inSubClass() {
+  test_checkFinalConditions_shadowed_byLocal_inSubClass() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -98,17 +96,16 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage:
-              "Usage of renamed method will be shadowed by local variable 'newName'.",
-          expectedContextSearch: 'test(); // marker');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage:
+            "Usage of renamed method will be shadowed by local variable 'newName'.",
+        expectedContextSearch: 'test(); // marker');
   }
 
-  test_checkFinalConditions_shadowed_byLocal_OK_qualifiedReference() {
+  test_checkFinalConditions_shadowed_byLocal_OK_qualifiedReference() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -121,12 +118,11 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
-  test_checkFinalConditions_shadowed_byLocal_OK_renamedNotUsed() {
+  test_checkFinalConditions_shadowed_byLocal_OK_renamedNotUsed() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -138,12 +134,11 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
-  test_checkFinalConditions_shadowed_byParameter_inSameClass() {
+  test_checkFinalConditions_shadowed_byParameter_inSameClass() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -155,17 +150,16 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage:
-              "Usage of renamed method will be shadowed by parameter 'newName'.",
-          expectedContextSearch: 'test(); // marker');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage:
+            "Usage of renamed method will be shadowed by parameter 'newName'.",
+        expectedContextSearch: 'test(); // marker');
   }
 
-  test_checkFinalConditions_shadowed_inSubClass() {
+  test_checkFinalConditions_shadowed_inSubClass() async {
     indexTestUnit('''
 class A {
   newName() {} // marker
@@ -180,16 +174,15 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Renamed method will shadow method 'A.newName'.",
-          expectedContextSearch: 'newName() {} // marker');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Renamed method will shadow method 'A.newName'.",
+        expectedContextSearch: 'newName() {} // marker');
   }
 
-  test_checkFinalConditions_shadowsSuper_inSubClass_FieldElement() {
+  test_checkFinalConditions_shadowsSuper_inSubClass_FieldElement() async {
     indexTestUnit('''
 class A {
   int newName; // marker
@@ -206,16 +199,15 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Renamed method will shadow field 'A.newName'.",
-          expectedContextSearch: 'newName; // marker');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Renamed method will shadow field 'A.newName'.",
+        expectedContextSearch: 'newName; // marker');
   }
 
-  test_checkFinalConditions_shadowsSuper_MethodElement() {
+  test_checkFinalConditions_shadowsSuper_MethodElement() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -230,16 +222,15 @@
     createRenameRefactoringAtString('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Renamed method will be shadowed by method 'B.newName'.",
-          expectedContextSearch: 'newName() {} // marker');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Renamed method will be shadowed by method 'B.newName'.",
+        expectedContextSearch: 'newName() {} // marker');
   }
 
-  test_checkInitialConditions_operator() {
+  test_checkInitialConditions_operator() async {
     indexTestUnit('''
 class A {
   operator -(other) => this;
@@ -248,9 +239,8 @@
     createRenameRefactoringAtString('-(other)');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkInitialConditions().then((status) {
-      assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
-    });
+    RefactoringStatus status = await refactoring.checkInitialConditions();
+    assertRefactoringStatus(status, RefactoringProblemSeverity.FATAL);
   }
 
   test_checkNewName_FieldElement() {
@@ -539,7 +529,7 @@
 ''');
   }
 
-  test_createChange_MethodElement_potential() {
+  test_createChange_MethodElement_potential() async {
     indexTestUnit('''
 class A {
   test() {}
@@ -556,7 +546,7 @@
     expect(refactoring.oldName, 'test');
     refactoring.newName = 'newName';
     // validate change
-    return assertSuccessfulRefactoring('''
+    await assertSuccessfulRefactoring('''
 class A {
   newName() {}
 }
@@ -565,12 +555,11 @@
   new A().newName();
   a.newName(); // 2
 }
-''').then((_) {
-      assertPotentialEdits(['test(); // 1', 'test(); // 2']);
-    });
+''');
+    assertPotentialEdits(['test(); // 1', 'test(); // 2']);
   }
 
-  test_createChange_MethodElement_potential_private_otherLibrary() {
+  test_createChange_MethodElement_potential_private_otherLibrary() async {
     indexUnit('/lib.dart', '''
 library lib;
 main(p) {
@@ -592,7 +581,7 @@
     expect(refactoring.oldName, '_test');
     refactoring.newName = 'newName';
     // validate change
-    return assertSuccessfulRefactoring('''
+    await assertSuccessfulRefactoring('''
 class A {
   newName() {}
 }
@@ -600,9 +589,8 @@
   a.newName();
   new A().newName();
 }
-''').then((_) {
-      assertNoFileChange('/lib.dart');
-    });
+''');
+    assertNoFileChange('/lib.dart');
   }
 
   test_createChange_PropertyAccessorElement_getter() {
diff --git a/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart b/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
index 90b6153..2d01a48 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_constructor_test.dart
@@ -5,6 +5,7 @@
 library test.services.refactoring.rename_constructor;
 
 import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:analyzer/src/generated/ast.dart';
 import 'package:analyzer/src/generated/element.dart';
 import 'package:unittest/unittest.dart';
@@ -21,7 +22,7 @@
 
 @reflectiveTest
 class RenameConstructorTest extends RenameRefactoringTest {
-  test_checkFinalConditions_hasMember_constructor() {
+  test_checkFinalConditions_hasMember_constructor() async {
     indexTestUnit('''
 class A {
   A.test() {}
@@ -31,16 +32,15 @@
     _createConstructorDeclarationRefactoring('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Class 'A' already declares constructor with name 'newName'.",
-          expectedContextSearch: 'newName() {} // existing');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Class 'A' already declares constructor with name 'newName'.",
+        expectedContextSearch: 'newName() {} // existing');
   }
 
-  test_checkFinalConditions_hasMember_method() {
+  test_checkFinalConditions_hasMember_method() async {
     indexTestUnit('''
 class A {
   A.test() {}
@@ -50,13 +50,12 @@
     _createConstructorDeclarationRefactoring('test() {}');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Class 'A' already declares method with name 'newName'.",
-          expectedContextSearch: 'newName() {} // existing');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Class 'A' already declares method with name 'newName'.",
+        expectedContextSearch: 'newName() {} // existing');
   }
 
   test_checkNewName() {
diff --git a/pkg/analysis_server/test/services/refactoring/rename_library_test.dart b/pkg/analysis_server/test/services/refactoring/rename_library_test.dart
index 552ff56..4df587d 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_library_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_library_test.dart
@@ -46,7 +46,7 @@
         expectedMessage: "The new name must be different than the current name.");
   }
 
-  test_createChange() {
+  test_createChange() async {
     Source unitSource = addSource('/part.dart', '''
 part of my.app;
 ''');
@@ -63,14 +63,13 @@
     expect(refactoring.elementKindName, 'library');
     refactoring.newName = 'the.new.name';
     // validate change
-    return assertSuccessfulRefactoring('''
+    await assertSuccessfulRefactoring('''
 library the.new.name;
 part 'part.dart';
-''').then((_) {
-      assertFileChangeResult('/part.dart', '''
+''');
+    assertFileChangeResult('/part.dart', '''
 part of the.new.name;
 ''');
-    });
   }
 
   void _createRenameRefactoring() {
diff --git a/pkg/analysis_server/test/services/refactoring/rename_local_test.dart b/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
index 8cb9478..92c67d9 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_local_test.dart
@@ -9,6 +9,7 @@
 
 import '../../reflective_tests.dart';
 import 'abstract_rename.dart';
+import 'package:analysis_server/src/services/correction/status.dart';
 
 
 main() {
@@ -19,7 +20,7 @@
 
 @reflectiveTest
 class RenameLocalTest extends RenameRefactoringTest {
-  test_checkFinalConditions_hasLocalFunction_after() {
+  test_checkFinalConditions_hasLocalFunction_after() async {
     indexTestUnit('''
 main() {
   int test = 0;
@@ -29,16 +30,15 @@
     createRenameRefactoringAtString('test = 0');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Duplicate function 'newName'.",
-          expectedContextSearch: 'newName() => 1');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Duplicate function 'newName'.",
+        expectedContextSearch: 'newName() => 1');
   }
 
-  test_checkFinalConditions_hasLocalFunction_before() {
+  test_checkFinalConditions_hasLocalFunction_before() async {
     indexTestUnit('''
 main() {
   newName() => 1;
@@ -48,15 +48,14 @@
     createRenameRefactoringAtString('test = 0');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Duplicate function 'newName'.");
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Duplicate function 'newName'.");
   }
 
-  test_checkFinalConditions_hasLocalVariable_after() {
+  test_checkFinalConditions_hasLocalVariable_after() async {
     indexTestUnit('''
 main() {
   int test = 0;
@@ -67,17 +66,16 @@
     createRenameRefactoringAtString('test = 0');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      expect(status.problems, hasLength(1));
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Duplicate local variable 'newName'.",
-          expectedContextSearch: 'newName = 1;');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    expect(status.problems, hasLength(1));
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Duplicate local variable 'newName'.",
+        expectedContextSearch: 'newName = 1;');
   }
 
-  test_checkFinalConditions_hasLocalVariable_before() {
+  test_checkFinalConditions_hasLocalVariable_before() async {
     indexTestUnit('''
 main() {
   var newName = 1;
@@ -87,13 +85,12 @@
     createRenameRefactoringAtString('test = 0');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Duplicate local variable 'newName'.",
-          expectedContextSearch: 'newName = 1;');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Duplicate local variable 'newName'.",
+        expectedContextSearch: 'newName = 1;');
   }
 
   test_checkFinalConditions_hasLocalVariable_otherBlock() {
@@ -128,7 +125,7 @@
     return assertRefactoringConditionsOK();
   }
 
-  test_checkFinalConditions_shadows_classMember() {
+  test_checkFinalConditions_shadows_classMember() async {
     indexTestUnit('''
 class A {
   var newName = 1;
@@ -141,14 +138,13 @@
     createRenameRefactoringAtString('test = 0');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: 'Usage of field "A.newName" declared in "test.dart" '
-              'will be shadowed by renamed local variable.',
-          expectedContextSearch: 'newName);');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: 'Usage of field "A.newName" declared in "test.dart" '
+            'will be shadowed by renamed local variable.',
+        expectedContextSearch: 'newName);');
   }
 
   test_checkFinalConditions_shadows_classMemberOK_qualifiedReference() {
@@ -181,7 +177,7 @@
     return assertRefactoringFinalConditionsOK();
   }
 
-  test_checkFinalConditions_shadows_topLevelFunction() {
+  test_checkFinalConditions_shadows_topLevelFunction() async {
     indexTestUnit('''
 newName() {}
 main() {
@@ -192,12 +188,11 @@
     createRenameRefactoringAtString('test = 0');
     // check status
     refactoring.newName = 'newName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedContextSearch: 'newName(); // ref');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedContextSearch: 'newName(); // ref');
   }
 
   test_checkNewName_FunctionElement() {
@@ -416,7 +411,7 @@
 ''');
   }
 
-  test_createChange_parameter_namedInOtherFile() {
+  test_createChange_parameter_namedInOtherFile() async {
     indexTestUnit('''
 class A {
   A({test});
@@ -433,18 +428,17 @@
     expect(refactoring.refactoringName, 'Rename Parameter');
     refactoring.newName = 'newName';
     // validate change
-    return assertSuccessfulRefactoring('''
+    await assertSuccessfulRefactoring('''
 class A {
   A({newName});
 }
-''').then((_) {
-      assertFileChangeResult('/test2.dart', '''
+''');
+    assertFileChangeResult('/test2.dart', '''
 import 'test.dart';
 main() {
   new A(newName: 2);
 }
 ''');
-    });
   }
 
   test_oldName() {
diff --git a/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart b/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
index 803e9ba..1754975 100644
--- a/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
+++ b/pkg/analysis_server/test/services/refactoring/rename_unit_member_test.dart
@@ -5,6 +5,7 @@
 library test.services.refactoring.rename_unit_member;
 
 import 'package:analysis_server/src/protocol.dart';
+import 'package:analysis_server/src/services/correction/status.dart';
 import 'package:unittest/unittest.dart';
 
 import '../../reflective_tests.dart';
@@ -19,7 +20,7 @@
 
 @reflectiveTest
 class RenameUnitMemberTest extends RenameRefactoringTest {
-  test_checkFinalConditions_hasTopLevel_ClassElement() {
+  test_checkFinalConditions_hasTopLevel_ClassElement() async {
     indexTestUnit('''
 class Test {}
 class NewName {} // existing
@@ -27,16 +28,15 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Library already declares class with name 'NewName'.",
-          expectedContextSearch: 'NewName {} // existing');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Library already declares class with name 'NewName'.",
+        expectedContextSearch: 'NewName {} // existing');
   }
 
-  test_checkFinalConditions_hasTopLevel_FunctionTypeAliasElement() {
+  test_checkFinalConditions_hasTopLevel_FunctionTypeAliasElement() async {
     indexTestUnit('''
 class Test {}
 typedef NewName(); // existing
@@ -44,17 +44,16 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage:
-              "Library already declares function type alias with name 'NewName'.",
-          expectedContextSearch: 'NewName(); // existing');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage:
+            "Library already declares function type alias with name 'NewName'.",
+        expectedContextSearch: 'NewName(); // existing');
   }
 
-  test_checkFinalConditions_OK_qualifiedSuper_MethodElement() {
+  test_checkFinalConditions_OK_qualifiedSuper_MethodElement() async {
     indexTestUnit('''
 class Test {}
 class A {
@@ -69,12 +68,11 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
-  test_checkFinalConditions_shadowedBy_MethodElement() {
+  test_checkFinalConditions_shadowedBy_MethodElement() async {
     indexTestUnit('''
 class Test {}
 class A {
@@ -87,17 +85,16 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage:
-              "Reference to renamed class will be shadowed by method 'A.NewName'.",
-          expectedContextSearch: 'NewName() {}');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage:
+            "Reference to renamed class will be shadowed by method 'A.NewName'.",
+        expectedContextSearch: 'NewName() {}');
   }
 
-  test_checkFinalConditions_shadowsInSubClass_importedLib() {
+  test_checkFinalConditions_shadowsInSubClass_importedLib() async {
     indexTestUnit('''
 class Test {}
 ''');
@@ -116,15 +113,18 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Renamed class will shadow method 'A.NewName'.");
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Renamed class will shadow method 'A.NewName'.");
   }
 
-  test_checkFinalConditions_shadowsInSubClass_importedLib_hideCombinator() {
+
+
+
+
+      test_checkFinalConditions_shadowsInSubClass_importedLib_hideCombinator() async {
     indexTestUnit('''
 class Test {}
 ''');
@@ -143,12 +143,11 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
-  test_checkFinalConditions_shadowsInSubClass_MethodElement() {
+  test_checkFinalConditions_shadowsInSubClass_MethodElement() async {
     indexTestUnit('''
 class Test {}
 class A {
@@ -163,16 +162,15 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatus(
-          status,
-          RefactoringProblemSeverity.ERROR,
-          expectedMessage: "Renamed class will shadow method 'A.NewName'.",
-          expectedContextSearch: 'NewName(); // super-ref');
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatus(
+        status,
+        RefactoringProblemSeverity.ERROR,
+        expectedMessage: "Renamed class will shadow method 'A.NewName'.",
+        expectedContextSearch: 'NewName(); // super-ref');
   }
 
-  test_checkFinalConditions_shadowsInSubClass_notImportedLib() {
+  test_checkFinalConditions_shadowsInSubClass_notImportedLib() async {
     indexUnit('/lib.dart', '''
 library my.lib;
 class A {
@@ -190,12 +188,11 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
-  test_checkFinalConditions_shadowsInSubClass_notSubClass() {
+  test_checkFinalConditions_shadowsInSubClass_notSubClass() async {
     indexTestUnit('''
 class Test {}
 class A {
@@ -210,9 +207,8 @@
     createRenameRefactoringAtString('Test {}');
     // check status
     refactoring.newName = 'NewName';
-    return refactoring.checkFinalConditions().then((status) {
-      assertRefactoringStatusOK(status);
-    });
+    RefactoringStatus status = await refactoring.checkFinalConditions();
+    assertRefactoringStatusOK(status);
   }
 
   test_checkNewName_ClassElement() {
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index 37c4ea7..b34defb 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -6,7 +6,7 @@
   </head>
   <body>
     <h1>Analysis Server API Specification</h1>
-    <h1 style="color:#999999">Version <version>1.0.0</version></h1>
+    <h1 style="color:#999999">Version <version>1.2.0</version></h1>
     <p>
       This document contains a specification of the API provided by the
       analysis server.  The API in this document is currently under
@@ -468,11 +468,26 @@
       </request>
       <request method="reanalyze">
         <p>
-          Force the re-analysis of everything contained in the
-          existing analysis roots. This will cause all previously
-          computed analysis results to be discarded and recomputed,
-          and will cause all subscribed notifications to be re-sent.
+          Force the re-analysis of everything contained in the specified
+          analysis roots. This will cause all previously computed analysis
+          results to be discarded and recomputed, and will cause all subscribed
+          notifications to be re-sent.
         </p>
+        <p>
+          If no analysis roots are provided, then all current analysis roots
+          will be re-analyzed. If an empty list of analysis roots is provided,
+          then nothing will be re-analyzed. If the list contains one or more
+          paths that are not currently analysis roots, then an error of type
+          <tt>INVALID_ANALYSIS_ROOT</tt> will be generated.
+        </p>
+        <params>
+          <field name="roots" optional="true">
+            <list><ref>FilePath</ref></list>
+            <p>
+              A list of the analysis roots that are to be re-analyzed.
+            </p>
+          </field>
+        </params>
       </request>
       <request method="setAnalysisRoots">
         <p>
@@ -1285,7 +1300,9 @@
           text is passed in so that the selection can be preserved across the
           formatting operation. The updated selection will be as close to
           matching the original as possible, but whitespace at the beginning or
-          end of the selected region will be ignored.
+          end of the selected region will be ignored. If preserving selection
+          information is not required, zero (0) can be specified for both the
+          selection offset and selection length.
         </p>
         <p>
           If a request is made for a file which does not exist, or
@@ -1304,17 +1321,13 @@
           <field name="selectionOffset">
             <ref>int</ref>
             <p>
-              The offset of the current selection in the file. In case
-              preserving, selection information is not required, 0 can be
-              specified for both selection offset and length.
+              The offset of the current selection in the file.
             </p>
           </field>
           <field name="selectionLength">
             <ref>int</ref>
             <p>
-              The length of the current selection in the file. In case
-              preserving, selection information is not required, 0 can be
-              specified for both selection offset and length.
+              The length of the current selection in the file.
             </p>
           </field>
           <!--
@@ -1626,7 +1639,8 @@
           <field name="contextRoot">
             <ref>FilePath</ref>
             <p>
-              The path of the Dart or HTML file that will be launched.
+              The path of the Dart or HTML file that will be launched, or the
+              path of the directory containing the file.
             </p>
           </field>
         </params>
@@ -1662,25 +1676,27 @@
           context.
         </p>
         <p>
-          Exactly one of the file and uri fields must be provided.
+          Exactly one of the file and uri fields must be provided. If both
+          fields are provided, then an error of type <tt>INVALID_PARAMETER</tt>
+          will be generated. Similarly, if neither field is provided, then an
+          error of type <tt>INVALID_PARAMETER</tt> will be generated.
         </p>
         <p>
           If the file field is provided and the value is not the path of a file
           (either the file does not exist or the path references something other
-          than a file), then an error of type <tt>MAP_URI_INVALID_FILE</tt> will
+          than a file), then an error of type <tt>INVALID_PARAMETER</tt> will
           be generated.
         </p>
         <p>
           If the uri field is provided and the value is not a valid URI or if
           the URI references something that is not a file (either a file that
           does not exist or something other than a file), then an error of type
-          <tt>MAP_URI_INVALID_URI</tt> will be generated.
+          <tt>INVALID_PARAMETER</tt> will be generated.
         </p>
         <p>
-          If the contextRoot used to create the execution context is not a file
-          (either the file does not exist or the path references something other
-          than a file), then an error of type <tt>INVALID_EXECUTION_CONTEXT</tt>
-          will be generated.
+          If the contextRoot used to create the execution context does not
+          exist, then an error of type <tt>INVALID_EXECUTION_CONTEXT</tt> will
+          be generated.
         </p>
         <params>
           <field name="id">
@@ -3072,6 +3088,13 @@
             </p>
           </value>
           <value>
+            <code>INVALID_EXECUTION_CONTEXT</code>
+            <p>
+              The context root used to create an execution context does not
+              exist.
+            </p>
+          </value>
+          <value>
             <code>INVALID_OVERLAY_CHANGE</code>
             <p>
               An analysis.updateContent request contained a
diff --git a/pkg/analyzer/lib/file_system/file_system.dart b/pkg/analyzer/lib/file_system/file_system.dart
index a6580f5..ff3e5ff 100644
--- a/pkg/analyzer/lib/file_system/file_system.dart
+++ b/pkg/analyzer/lib/file_system/file_system.dart
@@ -147,6 +147,9 @@
     return null;
   }
 
+  @override
+  Uri restoreAbsolute(Source source) => source.uri;
+
   /**
    * Return `true` if the given URI is a `file` URI.
    *
diff --git a/pkg/analyzer/lib/source/package_map_resolver.dart b/pkg/analyzer/lib/source/package_map_resolver.dart
index 2f70b86..badaf3a 100644
--- a/pkg/analyzer/lib/source/package_map_resolver.dart
+++ b/pkg/analyzer/lib/source/package_map_resolver.dart
@@ -49,16 +49,13 @@
     // Prepare path.
     String path = uri.path;
     // Prepare path components.
-    String pkgName;
-    String relPath;
     int index = path.indexOf('/');
     if (index == -1 || index == 0) {
       return null;
-    } else {
-      // <pkgName>/<relPath>
-      pkgName = path.substring(0, index);
-      relPath = path.substring(index + 1);
     }
+    // <pkgName>/<relPath>
+    String pkgName = path.substring(0, index);
+    String relPath = path.substring(index + 1);
     // Try to find an existing file.
     List<Folder> packageDirs = packageMap[pkgName];
     if (packageDirs != null) {
diff --git a/pkg/analyzer/lib/src/error_formatter.dart b/pkg/analyzer/lib/src/error_formatter.dart
index b64dfc6..ea0ad47 100644
--- a/pkg/analyzer/lib/src/error_formatter.dart
+++ b/pkg/analyzer/lib/src/error_formatter.dart
@@ -56,7 +56,8 @@
       out.write(escapePipe(error.message));
     } else {
       String errorType = severity.displayName;
-      if (error.errorCode.type == ErrorType.HINT) {
+      if (error.errorCode.type == ErrorType.HINT ||
+          error.errorCode.type == ErrorType.LINT) {
         errorType = error.errorCode.type.displayName;
       }
       // [warning] 'foo' is not a... (/Users/.../tmp/foo.dart, line 1, col 2)
diff --git a/pkg/analyzer/lib/src/generated/element.dart b/pkg/analyzer/lib/src/generated/element.dart
index 337dc99..ebfbcac 100644
--- a/pkg/analyzer/lib/src/generated/element.dart
+++ b/pkg/analyzer/lib/src/generated/element.dart
@@ -87,6 +87,9 @@
       identical(object, this);
 
   @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
   bool internalIsMoreSpecificThan(DartType type, bool withDynamic,
       Set<TypeImpl_TypePair> visitedTypePairs) =>
       true;
@@ -2179,6 +2182,9 @@
       identical(object, this);
 
   @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
   bool internalIsMoreSpecificThan(DartType type, bool withDynamic,
       Set<TypeImpl_TypePair> visitedTypePairs) {
     // T is S
@@ -4791,26 +4797,32 @@
   }
 
   @override
-  int get hashCode {
+  int get hashCode => internalHashCode(<DartType>[]);
+
+  @override
+  int internalHashCode(List<DartType> visitedTypes) {
     if (element == null) {
       return 0;
+    } else if (visitedTypes.contains(this)) {
+      return 3;
     }
+    visitedTypes.add(this);
     // Reference the arrays of parameters
     List<DartType> normalParameterTypes = this.normalParameterTypes;
     List<DartType> optionalParameterTypes = this.optionalParameterTypes;
     Iterable<DartType> namedParameterTypes = this.namedParameterTypes.values;
     // Generate the hashCode
-    int hashCode = returnType.hashCode;
+    int code = (returnType as TypeImpl).internalHashCode(visitedTypes);
     for (int i = 0; i < normalParameterTypes.length; i++) {
-      hashCode = (hashCode << 1) + normalParameterTypes[i].hashCode;
+      code = (code << 1) + (normalParameterTypes[i] as TypeImpl).internalHashCode(visitedTypes);
     }
     for (int i = 0; i < optionalParameterTypes.length; i++) {
-      hashCode = (hashCode << 1) + optionalParameterTypes[i].hashCode;
+      code = (code << 1) + (optionalParameterTypes[i] as TypeImpl).internalHashCode(visitedTypes);
     }
     for (DartType type in namedParameterTypes) {
-      hashCode = (hashCode << 1) + type.hashCode;
+      code = (code << 1) + (type as TypeImpl).internalHashCode(visitedTypes);
     }
-    return hashCode;
+    return code;
   }
 
   @override
@@ -4935,7 +4947,11 @@
       internalEquals(object, new HashSet<ElementPair>());
 
   @override
-  void appendTo(StringBuffer buffer) {
+  void appendTo(StringBuffer buffer, Set<DartType> visitedTypes) {
+    if (!visitedTypes.add(this)) {
+      buffer.write(name == null ? '...' : name);
+      return;
+    }
     List<DartType> normalParameterTypes = this.normalParameterTypes;
     List<DartType> optionalParameterTypes = this.optionalParameterTypes;
     Map<String, DartType> namedParameterTypes = this.namedParameterTypes;
@@ -4949,7 +4965,7 @@
         } else {
           needsComma = true;
         }
-        (type as TypeImpl).appendTo(buffer);
+        (type as TypeImpl).appendTo(buffer, visitedTypes);
       }
     }
     if (optionalParameterTypes.length > 0) {
@@ -4964,7 +4980,7 @@
         } else {
           needsComma = true;
         }
-        (type as TypeImpl).appendTo(buffer);
+        (type as TypeImpl).appendTo(buffer, visitedTypes);
       }
       buffer.write("]");
       needsComma = true;
@@ -4983,7 +4999,7 @@
         }
         buffer.write(name);
         buffer.write(": ");
-        (type as TypeImpl).appendTo(buffer);
+        (type as TypeImpl).appendTo(buffer, visitedTypes);
       });
       buffer.write("}");
       needsComma = true;
@@ -4993,7 +5009,7 @@
     if (returnType == null) {
       buffer.write("null");
     } else {
-      (returnType as TypeImpl).appendTo(buffer);
+      (returnType as TypeImpl).appendTo(buffer, visitedTypes);
     }
   }
 
@@ -6256,6 +6272,9 @@
   }
 
   @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
   List<InterfaceType> get interfaces {
     ClassElement classElement = element;
     List<InterfaceType> interfaces = classElement.interfaces;
@@ -6335,7 +6354,11 @@
       internalEquals(object, new HashSet<ElementPair>());
 
   @override
-  void appendTo(StringBuffer buffer) {
+  void appendTo(StringBuffer buffer, Set<DartType> visitedTypes) {
+    if (!visitedTypes.add(this)) {
+      buffer.write(name == null ? '...' : name);
+      return;
+    }
     buffer.write(name);
     int argumentCount = typeArguments.length;
     if (argumentCount > 0) {
@@ -6344,7 +6367,7 @@
         if (i > 0) {
           buffer.write(", ");
         }
-        (typeArguments[i] as TypeImpl).appendTo(buffer);
+        (typeArguments[i] as TypeImpl).appendTo(buffer, visitedTypes);
       }
       buffer.write(">");
     }
@@ -9985,9 +10008,14 @@
   bool get isVoid => false;
 
   /**
-   * Append a textual representation of this type to the given [buffer].
+   * Append a textual representation of this type to the given [buffer]. The set
+   * of [visitedTypes] is used to prevent infinite recusion.
    */
-  void appendTo(StringBuffer buffer) {
+  void appendTo(StringBuffer buffer, Set<DartType> visitedTypes) {
+    if (!visitedTypes.add(this)) {
+      buffer.write(name == null ? '...' : name);
+      return;
+    }
     if (name == null) {
       buffer.write("<unnamed type>");
     } else {
@@ -10000,6 +10028,8 @@
 
   bool internalEquals(Object object, Set<ElementPair> visitedElementPairs);
 
+  int internalHashCode(List<DartType> visitedTypes);
+
   bool internalIsMoreSpecificThan(DartType type, bool withDynamic,
       Set<TypeImpl_TypePair> visitedTypePairs);
 
@@ -10127,7 +10157,7 @@
   @override
   String toString() {
     StringBuffer buffer = new StringBuffer();
-    appendTo(buffer);
+    appendTo(buffer, new HashSet<DartType>());
     return buffer.toString();
   }
 
@@ -10346,6 +10376,9 @@
       this == object;
 
   @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
   bool internalIsMoreSpecificThan(DartType s, bool withDynamic,
       Set<TypeImpl_TypePair> visitedTypePairs) {
     //
@@ -10503,6 +10536,9 @@
       identical(object, this);
 
   @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
   bool internalIsMoreSpecificThan(DartType type, bool withDynamic,
       Set<TypeImpl_TypePair> visitedTypePairs) {
     // T is S
@@ -10583,6 +10619,9 @@
   int get hashCode => _types.hashCode;
 
   @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
   bool operator ==(Object other) {
     if (other == null || other is! UnionType) {
       return false;
@@ -10594,11 +10633,15 @@
   }
 
   @override
-  void appendTo(StringBuffer buffer) {
+  void appendTo(StringBuffer buffer, Set<DartType> visitedTypes) {
+    if (!visitedTypes.add(this)) {
+      buffer.write(name == null ? '...' : name);
+      return;
+    }
     String prefix = "{";
     for (DartType type in _types) {
       buffer.write(prefix);
-      (type as TypeImpl).appendTo(buffer);
+      (type as TypeImpl).appendTo(buffer, visitedTypes);
       prefix = ",";
     }
     buffer.write("}");
@@ -11087,6 +11130,9 @@
   int get hashCode => 2;
 
   @override
+  int internalHashCode(List<DartType> visitedTypes) => hashCode;
+
+  @override
   bool get isVoid => true;
 
   @override
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index af7eb92..67a7a23 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -715,17 +715,16 @@
   LineInfo getLineInfo(Source source);
 
   /**
-   * Return the modification stamp for the given source. A modification stamp is a non-negative
-   * integer with the property that if the contents of the source have not been modified since the
-   * last time the modification stamp was accessed then the same value will be returned, but if the
-   * contents of the source have been modified one or more times (even if the net change is zero)
-   * the stamps will be different.
+   * Return the modification stamp for the [source], or a negative value if the
+   * source does not exist. A modification stamp is a non-negative integer with
+   * the property that if the contents of the source have not been modified
+   * since the last time the modification stamp was accessed then the same value
+   * will be returned, but if the contents of the source have been modified one
+   * or more times (even if the net change is zero) the stamps will be different.
    *
-   * This method should be used rather than the method [Source.getModificationStamp] because
-   * contexts can have local overrides of the content of a source that the source is not aware of.
-   *
-   * @param source the source whose modification stamp is to be returned
-   * @return the modification stamp for the source
+   * This method should be used rather than the method
+   * [Source.getModificationStamp] because contexts can have local overrides of
+   * the content of a source that the source is not aware of.
    */
   int getModificationStamp(Source source);
 
@@ -1977,7 +1976,10 @@
       if (AnalysisEngine.isHtmlFileName(sourceName)) {
         return computeHtmlElement(source);
       }
-    } on AnalysisException catch (exception) {
+    } catch (exception) {
+      // If the location cannot be decoded for some reason then the underlying
+      // cause should have been logged already and we can fall though to return
+      // null.
     }
     return null;
   }
@@ -4682,6 +4684,11 @@
     CaughtException thrownException = task.exception;
     if (thrownException != null) {
       sourceEntry.recordContentError(thrownException);
+      {
+        sourceEntry.setValue(SourceEntry.CONTENT_ERRORS, task.errors);
+        ChangeNoticeImpl notice = _getNotice(source);
+        notice.setErrors(sourceEntry.allErrors, null);
+      }
       _workManager.remove(source);
       throw new AnalysisException('<rethrow>', thrownException);
     }
@@ -7870,7 +7877,8 @@
       new DataDescriptor<CompilationUnit>("DartEntry.RESOLVED_UNIT");
 
   /**
-   * The data descriptor representing the token stream.
+   * The data descriptor representing the errors resulting from scanning the
+   * source.
    */
   static final DataDescriptor<List<AnalysisError>> SCAN_ERRORS =
       new DataDescriptor<List<AnalysisError>>(
@@ -7917,6 +7925,7 @@
    */
   List<AnalysisError> get allErrors {
     List<AnalysisError> errors = new List<AnalysisError>();
+    errors.addAll(super.allErrors);
     errors.addAll(getValue(SCAN_ERRORS));
     errors.addAll(getValue(PARSE_ERRORS));
     ResolutionState state = _resolutionState;
@@ -9031,6 +9040,11 @@
   String _content;
 
   /**
+   * The errors that were produced by getting the source content.
+   */
+  final List<AnalysisError> errors = <AnalysisError>[];
+
+  /**
    * The time at which the contents of the source were last modified.
    */
   int _modificationTime = -1;
@@ -9094,6 +9108,11 @@
           _modificationTime,
           _content);
     } catch (exception, stackTrace) {
+      errors.add(
+          new AnalysisError.con1(
+              source,
+              ScannerErrorCode.UNABLE_GET_CONTENT,
+              [exception]));
       throw new AnalysisException(
           "Could not get contents of $source",
           new CaughtException(exception, stackTrace));
@@ -9165,6 +9184,7 @@
    */
   List<AnalysisError> get allErrors {
     List<AnalysisError> errors = new List<AnalysisError>();
+    errors.addAll(super.allErrors);
     errors.addAll(getValue(PARSE_ERRORS));
     errors.addAll(getValue(RESOLUTION_ERRORS));
     errors.addAll(getValue(HINTS));
@@ -10924,6 +10944,7 @@
   void setValue(DataDescriptor /*<V>*/ descriptor, dynamic /*V*/ value) {
     CachedResult result =
         resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor));
+    SourceEntry.countTransition(descriptor, result);
     result.state = CacheState.VALID;
     result.value = value == null ? descriptor.defaultValue : value;
   }
@@ -11592,6 +11613,15 @@
       new DataDescriptor<String>("SourceEntry.CONTENT");
 
   /**
+   * The data descriptor representing the errors resulting from reading the
+   * source content.
+   */
+  static final DataDescriptor<List<AnalysisError>> CONTENT_ERRORS =
+      new DataDescriptor<List<AnalysisError>>(
+          "SourceEntry.CONTENT_ERRORS",
+          AnalysisError.NO_ERRORS);
+
+  /**
    * The data descriptor representing the line information.
    */
   static final DataDescriptor<LineInfo> LINE_INFO =
@@ -11605,6 +11635,13 @@
   static int _EXPLICITLY_ADDED_FLAG = 0;
 
   /**
+   * A table mapping data descriptors to a count of the number of times a value
+   * was set when in a given state.
+   */
+  static final Map<DataDescriptor, Map<CacheState, int>> transitionMap =
+      new HashMap<DataDescriptor, Map<CacheState, int>>();
+
+  /**
    * The most recent time at which the state of the source matched the state
    * represented by this entry.
    */
@@ -11629,11 +11666,18 @@
       new HashMap<DataDescriptor, CachedResult>();
 
   /**
+   * Return all of the errors associated with this entry.
+   */
+  List<AnalysisError> get allErrors {
+    return getValue(CONTENT_ERRORS);
+  }
+
+  /**
    * Get a list of all the library-independent descriptors for which values may
    * be stored in this SourceEntry.
    */
   List<DataDescriptor> get descriptors {
-    return <DataDescriptor>[SourceEntry.CONTENT, SourceEntry.LINE_INFO];
+    return <DataDescriptor>[CONTENT, CONTENT_ERRORS, LINE_INFO];
   }
 
   /**
@@ -11732,6 +11776,7 @@
    */
   void invalidateAllInformation() {
     setState(CONTENT, CacheState.INVALID);
+    setState(CONTENT_ERRORS, CacheState.INVALID);
     setState(LINE_INFO, CacheState.INVALID);
   }
 
@@ -11794,10 +11839,23 @@
     _validateStateChange(descriptor, CacheState.VALID);
     CachedResult result =
         resultMap.putIfAbsent(descriptor, () => new CachedResult(descriptor));
+    countTransition(descriptor, result);
     result.state = CacheState.VALID;
     result.value = value == null ? descriptor.defaultValue : value;
   }
 
+  /**
+   * Increment the count of the number of times that data represented by the
+   * given [descriptor] was transitioned from the current state (as found in the
+   * given [result] to a valid state.
+   */
+  static void countTransition(DataDescriptor descriptor, CachedResult result) {
+    Map<CacheState, int> countMap =
+        transitionMap.putIfAbsent(descriptor, () => new HashMap<CacheState, int>());
+    int count = countMap[result.state];
+    countMap[result.state] = count == null ? 1 : count + 1;
+  }
+
   @override
   String toString() {
     StringBuffer buffer = new StringBuffer();
@@ -11826,7 +11884,9 @@
    * Return `true` if the [descriptor] is valid for this entry.
    */
   bool _isValidDescriptor(DataDescriptor descriptor) {
-    return descriptor == CONTENT || descriptor == LINE_INFO;
+    return descriptor == CONTENT ||
+        descriptor == CONTENT_ERRORS ||
+        descriptor == LINE_INFO;
   }
 
   /**
@@ -11887,6 +11947,12 @@
     }
     needsSeparator =
         _writeStateDiffOn(buffer, needsSeparator, "content", CONTENT, oldEntry);
+    needsSeparator = _writeStateDiffOn(
+        buffer,
+        needsSeparator,
+        "contentErrors",
+        CONTENT_ERRORS,
+        oldEntry);
     needsSeparator =
         _writeStateDiffOn(buffer, needsSeparator, "lineInfo", LINE_INFO, oldEntry);
     return needsSeparator;
@@ -11900,6 +11966,7 @@
     buffer.write("time = ");
     buffer.write(modificationTime);
     _writeStateOn(buffer, "content", CONTENT);
+    _writeStateOn(buffer, "contentErrors", CONTENT_ERRORS);
     _writeStateOn(buffer, "lineInfo", LINE_INFO);
   }
 
diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
index 038d0f0..ef8b807 100644
--- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
@@ -1170,8 +1170,8 @@
   List<AnalysisError> _newScanErrors = <AnalysisError>[];
   List<AnalysisError> _newParseErrors = <AnalysisError>[];
 
-  PoorMansIncrementalResolver(this._typeProvider, this._unitSource,
-      this._entry, this._oldUnit, bool resolveApiChanges) {
+  PoorMansIncrementalResolver(this._typeProvider, this._unitSource, this._entry,
+      this._oldUnit, bool resolveApiChanges) {
     _resolveApiChanges = resolveApiChanges;
   }
 
@@ -1426,21 +1426,29 @@
   }
 
   static _TokenDifferenceKind _compareToken(Token oldToken, Token newToken,
-      int delta) {
-    if (oldToken == null && newToken == null) {
-      return null;
-    }
-    if (oldToken == null || newToken == null) {
-      return _TokenDifferenceKind.CONTENT;
-    }
-    if (oldToken.type != newToken.type) {
-      return _TokenDifferenceKind.CONTENT;
-    }
-    if (oldToken.lexeme != newToken.lexeme) {
-      return _TokenDifferenceKind.CONTENT;
-    }
-    if (newToken.offset - oldToken.offset != delta) {
-      return _TokenDifferenceKind.OFFSET;
+      int delta, bool forComment) {
+    while (true) {
+      if (oldToken == null && newToken == null) {
+        return null;
+      }
+      if (oldToken == null || newToken == null) {
+        return _TokenDifferenceKind.CONTENT;
+      }
+      if (oldToken.type != newToken.type) {
+        return _TokenDifferenceKind.CONTENT;
+      }
+      if (oldToken.lexeme != newToken.lexeme) {
+        return _TokenDifferenceKind.CONTENT;
+      }
+      if (newToken.offset - oldToken.offset != delta) {
+        return _TokenDifferenceKind.OFFSET;
+      }
+      // continue if comment tokens are being checked
+      if (!forComment) {
+        break;
+      }
+      oldToken = oldToken.next;
+      newToken = newToken.next;
     }
     return null;
   }
@@ -1457,7 +1465,7 @@
       {
         Token oldComment = oldToken.precedingComments;
         Token newComment = newToken.precedingComments;
-        if (_compareToken(oldComment, newComment, 0) != null) {
+        if (_compareToken(oldComment, newComment, 0, true) != null) {
           _TokenDifferenceKind diffKind = _TokenDifferenceKind.COMMENT;
           if (oldComment is DocumentationCommentToken ||
               newComment is DocumentationCommentToken) {
@@ -1467,7 +1475,8 @@
         }
       }
       // compare tokens
-      _TokenDifferenceKind diffKind = _compareToken(oldToken, newToken, 0);
+      _TokenDifferenceKind diffKind =
+          _compareToken(oldToken, newToken, 0, false);
       if (diffKind != null) {
         return new _TokenPair(diffKind, oldToken, newToken);
       }
@@ -1483,7 +1492,8 @@
     int delta = newToken.offset - oldToken.offset;
     while (oldToken.previous != oldToken && newToken.previous != newToken) {
       // compare tokens
-      _TokenDifferenceKind diffKind = _compareToken(oldToken, newToken, delta);
+      _TokenDifferenceKind diffKind =
+          _compareToken(oldToken, newToken, delta, false);
       if (diffKind != null) {
         return new _TokenPair(diffKind, oldToken.next, newToken.next);
       }
@@ -1491,7 +1501,7 @@
       {
         Token oldComment = oldToken.precedingComments;
         Token newComment = newToken.precedingComments;
-        if (_compareToken(oldComment, newComment, delta) != null) {
+        if (_compareToken(oldComment, newComment, delta, true) != null) {
           _TokenDifferenceKind diffKind = _TokenDifferenceKind.COMMENT;
           if (oldComment is DocumentationCommentToken ||
               newComment is DocumentationCommentToken) {
diff --git a/pkg/analyzer/lib/src/generated/java_io.dart b/pkg/analyzer/lib/src/generated/java_io.dart
index 5e900d1..e947d80a 100644
--- a/pkg/analyzer/lib/src/generated/java_io.dart
+++ b/pkg/analyzer/lib/src/generated/java_io.dart
@@ -71,9 +71,11 @@
     return _newFile().existsSync();
   }
   int lastModified() {
-    if (!_newFile().existsSync()) return 0;
-    return _newFile().lastModifiedSync().millisecondsSinceEpoch;
-
+    try {
+      return _newFile().lastModifiedSync().millisecondsSinceEpoch;
+    } catch (exception) {
+      return -1;
+    }
   }
   List<JavaFile> listFiles() {
     var files = <JavaFile>[];
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 9f3ab6d..a40c7e6 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -598,7 +598,7 @@
     }
     // Check the block for a return statement, if not, create the hint
     BlockFunctionBody blockFunctionBody = body as BlockFunctionBody;
-    if (!blockFunctionBody.accept(new ExitDetector())) {
+    if (!ExitDetector.exits(blockFunctionBody)) {
       _errorReporter.reportErrorForNode(
           HintCode.MISSING_RETURN,
           returnType,
@@ -4331,6 +4331,13 @@
     }
     return false;
   }
+
+  /**
+   * Return `true` if the given [node] exits.
+   */
+  static bool exits(AstNode node) {
+    return new ExitDetector()._nodeExits(node);
+  }
 }
 
 /**
@@ -7962,7 +7969,7 @@
     }
     LibraryElement asyncElement = _asyncLibrary.libraryElement;
     if (asyncElement == null) {
-      throw new AnalysisException("Coulb not resolve dart:async");
+      throw new AnalysisException("Could not resolve dart:async");
     }
     _buildDirectiveModels();
     _typeProvider = new TypeProviderImpl(coreElement, asyncElement);
diff --git a/pkg/analyzer/lib/src/generated/scanner.dart b/pkg/analyzer/lib/src/generated/scanner.dart
index 436a5bb..29ec7cc 100644
--- a/pkg/analyzer/lib/src/generated/scanner.dart
+++ b/pkg/analyzer/lib/src/generated/scanner.dart
@@ -1816,6 +1816,9 @@
   static const ScannerErrorCode MISSING_QUOTE =
       const ScannerErrorCode('MISSING_QUOTE', "Expected quote (' or \")");
 
+  static const ScannerErrorCode UNABLE_GET_CONTENT =
+      const ScannerErrorCode('UNABLE_GET_CONTENT', "Unable to get content: {0}");
+
   static const ScannerErrorCode UNTERMINATED_MULTI_LINE_COMMENT =
       const ScannerErrorCode(
           'UNTERMINATED_MULTI_LINE_COMMENT',
diff --git a/pkg/analyzer/lib/src/generated/source.dart b/pkg/analyzer/lib/src/generated/source.dart
index 01bff35..7c48e3c 100644
--- a/pkg/analyzer/lib/src/generated/source.dart
+++ b/pkg/analyzer/lib/src/generated/source.dart
@@ -439,17 +439,16 @@
   bool get isInSystemLibrary;
 
   /**
-   * Return the modification stamp for this source. A modification stamp is a non-negative integer
-   * with the property that if the contents of the source have not been modified since the last time
-   * the modification stamp was accessed then the same value will be returned, but if the contents
-   * of the source have been modified one or more times (even if the net change is zero) the stamps
-   * will be different.
+   * Return the modification stamp for this source, or a negative value if the
+   * source does not exist. A modification stamp is a non-negative integer with
+   * the property that if the contents of the source have not been modified
+   * since the last time the modification stamp was accessed then the same value
+   * will be returned, but if the contents of the source have been modified one
+   * or more times (even if the net change is zero) the stamps will be different.
    *
    * Clients should consider using the the method
-   * [AnalysisContext.getModificationStamp] because contexts can have local overrides
-   * of the content of a source that the source is not aware of.
-   *
-   * @return the modification stamp for this source
+   * [AnalysisContext.getModificationStamp] because contexts can have local
+   * overrides of the content of a source that the source is not aware of.
    */
   int get modificationStamp;
 
@@ -662,7 +661,7 @@
   Source fromEncoding(String encoding) {
     Source source = forUri(encoding);
     if (source == null) {
-      throw new IllegalArgumentException("Invalid source encoding: $encoding");
+      throw new IllegalArgumentException("Invalid source encoding: '$encoding'");
     }
     return source;
   }
diff --git a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
index 7b461df..da3e457 100644
--- a/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
+++ b/pkg/analyzer/lib/src/generated/static_type_analyzer.dart
@@ -1433,7 +1433,8 @@
       type = _dynamicType;
     }
     if (body.isAsynchronous) {
-      return _typeProvider.futureType.substitute4(<DartType>[type]);
+      return _typeProvider.futureType.substitute4(
+          <DartType>[flattenFutures(_typeProvider, type)]);
     } else {
       return type;
     }
diff --git a/pkg/analyzer/lib/task/model.dart b/pkg/analyzer/lib/task/model.dart
index a00b215..3fd8ddb 100644
--- a/pkg/analyzer/lib/task/model.dart
+++ b/pkg/analyzer/lib/task/model.dart
@@ -49,6 +49,12 @@
  */
 abstract class AnalysisTask {
   /**
+   * A table mapping the types of analysis tasks to the number of times each
+   * kind of task has been performed.
+   */
+  static final Map<Type, int> countMap = new HashMap<Type, int>();
+
+  /**
    * A table mapping the types of analysis tasks to stopwatches used to compute
    * how much time was spent executing each kind of task.
    */
@@ -164,6 +170,8 @@
    */
   void _safelyPerform() {
     try {
+      int count = countMap[runtimeType];
+      countMap[runtimeType] = count == null ? 1 : count + 1;
       Stopwatch stopwatch = stopwatchMap[runtimeType];
       if (stopwatch == null) {
         stopwatch = new Stopwatch();
diff --git a/pkg/analyzer/pubspec.yaml b/pkg/analyzer/pubspec.yaml
index 6c686bb..e0c1fe4 100644
--- a/pkg/analyzer/pubspec.yaml
+++ b/pkg/analyzer/pubspec.yaml
@@ -1,5 +1,5 @@
 name: analyzer
-version: 0.23.0
+version: 0.24.0-dev.1
 author: Dart Team <misc@dartlang.org>
 description: Static analyzer for Dart.
 homepage: http://www.dartlang.org
diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart
index e4e8404..18af984 100644
--- a/pkg/analyzer/test/generated/all_the_rest_test.dart
+++ b/pkg/analyzer/test/generated/all_the_rest_test.dart
@@ -7342,9 +7342,8 @@
   }
 
   void _assertHasReturn(bool expectedResult, String source) {
-    ExitDetector detector = new ExitDetector();
     Statement statement = ParserTestCase.parseStatement(source);
-    expect(statement.accept(detector), same(expectedResult));
+    expect(ExitDetector.exits(statement), expectedResult);
   }
 
   void _assertTrue(String source) {
@@ -7378,7 +7377,7 @@
     FunctionDeclaration function = unit.declarations.last;
     BlockFunctionBody body = function.functionExpression.body;
     Statement statement = body.block.statements[1];
-    expect(statement.accept(new ExitDetector()), false);
+    expect(ExitDetector.exits(statement), false);
   }
 
   void test_switch_withEnum_false_withDefault() {
@@ -7400,7 +7399,7 @@
     FunctionDeclaration function = unit.declarations.last;
     BlockFunctionBody body = function.functionExpression.body;
     Statement statement = body.block.statements[1];
-    expect(statement.accept(new ExitDetector()), false);
+    expect(ExitDetector.exits(statement), false);
   }
 
   void test_switch_withEnum_true_noDefault() {
@@ -7420,7 +7419,7 @@
     FunctionDeclaration function = unit.declarations.last;
     BlockFunctionBody body = function.functionExpression.body;
     Statement statement = body.block.statements[0];
-    expect(statement.accept(new ExitDetector()), true);
+    expect(ExitDetector.exits(statement), true);
   }
 
   void test_switch_withEnum_true_withDefault() {
@@ -7440,7 +7439,7 @@
     FunctionDeclaration function = unit.declarations.last;
     BlockFunctionBody body = function.functionExpression.body;
     Statement statement = body.block.statements[0];
-    expect(statement.accept(new ExitDetector()), true);
+    expect(ExitDetector.exits(statement), true);
   }
 }
 
diff --git a/pkg/analyzer/test/generated/element_test.dart b/pkg/analyzer/test/generated/element_test.dart
index e89fb06..64d5fb4 100644
--- a/pkg/analyzer/test/generated/element_test.dart
+++ b/pkg/analyzer/test/generated/element_test.dart
@@ -1709,6 +1709,14 @@
     expect(namedParameters, hasLength(1));
     expect(namedParameters[namedParameterName], namedParameterType);
   }
+
+  void test_toString_recursive() {
+    FunctionElementImpl t = ElementFactory.functionElement("t");
+    FunctionElementImpl s = ElementFactory.functionElement("s");
+    t.returnType = s.type;
+    s.returnType = t.type;
+    expect(t.type.toString(), '() \u2192 () \u2192 ...');
+  }
 }
 
 @reflectiveTest
diff --git a/pkg/analyzer/test/generated/engine_test.dart b/pkg/analyzer/test/generated/engine_test.dart
index 0089eb8..540f2d9 100644
--- a/pkg/analyzer/test/generated/engine_test.dart
+++ b/pkg/analyzer/test/generated/engine_test.dart
@@ -1636,6 +1636,42 @@
         reason: "part resolved 3");
   }
 
+  void test_performAnalysisTask_getContentException_dart() {
+    // add source that throw an exception on "get content"
+    Source source = new _Source_getContent_throwException('test.dart');
+    {
+      ChangeSet changeSet = new ChangeSet();
+      changeSet.addedSource(source);
+      _context.applyChanges(changeSet);
+    }
+    // prepare errors
+    _analyzeAll_assertFinished();
+    List<AnalysisError> errors = _context.getErrors(source).errors;
+    // validate errors
+    expect(errors, hasLength(1));
+    AnalysisError error = errors[0];
+    expect(error.source, same(source));
+    expect(error.errorCode, ScannerErrorCode.UNABLE_GET_CONTENT);
+  }
+
+  void test_performAnalysisTask_getContentException_html() {
+    // add source that throw an exception on "get content"
+    Source source = new _Source_getContent_throwException('test.html');
+    {
+      ChangeSet changeSet = new ChangeSet();
+      changeSet.addedSource(source);
+      _context.applyChanges(changeSet);
+    }
+    // prepare errors
+    _analyzeAll_assertFinished();
+    List<AnalysisError> errors = _context.getErrors(source).errors;
+    // validate errors
+    expect(errors, hasLength(1));
+    AnalysisError error = errors[0];
+    expect(error.source, same(source));
+    expect(error.errorCode, ScannerErrorCode.UNABLE_GET_CONTENT);
+  }
+
   void test_performAnalysisTask_importedLibraryAdd() {
     Source libASource =
         _addSource("/libA.dart", "library libA; import 'libB.dart';");
@@ -2219,7 +2255,6 @@
   }
 }
 
-
 class AnalysisContextImplTest_Source_exists_true extends TestSource {
   @override
   bool exists() => true;
@@ -2360,6 +2395,10 @@
     DartEntry entry = new DartEntry();
     expect(entry.allErrors, hasLength(0));
     entry.setValue(
+        SourceEntry.CONTENT_ERRORS,
+        <AnalysisError>[
+            new AnalysisError.con1(source, ScannerErrorCode.UNABLE_GET_CONTENT)]);
+    entry.setValue(
         DartEntry.SCAN_ERRORS,
         <AnalysisError>[
             new AnalysisError.con1(source, ScannerErrorCode.UNTERMINATED_STRING_LITERAL)]);
@@ -2383,7 +2422,7 @@
         DartEntry.HINTS,
         source,
         <AnalysisError>[new AnalysisError.con1(source, HintCode.DEAD_CODE)]);
-    expect(entry.allErrors, hasLength(5));
+    expect(entry.allErrors, hasLength(6));
   }
 
   void test_creation() {
@@ -2665,6 +2704,7 @@
     DartEntry entry = _entryWithValidState(librarySource);
     entry.invalidateAllInformation();
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.INVALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.INVALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.INVALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -2713,6 +2753,7 @@
     DartEntry entry = _entryWithValidState(librarySource);
     entry.invalidateAllResolutionInformation(false);
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -2761,6 +2802,7 @@
     DartEntry entry = _entryWithValidState(librarySource);
     entry.invalidateAllResolutionInformation(true);
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -2842,6 +2884,7 @@
         firstLibrary,
         new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -2909,6 +2952,7 @@
     entry.recordContentError(
         new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.ERROR));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.ERROR));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -2969,6 +3013,7 @@
         firstLibrary,
         new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -3035,6 +3080,7 @@
     DartEntry entry = _entryWithValidState(firstLibrary);
     entry.recordParseError(new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -3094,6 +3140,7 @@
     entry.recordResolutionError(
         new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -3154,6 +3201,7 @@
         firstLibrary,
         new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -3220,6 +3268,7 @@
     DartEntry entry = _entryWithValidState(firstLibrary);
     entry.recordScanError(new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.ERROR));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -3280,6 +3329,7 @@
         firstLibrary,
         new CaughtException(new AnalysisException(), null));
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -3655,6 +3705,7 @@
   DartEntry _entryWithValidState([Source firstLibrary, Source secondLibrary]) {
     DartEntry entry = new DartEntry();
     entry.setValue(SourceEntry.CONTENT, null);
+    entry.setValue(SourceEntry.CONTENT_ERRORS, null);
     entry.setValue(SourceEntry.LINE_INFO, null);
     entry.setValue(DartEntry.CONTAINING_LIBRARIES, null);
     entry.setValue(DartEntry.ELEMENT, null);
@@ -3695,6 +3746,7 @@
     // Validate that the state was set correctly.
     //
     expect(entry.getState(SourceEntry.CONTENT), same(CacheState.VALID));
+    expect(entry.getState(SourceEntry.CONTENT_ERRORS), same(CacheState.VALID));
     expect(entry.getState(SourceEntry.LINE_INFO), same(CacheState.VALID));
     expect(
         entry.getState(DartEntry.CONTAINING_LIBRARIES),
@@ -3989,6 +4041,7 @@
   bool visitGenerateDartHintsTask(GenerateDartHintsTask task) => true;
 }
 
+
 class GenerateDartHintsTaskTestTV_perform extends TestTaskVisitor<bool> {
   Source librarySource;
   Source partSource;
@@ -4115,12 +4168,12 @@
   }
 }
 
-
 class GetContentTaskTestTV_accept extends TestTaskVisitor<bool> {
   @override
   bool visitGetContentTask(GetContentTask task) => true;
 }
 
+
 class GetContentTaskTestTV_perform_exception extends TestTaskVisitor<bool> {
   @override
   bool visitGetContentTask(GetContentTask task) {
@@ -4129,7 +4182,6 @@
   }
 }
 
-
 class GetContentTaskTestTV_perform_valid extends TestTaskVisitor<bool> {
   InternalAnalysisContext context;
   Source source;
@@ -4999,12 +5051,12 @@
   }
 }
 
+
 class LintGeneratorTest_Linter_Null_Visitor extends Linter {
   @override
   AstVisitor getVisitor() => null;
 }
 
-
 @reflectiveTest
 class ParseDartTaskTest extends EngineTestCase {
   void test_accept() {
@@ -7093,6 +7145,17 @@
 }
 
 
+class _Source_getContent_throwException extends NonExistingSource {
+  _Source_getContent_throwException(String name)
+      : super(name, UriKind.FILE_URI);
+
+  @override
+  TimestampedData<String> get contents {
+    throw 'Read error';
+  }
+}
+
+
 class _UniversalCachePartitionTest_test_setMaxCacheSize implements
     CacheRetentionPolicy {
   @override
diff --git a/pkg/analyzer/test/generated/incremental_resolver_test.dart b/pkg/analyzer/test/generated/incremental_resolver_test.dart
index 5e41ef6..314b7ca 100644
--- a/pkg/analyzer/test/generated/incremental_resolver_test.dart
+++ b/pkg/analyzer/test/generated/incremental_resolver_test.dart
@@ -2779,6 +2779,38 @@
 ''');
   }
 
+  void test_dartDoc_elegant_updateText_insertToken() {
+    _resolveUnit(r'''
+/// A
+/// [int]
+class Test {
+}
+''');
+    _updateAndValidate(r'''
+/// A
+///
+/// [int]
+class Test {
+}
+''');
+  }
+
+  void test_dartDoc_elegant_updateText_removeToken() {
+    _resolveUnit(r'''
+/// A
+///
+/// [int]
+class Test {
+}
+''');
+    _updateAndValidate(r'''
+/// A
+/// [int]
+class Test {
+}
+''');
+  }
+
   void test_endOfLineComment_add_beforeKeywordToken() {
     _resolveUnit(r'''
 main() {
diff --git a/pkg/analyzer/test/generated/non_error_resolver_test.dart b/pkg/analyzer/test/generated/non_error_resolver_test.dart
index 3238e50..1761c81 100644
--- a/pkg/analyzer/test/generated/non_error_resolver_test.dart
+++ b/pkg/analyzer/test/generated/non_error_resolver_test.dart
@@ -339,6 +339,22 @@
     verify([source]);
   }
 
+  void test_async_flattened() {
+    Source source = addSource('''
+import 'dart:async';
+typedef Future<int> CreatesFutureInt();
+main() {
+  CreatesFutureInt createFutureInt = () async => f();
+  Future<int> futureInt = createFutureInt();
+  futureInt.then((int i) => print(i));
+}
+Future<int> f() => null;
+''');
+    resolve(source);
+    assertNoErrors(source);
+    verify([source]);
+  }
+
   void test_async_future_dynamic_with_return() {
     Source source = addSource('''
 import 'dart:async';
diff --git a/pkg/analyzer/test/generated/resolver_test.dart b/pkg/analyzer/test/generated/resolver_test.dart
index b2f48d0..fb54593 100644
--- a/pkg/analyzer/test/generated/resolver_test.dart
+++ b/pkg/analyzer/test/generated/resolver_test.dart
@@ -10030,6 +10030,69 @@
     _listener.assertNoErrors();
   }
 
+  void test_visitFunctionExpression_async_block() {
+    // () async {}
+    BlockFunctionBody body = AstFactory.blockFunctionBody2();
+    body.keyword = TokenFactory.tokenFromString('async');
+    FunctionExpression node =
+        _resolvedFunctionExpression(AstFactory.formalParameterList([]), body);
+    DartType resultType = _analyze(node);
+    _assertFunctionType(
+        _typeProvider.futureDynamicType,
+        null,
+        null,
+        null,
+        resultType);
+    _listener.assertNoErrors();
+  }
+
+  void test_visitFunctionExpression_async_expression() {
+    // () async => e, where e has type int
+    InterfaceType intType = _typeProvider.intType;
+    InterfaceType futureIntType =
+        _typeProvider.futureType.substitute4(<DartType>[intType]);
+    Expression expression = _resolvedVariable(intType, 'e');
+    ExpressionFunctionBody body = AstFactory.expressionFunctionBody(expression);
+    body.keyword = TokenFactory.tokenFromString('async');
+    FunctionExpression node =
+        _resolvedFunctionExpression(AstFactory.formalParameterList([]), body);
+    DartType resultType = _analyze(node);
+    _assertFunctionType(futureIntType, null, null, null, resultType);
+    _listener.assertNoErrors();
+  }
+
+  void test_visitFunctionExpression_async_expression_flatten() {
+    // () async => e, where e has type Future<int>
+    InterfaceType intType = _typeProvider.intType;
+    InterfaceType futureIntType =
+        _typeProvider.futureType.substitute4(<DartType>[intType]);
+    Expression expression = _resolvedVariable(futureIntType, 'e');
+    ExpressionFunctionBody body = AstFactory.expressionFunctionBody(expression);
+    body.keyword = TokenFactory.tokenFromString('async');
+    FunctionExpression node =
+        _resolvedFunctionExpression(AstFactory.formalParameterList([]), body);
+    DartType resultType = _analyze(node);
+    _assertFunctionType(futureIntType, null, null, null, resultType);
+    _listener.assertNoErrors();
+  }
+
+  void test_visitFunctionExpression_async_expression_flatten_twice() {
+    // () async => e, where e has type Future<Future<int>>
+    InterfaceType intType = _typeProvider.intType;
+    InterfaceType futureIntType =
+        _typeProvider.futureType.substitute4(<DartType>[intType]);
+    InterfaceType futureFutureIntType =
+        _typeProvider.futureType.substitute4(<DartType>[futureIntType]);
+    Expression expression = _resolvedVariable(futureFutureIntType, 'e');
+    ExpressionFunctionBody body = AstFactory.expressionFunctionBody(expression);
+    body.keyword = TokenFactory.tokenFromString('async');
+    FunctionExpression node =
+        _resolvedFunctionExpression(AstFactory.formalParameterList([]), body);
+    DartType resultType = _analyze(node);
+    _assertFunctionType(futureIntType, null, null, null, resultType);
+    _listener.assertNoErrors();
+  }
+
   void test_visitFunctionExpression_generator_async() {
     // () async* {}
     BlockFunctionBody body = AstFactory.blockFunctionBody2();
@@ -10864,7 +10927,7 @@
         expect(namedTypes[name], same(type));
       });
     }
-    expect(functionType.returnType, same(expectedReturnType));
+    expect(functionType.returnType, equals(expectedReturnType));
   }
 
   void _assertType(InterfaceTypeImpl expectedType,
diff --git a/pkg/analyzer2dart/lib/src/converted_world.dart b/pkg/analyzer2dart/lib/src/converted_world.dart
index f54a7a6..600487a 100644
--- a/pkg/analyzer2dart/lib/src/converted_world.dart
+++ b/pkg/analyzer2dart/lib/src/converted_world.dart
@@ -31,6 +31,8 @@
   final dart2js.FunctionElement mainFunction;
   Map<dart2js.AstElement, ir.Node> executableElements =
       new HashMap<dart2js.AstElement, ir.Node>();
+  final List<dart2js.ClassElement> instantiatedClasses =
+      <dart2js.ClassElement>[];
 
   _ConvertedWorldImpl(this.mainFunction);
 
@@ -40,8 +42,6 @@
 
   Iterable<dart2js.AstElement> get resolvedElements => executableElements.keys;
 
-  Iterable<dart2js.ClassElement> get instantiatedClasses => [];
-
   ir.Node getIr(dart2js.Element element) => executableElements[element];
 
   final dart2js.DartTypes dartTypes = new _DartTypes();
@@ -61,9 +61,8 @@
         converter.convertElement(analyzerElement);
     CpsElementVisitor visitor = new CpsElementVisitor(converter, node);
     ir.Node cpsNode = analyzerElement.accept(visitor);
-    if (cpsNode != null) {
-      convertedWorld.executableElements[dart2jsElement] = cpsNode;
-    } else {
+    convertedWorld.executableElements[dart2jsElement] = cpsNode;
+    if (cpsNode == null && !analyzerElement.isSynthetic) {
       String message =
          'No CPS node generated for $analyzerElement (${node.runtimeType}).';
       reportSourceMessage(analyzerElement.source, node, message);
@@ -71,9 +70,18 @@
     }
   }
 
+  void convertClass(analyzer.ClassElement analyzerElement, _) {
+    // Skip conversion of SDK sources since we don't generate code for it
+    // anyway.
+    if (analyzerElement.source.isInSystemLibrary) return;
+    convertedWorld.instantiatedClasses.add(
+        converter.convertElement(analyzerElement));
+  }
+
   closedWorld.executableElements.forEach(convert);
   closedWorld.variables.forEach(convert);
   closedWorld.fields.forEach(convert);
+  closedWorld.instantiatedClasses.forEach(convertClass);
 
   return convertedWorld;
 }
diff --git a/pkg/analyzer2dart/lib/src/cps_generator.dart b/pkg/analyzer2dart/lib/src/cps_generator.dart
index 11c8a84..879b308 100644
--- a/pkg/analyzer2dart/lib/src/cps_generator.dart
+++ b/pkg/analyzer2dart/lib/src/cps_generator.dart
@@ -35,7 +35,14 @@
     CpsGeneratingVisitor visitor = new CpsGeneratingVisitor(converter, element);

     FunctionDeclaration functionDeclaration = node;

     return visitor.handleFunctionDeclaration(

-        element, functionDeclaration.functionExpression);

+        element, functionDeclaration.functionExpression.body);

+  }

+

+  @override

+  ir.FunctionDefinition visitMethodElement(analyzer.MethodElement element) {

+    CpsGeneratingVisitor visitor = new CpsGeneratingVisitor(converter, element);

+    MethodDeclaration methodDeclaration = node;

+    return visitor.handleFunctionDeclaration(element, methodDeclaration.body);

   }

 

   @override

@@ -52,8 +59,13 @@
     CpsGeneratingVisitor visitor = new CpsGeneratingVisitor(converter, element);

     if (!element.isFactory) {

       ConstructorDeclaration constructorDeclaration = node;

-      return visitor.handleConstructorDeclaration(

-          element, constructorDeclaration);

+      FunctionBody body;

+      if (constructorDeclaration != null) {

+        body = constructorDeclaration.body;

+      } else {

+        assert(element.isSynthetic);

+      }

+      return visitor.handleConstructorDeclaration(element, body);

     }

     // TODO(johnniwinther): Support factory constructors.

     return null;

@@ -78,8 +90,7 @@
   ir.Node visit(AstNode node) => node.accept(this);

 

   ir.ConstructorDefinition handleConstructorDeclaration(

-      analyzer.ConstructorElement constructor, ConstructorDeclaration node) {

-    FunctionBody body = node.body;

+      analyzer.ConstructorElement constructor, FunctionBody body) {

     dart2js.ConstructorElement element = converter.convertElement(constructor);

     return withBuilder(

         new DartIrBuilder(DART_CONSTANT_SYSTEM,

@@ -91,7 +102,8 @@
           constructor.parameters.map(converter.convertElement));

       // Visit the body directly to avoid processing the signature as

       // expressions.

-      visit(node.body);

+      // Call to allow for `body == null` in case of synthesized constructors.

+      build(body);

       return irBuilder.makeConstructorDefinition(const [], const []);

     });

   }

@@ -112,7 +124,7 @@
   }

 

   ir.FunctionDefinition handleFunctionDeclaration(

-      analyzer.FunctionElement function, FunctionExpression node) {

+      analyzer.ExecutableElement function, FunctionBody body) {

     dart2js.FunctionElement element = converter.convertElement(function);

     return withBuilder(

         new DartIrBuilder(DART_CONSTANT_SYSTEM,

@@ -124,7 +136,7 @@
           function.parameters.map(converter.convertElement));

       // Visit the body directly to avoid processing the signature as

       // expressions.

-      visit(node.body);

+      visit(body);

       return irBuilder.makeFunctionDefinition(const []);

     });

   }

@@ -132,12 +144,13 @@
   @override

   ir.Primitive visitFunctionExpression(FunctionExpression node) {

     return irBuilder.buildFunctionExpression(

-        handleFunctionDeclaration(node.element, node));

+        handleFunctionDeclaration(node.element, node.body));

   }

 

   @override

   ir.FunctionDefinition visitFunctionDeclaration(FunctionDeclaration node) {

-    return handleFunctionDeclaration(node.element, node.functionExpression);

+    return handleFunctionDeclaration(

+        node.element, node.functionExpression.body);

   }

 

   @override

@@ -146,7 +159,7 @@
     analyzer.FunctionElement function = functionDeclaration.element;

     dart2js.FunctionElement element = converter.convertElement(function);

     ir.FunctionDefinition definition = handleFunctionDeclaration(

-        function, functionDeclaration.functionExpression);

+        function, functionDeclaration.functionExpression.body);

     irBuilder.declareLocalFunction(element, definition);

   }

 

@@ -204,9 +217,10 @@
                                      AccessSemantics semantics) {

     analyzer.Element staticElement = semantics.element;

     dart2js.Element element = converter.convertElement(staticElement);

+    ir.Primitive receiver = irBuilder.buildLocalGet(element);

     List<ir.Definition> arguments = visitArguments(node.argumentList);

-    return irBuilder.buildLocalInvocation(

-      element,

+    return irBuilder.buildCallInvocation(

+      receiver,

       createSelectorFromMethodInvocation(

           node.argumentList, node.methodName.name),

       arguments);

@@ -229,7 +243,7 @@
       FunctionExpressionInvocation node) {

     ir.Primitive target = build(node.function);

     List<ir.Definition> arguments = visitArguments(node.argumentList);

-    return irBuilder.buildFunctionExpressionInvocation(

+    return irBuilder.buildCallInvocation(

         target,

         createSelectorFromMethodInvocation(node.argumentList, 'call'),

         arguments);

diff --git a/pkg/analyzer2dart/lib/src/dart_backend.dart b/pkg/analyzer2dart/lib/src/dart_backend.dart
index 8a932e9..880f968 100644
--- a/pkg/analyzer2dart/lib/src/dart_backend.dart
+++ b/pkg/analyzer2dart/lib/src/dart_backend.dart
@@ -28,7 +28,7 @@
           element,
           convertedWorld.getIr(element));
     },
-    shouldOutput: (_) => true,
+    shouldOutput: (Element element) => !element.isSynthesized,
     isSafeToRemoveTypeDeclarations: (_) => false);
 }
 
diff --git a/pkg/analyzer2dart/lib/src/element_converter.dart b/pkg/analyzer2dart/lib/src/element_converter.dart
index 4b97325..666124e 100644
--- a/pkg/analyzer2dart/lib/src/element_converter.dart
+++ b/pkg/analyzer2dart/lib/src/element_converter.dart
@@ -163,4 +163,9 @@
       analyzer.ConstructorElement input) {
     return new ConstructorElementY(converter, input);
   }
+
+  @override
+  dart2js.MethodElement visitMethodElement(analyzer.MethodElement input) {
+    return new InstanceMethodElementY(converter, input);
+  }
 }
diff --git a/pkg/analyzer2dart/lib/src/modely.dart b/pkg/analyzer2dart/lib/src/modely.dart
index 200a75b..4df5cdc 100644
--- a/pkg/analyzer2dart/lib/src/modely.dart
+++ b/pkg/analyzer2dart/lib/src/modely.dart
@@ -50,6 +50,9 @@
   @override
   bool get isLocal => false;
 
+  @override
+  bool get isSynthesized => false;
+
   unsupported(String method) {
     throw new UnsupportedError(
         "'$method' is unsupported on $this ($runtimeType)");
@@ -152,9 +155,6 @@
   bool get isNative => unsupported('isNative');
 
   @override
-  bool get isSynthesized => unsupported('isSynthesized');
-
-  @override
   bool get isTopLevel => unsupported('isTopLevel');
 
   @override
@@ -798,6 +798,9 @@
 
   @override
   bool get isClassMember => true;
+
+  @override
+  bool get isTopLevel => false;
 }
 
 class ConstructorElementY extends ElementY
@@ -824,6 +827,9 @@
   @override
   bool get isStatic => false;
 
+  @override
+  bool get isSynthesized => element.isSynthetic;
+
   ConstructorElementY(ElementConverter converter,
                       analyzer.ConstructorElement element)
       : super(converter, element);
@@ -843,3 +849,35 @@
   @override
   get nestedClosures => unsupported('nestedClosures');
 }
+
+class InstanceMethodElementY extends ElementY
+    with AnalyzableElementY,
+         AstElementY,
+         FunctionElementMixin,
+         ClassMemberMixin
+    implements dart2js.MethodElement {
+
+  analyzer.MethodElement get element => super.element;
+
+  @override
+  dart2js.ElementKind get kind => dart2js.ElementKind.FUNCTION;
+
+  @override
+  bool get isStatic => element.isStatic;
+
+  @override
+  bool get isAbstract => element.isAbstract;
+
+  @override
+  bool get isFactoryConstructor => false;
+
+  @override
+  bool get isInstanceMember => true;
+
+  InstanceMethodElementY(ElementConverter converter,
+                         analyzer.MethodElement element)
+      : super(converter, element);
+
+  @override
+  get nestedClosures => unsupported('nestedClosures');
+}
\ No newline at end of file
diff --git a/pkg/analyzer2dart/lib/src/tree_shaker.dart b/pkg/analyzer2dart/lib/src/tree_shaker.dart
index 7e765cc..6b9eaa6 100644
--- a/pkg/analyzer2dart/lib/src/tree_shaker.dart
+++ b/pkg/analyzer2dart/lib/src/tree_shaker.dart
@@ -90,7 +90,16 @@
       // constructor, in which case all we need to do is record the class as
       // being instantiated by this method.  TODO(paulberry): handle the
       // mixin case.
-      analysis.instantiates.add(method.enclosingElement);
+      ClassElement instantiatedClass = method.enclosingElement;
+      analysis.instantiates.add(instantiatedClass);
+      if (instantiatedClass.supertype != null) {
+        ClassElement superClass = instantiatedClass.supertype.element;
+        ConstructorElement superConstructor = superClass.unnamedConstructor;
+        if (superConstructor != null) {
+          // TODO(johnniwinther): Register instantiated type and selector.
+          analysis.calls.add(superConstructor);
+        }
+      }
     } else {
       // This is an executable element with no associated declaration in the
       // AST, and it's not a constructor.  TODO(paulberry): can this ever
@@ -369,7 +378,18 @@
       // we don't need to, because the redirected-to constructor will take care
       // of that).
       if (node.initializers.length != 1 || node.initializers[0] is! RedirectingConstructorInvocation) {
+        ClassElement classElement = node.element.enclosingElement;
         analysis.instantiates.add(node.element.enclosingElement);
+        if (!node.initializers.any((i) => i is SuperConstructorInvocation)) {
+          if (classElement.supertype != null) {
+            ClassElement superClass = classElement.supertype.element;
+            ConstructorElement superConstructor = superClass.unnamedConstructor;
+            if (superConstructor != null) {
+              // TODO(johnniwinther): Register instantiated type and selector.
+              analysis.calls.add(superConstructor);
+            }
+          }
+        }
       }
     } else if (node.redirectedConstructor != null) {
       if (node.redirectedConstructor.staticElement == null) {
diff --git a/pkg/analyzer2dart/test/end2end_data.dart b/pkg/analyzer2dart/test/end2end_data.dart
index b1c6ac3..d439638 100644
--- a/pkg/analyzer2dart/test/end2end_data.dart
+++ b/pkg/analyzer2dart/test/end2end_data.dart
@@ -820,6 +820,13 @@
 
   const Group('Constructors', const <TestSpec>[
     const TestSpec('''
+class C {}
+main() {
+  return new C();
+}
+'''),
+
+    const TestSpec('''
 class C {
   C() {}
 }
@@ -827,5 +834,37 @@
   return new C();
 }
 '''),
+
+    const TestSpec('''
+class B {}
+class C extends B {
+  C() {}
+}
+main() {
+  return new C();
+}
+'''),
+
+    const TestSpec('''
+class B {
+  B() {}
+}
+class C extends B {}
+main() {
+  return new C();
+}
+'''),
   ]),
+
+  const Group('Instance method', const <TestSpec>[
+    const TestSpec('''
+class C {
+  C() {}
+  foo() {}
+}
+main() {
+  return new C().foo();
+}
+'''),
+]),
 ];
diff --git a/pkg/analyzer2dart/test/end2end_test.dart b/pkg/analyzer2dart/test/end2end_test.dart
index 6cc500c0..18fe84a 100644
--- a/pkg/analyzer2dart/test/end2end_test.dart
+++ b/pkg/analyzer2dart/test/end2end_test.dart
@@ -21,8 +21,8 @@
 import 'output_helper.dart';
 import 'end2end_data.dart';
 
-main() {
-  performTests(TEST_DATA, unittester, checkResult);
+main(List<String> args) {
+  performTests(TEST_DATA, unittester, checkResult, args);
 }
 
 checkResult(TestSpec result) {
diff --git a/pkg/analyzer2dart/test/sexpr_data.dart b/pkg/analyzer2dart/test/sexpr_data.dart
index 646a42a..d380c6d 100644
--- a/pkg/analyzer2dart/test/sexpr_data.dart
+++ b/pkg/analyzer2dart/test/sexpr_data.dart
@@ -1386,6 +1386,25 @@
 
   const Group('Constructors', const <TestSpec>[
     const TestSpec('''
+class C {}
+main() {
+  return new C();
+}
+''',
+    const {
+'main': '''
+(FunctionDefinition main () return
+  (LetCont ((k0 (v0)
+      (InvokeContinuation return (v0))))
+    (InvokeConstructor C () k0)))
+''',
+'C.':  '''
+(FunctionDefinition  () return
+  (LetPrim (v0 (Constant (Null)))
+    (InvokeContinuation return (v0))))
+'''}),
+
+    const TestSpec('''
 class C {
   C() {}
 }
@@ -1405,5 +1424,85 @@
   (LetPrim (v0 (Constant (Null)))
     (InvokeContinuation return (v0))))
 '''}),
+
+    const TestSpec('''
+class B {}
+class C extends B {
+  C() {}
+}
+main() {
+  return new C();
+}
+''',
+    const {
+'main': '''
+(FunctionDefinition main () return
+  (LetCont ((k0 (v0)
+      (InvokeContinuation return (v0))))
+    (InvokeConstructor C () k0)))
+''',
+'B.':  '''
+(FunctionDefinition  () return
+  (LetPrim (v0 (Constant (Null)))
+    (InvokeContinuation return (v0))))
+''',
+'C.': '''
+(FunctionDefinition  () return
+  (LetPrim (v0 (Constant (Null)))
+    (InvokeContinuation return (v0))))
+'''}),
+
+    const TestSpec('''
+class B {
+  B() {}
+}
+class C extends B {}
+main() {
+  return new C();
+}
+''',
+    const {
+'main': '''
+(FunctionDefinition main () return
+  (LetCont ((k0 (v0)
+      (InvokeContinuation return (v0))))
+    (InvokeConstructor C () k0)))
+''',
+'B.': '''
+(FunctionDefinition  () return
+  (LetPrim (v0 (Constant (Null)))
+    (InvokeContinuation return (v0))))
+''',
+'C.':  '''
+(FunctionDefinition  () return
+  (LetPrim (v0 (Constant (Null)))
+    (InvokeContinuation return (v0))))
+'''}),
+  ]),
+
+  const Group('Instance method', const <TestSpec>[
+    const TestSpec('''
+class C {
+  C() {}
+  foo() {}
+}
+main() {
+  return new C().foo();
+}
+''',
+    const {
+'main': '''
+(FunctionDefinition main () return
+  (LetCont ((k0 (v0)
+      (LetCont ((k1 (v1)
+          (InvokeContinuation return (v1))))
+        (InvokeMethod v0 foo () k1))))
+    (InvokeConstructor C () k0)))
+''',
+'C.foo': '''
+(FunctionDefinition foo () return
+  (LetPrim (v0 (Constant (Null)))
+    (InvokeContinuation return (v0))))
+'''}),
   ]),
 ];
diff --git a/pkg/analyzer2dart/test/sexpr_test.dart b/pkg/analyzer2dart/test/sexpr_test.dart
index e4f83ee..9450ced 100644
--- a/pkg/analyzer2dart/test/sexpr_test.dart
+++ b/pkg/analyzer2dart/test/sexpr_test.dart
@@ -9,6 +9,7 @@
 import 'package:analyzer/src/generated/element.dart';
 import 'package:analyzer/src/generated/sdk.dart';
 import 'package:analyzer/src/generated/source.dart';
+import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';
 import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';
 import 'package:compiler/src/elements/elements.dart' as dart2js;
 import 'package:unittest/unittest.dart';
@@ -20,8 +21,8 @@
 import 'test_helper.dart';
 import 'sexpr_data.dart';
 
-main() {
-  performTests(TEST_DATA, unittester, checkResult);
+main(List<String> args) {
+  performTests(TEST_DATA, unittester, checkResult, args);
 }
 
 checkResult(TestSpec result) {
@@ -41,13 +42,22 @@
   void checkOutput(String elementName,
                    dart2js.Element element,
                    String expectedOutput) {
-    expectedOutput = expectedOutput.trim();
-    String output = convertedWorld.getIr(element)
-        .accept(new SExpressionStringifier());
-    expect(output, equals(expectedOutput),
-        reason: "\nInput:\n${result.input}\n"
-                "Expected for '$elementName':\n$expectedOutput\n"
-                "Actual for '$elementName':\n$output\n");
+    ExecutableDefinition ir = convertedWorld.getIr(element);
+    if (expectedOutput == null) {
+      expect(ir, isNull,
+          reason: "\nInput:\n${result.input}\n"
+                  "No CPS IR expected for $element");
+    } else {
+      expect(ir, isNotNull,
+          reason: "\nInput:\n${result.input}\n"
+                  "No CPS IR for $element");
+      expectedOutput = expectedOutput.trim();
+      String output = ir.accept(new SExpressionStringifier());
+      expect(output, equals(expectedOutput),
+          reason: "\nInput:\n${result.input}\n"
+                  "Expected for '$elementName':\n$expectedOutput\n"
+                  "Actual for '$elementName':\n$output\n");
+    }
   }
 
   if (result.output is String) {
diff --git a/pkg/analyzer2dart/test/test_helper.dart b/pkg/analyzer2dart/test/test_helper.dart
index 9bb0b4a..86a52b0 100644
--- a/pkg/analyzer2dart/test/test_helper.dart
+++ b/pkg/analyzer2dart/test/test_helper.dart
@@ -25,8 +25,16 @@
 typedef RunTest(TestSpecBase result);
 
 /// Test [data] using [testGroup] and [check].
-void performTests(List<Group> data, TestGroup testGroup, RunTest runTest) {
+void performTests(List<Group> data,
+                  TestGroup testGroup,
+                  RunTest runTest,
+                  List<String> groupsToRun) {
   for (Group group in data) {
+    if (groupsToRun.isNotEmpty &&
+        !groupsToRun.contains(group.name)) {
+      // Skip this group.
+      continue;
+    }
     testGroup(group, runTest);
   }
 }
diff --git a/pkg/analyzer2dart/test/tree_shaker_test.dart b/pkg/analyzer2dart/test/tree_shaker_test.dart
index 9ba20c7..cedb449 100644
--- a/pkg/analyzer2dart/test/tree_shaker_test.dart
+++ b/pkg/analyzer2dart/test/tree_shaker_test.dart
@@ -99,6 +99,18 @@
     helper.assertNoInstantiatedClass('B');
   });
 
+  test('Super class instantiation', () {
+    var helper = new TreeShakerTestHelper('''
+main() {
+  var x = new B();
+}
+class A {}
+class B extends A {}
+''');
+    helper.assertHasInstantiatedClass('A');
+    helper.assertHasInstantiatedClass('B');
+  });
+
   test('Method invocation', () {
     var helper = new TreeShakerTestHelper('''
 main() {
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
index 8ce30bb..be603fa 100644
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ b/pkg/compiler/lib/src/apiimpl.dart
@@ -84,7 +84,6 @@
             suppressWarnings: hasOption(options, '--suppress-warnings'),
             enableExperimentalMirrors:
                 hasOption(options, '--enable-experimental-mirrors'),
-            enableAsyncAwait: hasOption(options, '--enable-async'),
             generateCodeWithCompileTimeErrors:
                 hasOption(options, '--generate-code-with-compile-time-errors'),
             allowNativeExtensions:
@@ -106,10 +105,6 @@
       throw new ArgumentError("[packageRoot] must end with a /.");
     }
     if (!analyzeOnly) {
-      if (enableAsyncAwait && emitJavaScript) {
-        throw new ArgumentError(
-            "--enable-async is currently only supported with --analyze-only");
-      }
       if (allowNativeExtensions) {
         throw new ArgumentError(
             "--allow-native-extensions is only supported in combination with "
diff --git a/pkg/compiler/lib/src/closure.dart b/pkg/compiler/lib/src/closure.dart
index 93e7bb2..fbefe05 100644
--- a/pkg/compiler/lib/src/closure.dart
+++ b/pkg/compiler/lib/src/closure.dart
@@ -272,6 +272,7 @@
                                 ClosureClassElement enclosing)
       : expression = other,
         super(name, other.kind, other.modifiers, enclosing, false) {
+    asyncMarker = other.asyncMarker;
     functionSignatureCache = other.functionSignature;
   }
 
@@ -347,7 +348,13 @@
   // contain any nested closure.
   final Map<Node, ClosureScope> capturingScopes = new Map<Node, ClosureScope>();
 
-  final Set<Local> usedVariablesInTry = new Set<Local>();
+  /// Variables that are used in a try must be treated as boxed because the
+  /// control flow can be non-linear.
+  ///
+  /// Also parameters to a `sync*` generator must be boxed, because of the way
+  /// we rewrite sync* functions. See also comments in [useLocal].
+  /// TODO(johnniwinter): Add variables to this only if the variable is mutated.
+  final Set<Local> variablesUsedInTryOrGenerator = new Set<Local>();
 
   ClosureClassMap(this.closureElement,
                   this.closureClassElement,
@@ -427,6 +434,7 @@
   int closureFieldCounter = 0;
   int boxedFieldCounter = 0;
   bool inTryStatement = false;
+
   final Map<Node, ClosureClassMap> closureMappingCache;
 
   // Map of captured variables. Initially they will map to `null`. If
@@ -588,9 +596,13 @@
       // Note that nested (named) functions are immutable.
       if (variable != closureData.thisLocal &&
           variable != closureData.closureElement) {
-        // TODO(ngeoffray): only do this if the variable is mutated.
-        closureData.usedVariablesInTry.add(variable);
+        closureData.variablesUsedInTryOrGenerator.add(variable);
       }
+    } else if (variable is LocalParameterElement &&
+        variable.functionDeclaration.asyncMarker == AsyncMarker.SYNC_STAR) {
+      // Parameters in a sync* function are shared between each Iterator created
+      // by the Iterable returned by the function, therefore they must be boxed.
+      closureData.variablesUsedInTryOrGenerator.add(variable);
     }
   }
 
diff --git a/pkg/compiler/lib/src/compile_time_constants.dart b/pkg/compiler/lib/src/compile_time_constants.dart
index 5fb178d..13d5d63 100644
--- a/pkg/compiler/lib/src/compile_time_constants.dart
+++ b/pkg/compiler/lib/src/compile_time_constants.dart
@@ -676,10 +676,10 @@
           target.functionSignature.parameterCount,
           new ErroneousAstConstant(context, node));
     }
-    return selector.makeArgumentsList2(arguments,
-                                       target,
-                                       compileArgument,
-                                       compileDefaultValue);
+    return selector.makeArgumentsList(arguments,
+                                      target,
+                                      compileArgument,
+                                      compileDefaultValue);
   }
 
   AstConstant visitNewExpression(NewExpression node) {
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index a99134f..a3adcdb 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -176,6 +176,11 @@
   void registerInstantiation(InterfaceType type) {
     world.registerInstantiatedType(type, this);
   }
+
+  void registerAsyncMarker(FunctionElement element) {
+    backend.registerAsyncMarker(element, world, this);
+  }
+
 }
 
 /// [WorkItem] used exclusively by the [CodegenEnqueuer].
@@ -229,6 +234,8 @@
   void registerInstantiation(InterfaceType type);
 
   void registerGetOfStaticFunction(FunctionElement element);
+
+  void registerAsyncMarker(FunctionElement element);
 }
 
 abstract class Backend {
@@ -516,6 +523,10 @@
   void forgetElement(Element element) {}
 
   void registerMainHasArguments(Enqueuer enqueuer) {}
+
+  void registerAsyncMarker(FunctionElement element,
+                             Enqueuer enqueuer,
+                             Registry registry) {}
 }
 
 /// Backend callbacks function specific to the resolution phase.
@@ -740,12 +751,6 @@
 
   final bool suppressWarnings;
 
-  /// `true` if async/await features are supported.
-  final bool enableAsyncAwait;
-
-  /// `true` if the compiler uses the [JavaScriptBackend].
-  final bool emitJavaScript;
-
   /// If `true`, some values are cached for reuse in incremental compilation.
   /// Incremental compilation is basically calling [run] more than once.
   final bool hasIncrementalSupport;
@@ -768,6 +773,7 @@
   CompilerTask measuredTask;
   Element _currentElement;
   LibraryElement coreLibrary;
+  LibraryElement asyncLibrary;
 
   LibraryElement mainApp;
   FunctionElement mainFunction;
@@ -1007,14 +1013,11 @@
             this.suppressWarnings: false,
             bool hasIncrementalSupport: false,
             this.enableExperimentalMirrors: false,
-            bool enableAsyncAwait: false,
             this.allowNativeExtensions: false,
             this.generateCodeWithCompileTimeErrors: false,
             api.CompilerOutputProvider outputProvider,
             List<String> strips: const []})
-      : this.emitJavaScript = emitJavaScript,
-        this.enableAsyncAwait = enableAsyncAwait || !emitJavaScript,
-        this.disableTypeInferenceFlag =
+      : this.disableTypeInferenceFlag =
           disableTypeInferenceFlag || !emitJavaScript,
         this.analyzeOnly =
             analyzeOnly || analyzeSignaturesOnly || analyzeAllFlag,
@@ -1242,6 +1245,7 @@
       mirrorSystemClass = findRequiredElement(library, 'MirrorSystem');
       mirrorsUsedClass = findRequiredElement(library, 'MirrorsUsed');
     } else if (uri == DART_ASYNC) {
+      asyncLibrary = library;
       deferredLibraryClass = findRequiredElement(library, 'DeferredLibrary');
       _coreTypes.futureClass = findRequiredElement(library, 'Future');
       _coreTypes.streamClass = findRequiredElement(library, 'Stream');
@@ -1906,12 +1910,9 @@
   SourceSpan spanFromHInstruction(HInstruction instruction) {
     Element element = _elementFromHInstruction(instruction);
     if (element == null) element = currentElement;
-    var position = instruction.sourcePosition;
+    SourceInformation position = instruction.sourceInformation;
     if (position == null) return spanFromElement(element);
-    Token token = position.token;
-    if (token == null) return spanFromElement(element);
-    Uri uri = element.compilationUnit.script.readableUri;
-    return spanFromTokens(token, token, uri);
+    return position.sourceSpan;
   }
 
   /**
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
index f883b9b..366f409 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder.dart
@@ -731,18 +731,9 @@
         (k) => new ir.ConcatenateStrings(k, arguments));
   }
 
-  /// Create an invocation of [local] where the argument structure is defined
-  /// by [selector] and the argument values are defined by [arguments].
-  ir.Primitive buildLocalInvocation(LocalElement local,
-                                    Selector selector,
-                                    List<ir.Definition> arguments) {
-    return _buildInvokeCall(buildLocalGet(local), selector, arguments);
-  }
-
-  /// Create an invocation of the [functionExpression] where the argument
-  /// structure are defined by [selector] and the argument values are defined by
-  /// [arguments].
-  ir.Primitive buildFunctionExpressionInvocation(
+  /// Create an invocation of the `call` method of [functionExpression], where
+  /// the named arguments are given by [selector].
+  ir.Primitive buildCallInvocation(
       ir.Primitive functionExpression,
       Selector selector,
       List<ir.Definition> arguments) {
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart
index 9eafea6..e859eef 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_visitor.dart
@@ -67,11 +67,16 @@
       // TODO(sigurdm): Support native functions for dart2js.
       assert(invariant(element, !element.isNative));
 
-      // TODO(kmillikin,sigurdm): Support constructors.
-      if (element is ConstructorElement && !element.isGenerativeConstructor) {
-        return false;
+      if (element is ConstructorElement) {
+        if (!element.isGenerativeConstructor) {
+          // TODO(kmillikin,sigurdm): Support constructors.
+          return false;
+        }
+        if (element.isSynthesized) {
+          // Do generate CPS for synthetic constructors.
+          return true;
+        }
       }
-
     } else if (element is! FieldElement) {
       compiler.internalError(element, "Unexpected element type $element");
     }
@@ -144,7 +149,27 @@
   ClosureScope getClosureScopeForNode(ast.Node node);
   ClosureEnvironment getClosureEnvironment();
 
+  /// Normalizes the argument list to a static invocation (i.e. where the target
+  /// element is known).
+  ///
+  /// For the JS backend, inserts default arguments and normalizes order of
+  /// named arguments.
+  ///
+  /// For the Dart backend, returns [arguments].
+  List<ir.Primitive> normalizeStaticArguments(
+      Selector selector,
+      FunctionElement target,
+      List<ir.Primitive> arguments);
 
+  /// Normalizes the argument list of a dynamic invocation (i.e. where the
+  /// target element is unknown).
+  ///
+  /// For the JS backend, normalizes order of named arguments.
+  ///
+  /// For the Dart backend, returns [arguments].
+  List<ir.Primitive> normalizeDynamicArguments(
+      Selector selector,
+      List<ir.Primitive> arguments);
 
   ir.FunctionDefinition _makeFunctionBody(FunctionElement element,
                                           ast.FunctionExpression node) {
@@ -549,19 +574,13 @@
   ir.Primitive visitClosureSend(ast.Send node) {
     assert(irBuilder.isOpen);
     Element element = elements[node];
-    Selector closureSelector = elements.getSelector(node);
-    if (element == null) {
-      ir.Primitive closureTarget = visit(node.selector);
-      List<ir.Primitive> args =
-          node.arguments.mapToList(visit, growable:false);
-      return irBuilder.buildFunctionExpressionInvocation(
-          closureTarget, elements.getSelector(node), args);
-    } else {
-      List<ir.Primitive> args =
-          node.arguments.mapToList(visit, growable:false);
-      return irBuilder.buildLocalInvocation(
-          element, elements.getSelector(node), args);
-    }
+    Selector selector = elements.getSelector(node);
+    ir.Primitive receiver = (element == null)
+        ? visit(node.selector)
+        : irBuilder.buildLocalGet(element);
+    List<ir.Primitive> arguments = node.arguments.mapToList(visit);
+    arguments = normalizeDynamicArguments(selector, arguments);
+    return irBuilder.buildCallInvocation(receiver, selector, arguments);
   }
 
   /// If [node] is null, returns this.
@@ -583,10 +602,8 @@
     assert(irBuilder.isOpen);
     Selector selector = elements.getSelector(node);
     ir.Primitive receiver = visitReceiver(node.receiver);
-    List<ir.Primitive> arguments = new List<ir.Primitive>();
-    for (ast.Node n in node.arguments) {
-      arguments.add(visit(n));
-    }
+    List<ir.Primitive> arguments = node.arguments.mapToList(visit);
+    arguments = normalizeDynamicArguments(selector, arguments);
     return irBuilder.buildDynamicInvocation(receiver, selector, arguments);
   }
 
@@ -712,10 +729,31 @@
 
     Selector selector = elements.getSelector(node);
 
-    // TODO(lry): support default arguments, need support for locals.
-    List<ir.Primitive> arguments =
-        node.arguments.mapToList(visit, growable:false);
-    return irBuilder.buildStaticInvocation(element, selector, arguments);
+    if (selector.isCall && (element.isGetter || element.isField)) {
+      // We are invoking a static field or getter as if it was a method, e.g:
+      //
+      //     get foo => {..}
+      //     main() {  foo(1, 2, 3);  }
+      //
+      // We invoke the getter of 'foo' and then invoke the 'call' method
+      // on the result, using the given arguments.
+      Selector getter = new Selector.getterFrom(selector);
+      Selector call = new Selector.callClosureFrom(selector);
+      ir.Primitive receiver = irBuilder.buildStaticGet(element, getter);
+      List<ir.Primitive> arguments = node.arguments.mapToList(visit);
+      arguments = normalizeDynamicArguments(selector, arguments);
+      return irBuilder.buildCallInvocation(receiver, call, arguments);
+    } else if (selector.isGetter) {
+      // We are reading a static field or invoking a static getter.
+      return irBuilder.buildStaticGet(element, selector);
+    } else {
+      // We are invoking a static method.
+      assert(selector.isCall);
+      assert(element is FunctionElement);
+      List<ir.Primitive> arguments = node.arguments.mapToList(visit);
+      arguments = normalizeStaticArguments(selector, element, arguments);
+      return irBuilder.buildStaticInvocation(element, selector, arguments);
+    }
   }
 
   ir.Primitive visitSuperSend(ast.Send node) {
@@ -725,9 +763,9 @@
     } else {
       Selector selector = elements.getSelector(node);
       Element target = elements[node];
-      List<ir.Primitive> arguments = new List<ir.Primitive>();
-      for (ast.Node n in node.arguments) {
-        arguments.add(visit(n));
+      List<ir.Primitive> arguments = node.arguments.mapToList(visit);
+      if (selector.isCall) {
+        arguments = normalizeStaticArguments(selector, target, arguments);
       }
       return irBuilder.buildSuperInvocation(target, selector, arguments);
     }
@@ -861,8 +899,9 @@
     Selector selector = elements.getSelector(node.send);
     DartType type = elements.getType(node);
     ast.Node selectorNode = node.send.selector;
-    List<ir.Definition> arguments =
+    List<ir.Primitive> arguments =
         node.send.arguments.mapToList(visit, growable:false);
+    arguments = normalizeStaticArguments(selector, element, arguments);
     return irBuilder.buildConstructorInvocation(
         element, selector, type, arguments);
   }
@@ -887,12 +926,9 @@
     return irBuilder.buildStringConcatenation(arguments);
   }
 
-  ir.Primitive translateConstant(ast.Node node, [ConstantExpression constant]) {
+  ir.Primitive translateConstant(ast.Node node) {
     assert(irBuilder.isOpen);
-    if (constant == null) {
-      constant = getConstantForNode(node);
-    }
-    return irBuilder.buildConstantLiteral(constant);
+    return irBuilder.buildConstantLiteral(getConstantForNode(node));
   }
 
   ir.ExecutableDefinition nullIfGiveup(ir.ExecutableDefinition action()) {
@@ -1107,6 +1143,19 @@
 
     return withBuilder(builder, () => _makeFunctionBody(element, node));
   }
+
+  List<ir.Primitive> normalizeStaticArguments(
+      Selector selector,
+      FunctionElement target,
+      List<ir.Primitive> arguments) {
+    return arguments;
+  }
+
+  List<ir.Primitive> normalizeDynamicArguments(
+      Selector selector,
+      List<ir.Primitive> arguments) {
+    return arguments;
+  }
 }
 
 /// IR builder specific to the JavaScript backend, coupled to the [JsIrBuilder].
@@ -1242,6 +1291,18 @@
     return visitor.withBuilder(irBuilder, () => visitor.visit(expression));
   }
 
+  /// Builds the IR for a constant taken from a different [context].
+  ///
+  /// Such constants need to be compiled with a different [sourceFile] and
+  /// [elements] mapping.
+  ir.Primitive inlineConstant(AstElement context, ast.Expression exp) {
+    JsIrBuilderVisitor visitor = new JsIrBuilderVisitor(
+        context.resolvedAst.elements,
+        compiler,
+        elementSourceFile(context));
+    return visitor.withBuilder(irBuilder, () => visitor.translateConstant(exp));
+  }
+
   /// Builds the IR for a given constructor.
   ///
   /// 1. Evaluates all own or inherited field initializers.
@@ -1408,8 +1469,9 @@
       ir.Primitive value;
       // Load argument if provided.
       if (signature.optionalParametersAreNamed) {
-        int translatedIndex = selector.namedArguments.indexOf(param.name);
-        if (translatedIndex != -1) {
+        int nameIndex = selector.namedArguments.indexOf(param.name);
+        if (nameIndex != -1) {
+          int translatedIndex = selector.positionalArgumentCount + nameIndex;
           value = arguments[translatedIndex];
         }
       } else if (index < arguments.length) {
@@ -1418,7 +1480,7 @@
       // Load default if argument was not provided.
       if (value == null) {
         if (param.initializer != null) {
-          value = visit(param.initializer);
+          value = inlineExpression(target, param.initializer);
         } else {
           value = irBuilder.buildNullLiteral();
         }
@@ -1545,5 +1607,80 @@
     return withBuilder(builder, () => _makeFunctionBody(element, node));
   }
 
+  /// Creates a primitive for the default value of [parameter].
+  ir.Primitive translateDefaultValue(ParameterElement parameter) {
+    if (parameter.initializer == null) {
+      return irBuilder.buildNullLiteral();
+    } else {
+      return inlineConstant(parameter.executableContext, parameter.initializer);
+    }
+  }
+
+  /// Inserts default arguments and normalizes order of named arguments.
+  List<ir.Primitive> normalizeStaticArguments(
+      Selector selector,
+      FunctionElement target,
+      List<ir.Primitive> arguments) {
+    target = target.implementation;
+    FunctionSignature signature = target.functionSignature;
+    if (!signature.optionalParametersAreNamed &&
+        signature.parameterCount == arguments.length) {
+      // Optimization: don't copy the argument list for trivial cases.
+      return arguments;
+    }
+
+    List<ir.Primitive> result = <ir.Primitive>[];
+    int i = 0;
+    signature.forEachRequiredParameter((ParameterElement element) {
+      result.add(arguments[i]);
+      ++i;
+    });
+
+    if (!signature.optionalParametersAreNamed) {
+      signature.forEachOptionalParameter((ParameterElement element) {
+        if (i < arguments.length) {
+          result.add(arguments[i]);
+          ++i;
+        } else {
+          result.add(translateDefaultValue(element));
+        }
+      });
+    } else {
+      int offset = i;
+      // Iterate over the optional parameters of the signature, and try to
+      // find them in [compiledNamedArguments]. If found, we use the
+      // value in the temporary list, otherwise the default value.
+      signature.orderedOptionalParameters.forEach((ParameterElement element) {
+        int nameIndex = selector.namedArguments.indexOf(element.name);
+        if (nameIndex != -1) {
+          int translatedIndex = offset + nameIndex;
+          result.add(arguments[translatedIndex]);
+        } else {
+          result.add(translateDefaultValue(element));
+        }
+      });
+    }
+    return result;
+  }
+
+  /// Normalizes order of named arguments.
+  List<ir.Primitive> normalizeDynamicArguments(
+      Selector selector,
+      List<ir.Primitive> arguments) {
+    assert(arguments.length == selector.argumentCount);
+    // Optimization: don't copy the argument list for trivial cases.
+    if (selector.namedArguments.isEmpty) return arguments;
+    List<ir.Primitive> result = <ir.Primitive>[];
+    for (int i=0; i < selector.positionalArgumentCount; i++) {
+      result.add(arguments[i]);
+    }
+    for (String argName in selector.getOrderedNamedArguments()) {
+      int nameIndex = selector.namedArguments.indexOf(argName);
+      int translatedIndex = selector.positionalArgumentCount + nameIndex;
+      result.add(arguments[translatedIndex]);
+    }
+    return result;
+  }
+
 }
 
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 4aad6af..1fbf543 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -114,7 +114,6 @@
   bool stripArgumentSet = false;
   bool analyzeOnly = false;
   bool analyzeAll = false;
-  bool enableAsyncAwait = false;
   bool allowNativeExtensions = false;
   bool trustTypeAnnotations = false;
   bool trustPrimitives = false;
@@ -192,11 +191,6 @@
     passThrough(argument);
   }
 
-  setEnableAsync(String argument) {
-    enableAsyncAwait = true;
-    passThrough(argument);
-  }
-
   setAllowNativeExtensions(String argument) {
     allowNativeExtensions = true;
     passThrough(argument);
@@ -340,7 +334,12 @@
     new OptionHandler('--show-package-warnings', passThrough),
     new OptionHandler('--csp', passThrough),
     new OptionHandler('--enable-experimental-mirrors', passThrough),
-    new OptionHandler('--enable-async', setEnableAsync),
+    new OptionHandler('--enable-async', (_) {
+      diagnosticHandler.info(
+          "Option '--enable-async' is no longer needed. "
+          "Async-await is supported by default.",
+          api.Diagnostic.HINT);
+    }),
     new OptionHandler('--enable-enum', (_) {
       diagnosticHandler.info(
           "Option '--enable-enum' is no longer needed. "
@@ -410,10 +409,6 @@
   }
   if (analyzeAll) analyzeOnly = true;
   if (!analyzeOnly) {
-    if (enableAsyncAwait && outputLanguage != OUTPUT_LANGUAGE_DART) {
-      helpAndFail("Option '--enable-async' is currently only supported in "
-                  "combination with the '--analyze-only' option.");
-    }
     if (allowNativeExtensions) {
       helpAndFail("Option '--allow-native-extensions' is only supported in "
                   "combination with the '--analyze-only' option.");
diff --git a/pkg/compiler/lib/src/dart2jslib.dart b/pkg/compiler/lib/src/dart2jslib.dart
index fcfbf5e..19700eb 100644
--- a/pkg/compiler/lib/src/dart2jslib.dart
+++ b/pkg/compiler/lib/src/dart2jslib.dart
@@ -33,6 +33,7 @@
          DeferredLoaderGetterElementX;
 import 'helpers/helpers.dart';  // Included for debug helpers.
 import 'io/code_output.dart' show CodeBuffer;
+import 'io/source_information.dart' show SourceInformation;
 import 'js/js.dart' as js;
 import 'js_backend/js_backend.dart' as js_backend;
 import 'library_loader.dart'
diff --git a/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart b/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart
index 01a9ae0..2779663 100644
--- a/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart
+++ b/pkg/compiler/lib/src/dart_backend/backend_ast_emitter.dart
@@ -434,7 +434,7 @@
   Expression makeAssignment(Expression target, Expression value) {
     // Try to print as compound assignment or increment
     if (value is BinaryOperator && isCompoundableOperator(value.operator)) {
-      Expression leftOperand = value.left;
+      Receiver leftOperand = value.left;
       Expression rightOperand = value.right;
       bool valid = false;
       if (isSameVariable(target, leftOperand)) {
diff --git a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
index dea26fa..020b81b 100644
--- a/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/simple_types_inferrer.dart
@@ -541,10 +541,11 @@
         locals.update(element, parameterType, node);
       });
       ClassElement cls = analyzedElement.enclosingClass;
+      Spannable spannable = node;
       if (analyzedElement.isSynthesized) {
-        node = analyzedElement;
+        spannable = analyzedElement;
         ConstructorElement constructor = analyzedElement;
-        synthesizeForwardingCall(node, constructor.definingConstructor);
+        synthesizeForwardingCall(spannable, constructor.definingConstructor);
       } else {
         visitingInitializers = true;
         visit(node.initializers);
@@ -570,7 +571,8 @@
           if (field.isFinal) return;
           T type = locals.fieldScope.readField(field);
           if (type == null && field.initializer == null) {
-            inferrer.recordTypeOfNonFinalField(node, field, types.nullType);
+            inferrer.recordTypeOfNonFinalField(
+                spannable, field, types.nullType);
           }
         });
       }
@@ -580,7 +582,12 @@
         locals.update(element, inferrer.typeOfElement(element), node);
       });
       visit(node.body);
-      if (returnType == null) {
+      if (function.asyncMarker != AsyncMarker.SYNC) {
+        // TODO(herhut): Should be type Future/Iterable/Stream instead of
+        // dynamic.
+        returnType = inferrer.addReturnTypeFor(
+            analyzedElement, returnType, types.dynamicType);
+      } else if (returnType == null) {
         // No return in the body.
         returnType = locals.seenReturnOrThrow
             ? types.nonNullEmpty()  // Body always throws.
@@ -995,6 +1002,12 @@
     return null;
   }
 
+  T visitAwait(ast.Await node) {
+    T futureType = node.expression.accept(this);
+    // TODO(herhut): Return a better type here if possible.
+    return types.dynamicType;
+  }
+
   T visitStaticSend(ast.Send node) {
     Element element = elements[node];
     if (elements.isAssert(node)) {
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
index f60b4c5..7304661 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_inferrer.dart
@@ -45,6 +45,7 @@
     show ImmutableEmptySet,
          Setlet,
          Spannable;
+import '../js_backend/js_backend.dart' show Annotations, JavaScriptBackend;
 
 import 'inferrer_visitor.dart'
     show ArgumentsTypes,
@@ -559,6 +560,9 @@
   TypeGraphInferrerEngine(Compiler compiler, this.mainElement)
         : super(compiler, new TypeInformationSystem(compiler));
 
+  JavaScriptBackend get backend => compiler.backend;
+  Annotations get annotations => backend.annotations;
+
   /**
    * A set of selector names that [List] implements, that we know return
    * their element type.
diff --git a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
index 36aee09..6a9610d 100644
--- a/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
+++ b/pkg/compiler/lib/src/inferrer/type_graph_nodes.dart
@@ -392,8 +392,11 @@
 
   TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) {
     if (element.isField &&
-        !inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) {
-      // Do not infer types for fields being assigned by synthesized calls.
+        (!inferrer.backend.canBeUsedForGlobalOptimizations(element) ||
+         inferrer.annotations.assumeDynamic(element))) {
+      // Do not infer types for fields that have a corresponding annotation or
+      // are assigned by synthesized calls
+
       giveUp(inferrer);
       return safeType(inferrer);
     }
@@ -437,7 +440,9 @@
   TypeMask potentiallyNarrowType(TypeMask mask,
                                  TypeGraphInferrerEngine inferrer) {
     Compiler compiler = inferrer.compiler;
-    if (!compiler.trustTypeAnnotations && !compiler.enableTypeAssertions) {
+    if (!compiler.trustTypeAnnotations &&
+        !compiler.enableTypeAssertions &&
+        !inferrer.annotations.trustTypeAnnotations(element)) {
       return mask;
     }
     if (element.isGenerativeConstructor ||
@@ -496,6 +501,7 @@
  */
 class ParameterTypeInformation extends ElementTypeInformation {
   ParameterElement get element => super.element;
+  FunctionElement get declaration => element.functionDeclaration;
 
   ParameterTypeInformation._internal(ParameterElement element,
                                      TypeInformationSystem types)
@@ -522,9 +528,10 @@
 
   // TODO(herhut): Cleanup into one conditional.
   TypeMask handleSpecialCases(TypeGraphInferrerEngine inferrer) {
-    if (!inferrer.compiler.backend.canBeUsedForGlobalOptimizations(element)) {
-      // Do not infer types for fields and parameters being assigned
-      // by synthesized calls.
+    if (!inferrer.backend.canBeUsedForGlobalOptimizations(element) ||
+        inferrer.annotations.assumeDynamic(declaration)) {
+      // Do not infer types for parameters that have a correspondign annotation
+      // or that are assigned by synthesized calls.
       giveUp(inferrer);
       return safeType(inferrer);
     }
@@ -567,7 +574,8 @@
   TypeMask potentiallyNarrowType(TypeMask mask,
                                  TypeGraphInferrerEngine inferrer) {
     Compiler compiler = inferrer.compiler;
-    if (!compiler.trustTypeAnnotations) {
+    if (!compiler.trustTypeAnnotations &&
+        !inferrer.annotations.trustTypeAnnotations(declaration)) {
       return mask;
     }
     // When type assertions are enabled (aka checked mode), we have to always
diff --git a/pkg/compiler/lib/src/io/code_output.dart b/pkg/compiler/lib/src/io/code_output.dart
index bd97af0..5f13198 100644
--- a/pkg/compiler/lib/src/io/code_output.dart
+++ b/pkg/compiler/lib/src/io/code_output.dart
@@ -6,7 +6,7 @@
 
 import 'dart:async';
 
-import 'source_map_builder.dart';
+import 'source_information.dart';
 
 class CodeOutputMarker {
   final int offsetDelta;
diff --git a/pkg/compiler/lib/src/io/source_information.dart b/pkg/compiler/lib/src/io/source_information.dart
new file mode 100644
index 0000000..c0e6e0a8
--- /dev/null
+++ b/pkg/compiler/lib/src/io/source_information.dart
@@ -0,0 +1,164 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library dart2js.source_information;
+
+import '../dart2jslib.dart' show SourceSpan;
+import '../elements/elements.dart' show AstElement;
+import '../scanner/scannerlib.dart' show Token;
+import '../tree/tree.dart' show Node;
+import '../js/js.dart' show JavaScriptNodeSourceInformation;
+import 'code_output.dart';
+import 'source_file.dart';
+
+/// Interface for passing source information, for instance for use in source
+/// maps, through the backend.
+abstract class SourceInformation extends JavaScriptNodeSourceInformation {
+  SourceSpan get sourceSpan;
+  void beginMapping(CodeOutput output);
+  void endMapping(CodeOutput output);
+}
+
+/// Source information that contains start source position and optionally an
+/// end source position.
+class StartEndSourceInformation implements SourceInformation {
+  final SourceFileLocation startPosition;
+  final SourceFileLocation endPosition;
+
+  StartEndSourceInformation(this.startPosition, [this.endPosition]);
+
+  SourceSpan get sourceSpan {
+    Uri uri = Uri.parse(startPosition.sourceFile.filename);
+    int begin = startPosition.offset;
+    int end = endPosition == null ? begin : endPosition.offset;
+    return new SourceSpan(uri, begin, end);
+  }
+
+  void beginMapping(CodeBuffer output) {
+    output.beginMappedRange();
+    output.setSourceLocation(startPosition);
+  }
+
+  void endMapping(CodeBuffer output) {
+    if (endPosition != null) {
+      output.setSourceLocation(endPosition);
+    }
+    output.endMappedRange();
+  }
+
+  int get hashCode {
+    return (startPosition.hashCode * 17 +
+            endPosition.hashCode * 19)
+           & 0x7FFFFFFF;
+  }
+
+  bool operator ==(other) {
+    if (identical(this, other)) return true;
+    if (other is! StartEndSourceInformation) return false;
+    return startPosition == other.startPosition &&
+           endPosition == other.endPosition;
+  }
+
+  // TODO(johnniwinther): Remove this method. Source information should be
+  // computed based on the element by provided from statements and expressions.
+  static StartEndSourceInformation computeSourceInformation(
+      AstElement element) {
+
+    AstElement implementation = element.implementation;
+    SourceFile sourceFile = implementation.compilationUnit.script.file;
+    String name = element.name;
+    Node node = implementation.node;
+    Token beginToken;
+    Token endToken;
+    if (node == null) {
+      // Synthesized node. Use the enclosing element for the location.
+      beginToken = endToken = element.position;
+    } else {
+      beginToken = node.getBeginToken();
+      endToken = node.getEndToken();
+    }
+    // TODO(podivilov): find the right sourceFile here and remove offset
+    // checks below.
+    SourceFileLocation sourcePosition, endSourcePosition;
+    if (beginToken.charOffset < sourceFile.length) {
+      sourcePosition =
+          new TokenSourceFileLocation(sourceFile, beginToken, name);
+    }
+    if (endToken.charOffset < sourceFile.length) {
+      endSourcePosition =
+          new TokenSourceFileLocation(sourceFile, endToken, name);
+    }
+    return new StartEndSourceInformation(sourcePosition, endSourcePosition);
+  }
+
+  String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.write('${startPosition.getSourceUrl()}:');
+    sb.write('[${startPosition.getLine()},${startPosition.getColumn()}]');
+    if (endPosition != null) {
+      sb.write('-[${endPosition.getLine()},${endPosition.getColumn()}]');
+    }
+    return sb.toString();
+  }
+}
+
+// TODO(johnniwinther): Refactor this class to use getters.
+abstract class SourceFileLocation {
+  SourceFile sourceFile;
+
+  SourceFileLocation(this.sourceFile) {
+    assert(isValid());
+  }
+
+  int line;
+
+  int get offset;
+
+  String getSourceUrl() => sourceFile.filename;
+
+  int getLine() {
+    if (line == null) line = sourceFile.getLine(offset);
+    return line;
+  }
+
+  int getColumn() => sourceFile.getColumn(getLine(), offset);
+
+  String getSourceName();
+
+  bool isValid() => offset < sourceFile.length;
+
+  int get hashCode {
+    return getSourceUrl().hashCode * 17 +
+           offset.hashCode * 17 +
+           getSourceName().hashCode * 23;
+  }
+
+  bool operator ==(other) {
+    if (identical(this, other)) return true;
+    if (other is! SourceFileLocation) return false;
+    return getSourceUrl() == other.getSourceUrl() &&
+           offset == other.offset &&
+           getSourceName() == other.getSourceName();
+  }
+
+  String toString() => '${getSourceUrl()}:[${getLine()},${getColumn()}]';
+}
+
+class TokenSourceFileLocation extends SourceFileLocation {
+  final Token token;
+  final String name;
+
+  TokenSourceFileLocation(SourceFile sourceFile, this.token, this.name)
+    : super(sourceFile);
+
+  int get offset => token.charOffset;
+
+  String getSourceName() {
+    return name;
+  }
+
+  String toString() {
+    return '${super.toString()}:$name';
+  }
+}
diff --git a/pkg/compiler/lib/src/io/source_map_builder.dart b/pkg/compiler/lib/src/io/source_map_builder.dart
index 117e298..ed546f2 100644
--- a/pkg/compiler/lib/src/io/source_map_builder.dart
+++ b/pkg/compiler/lib/src/io/source_map_builder.dart
@@ -5,10 +5,9 @@
 library dart2js.source_map_builder;
 
 import '../util/util.dart';
-import '../scanner/scannerlib.dart' show Token;
 import '../util/uri_extras.dart' show relativize;
 import 'line_column_provider.dart';
-import 'source_file.dart';
+import 'source_information.dart' show SourceFileLocation;
 
 class SourceMapBuilder {
   static const int VLQ_BASE_SHIFT = 5;
@@ -230,42 +229,3 @@
 
   SourceMapEntry(this.sourceLocation, this.targetOffset);
 }
-
-abstract class SourceFileLocation {
-  SourceFile sourceFile;
-
-  SourceFileLocation(this.sourceFile) {
-    assert(isValid());
-  }
-
-  int line;
-
-  int get offset;
-
-  String getSourceUrl() => sourceFile.filename;
-
-  int getLine() {
-    if (line == null) line = sourceFile.getLine(offset);
-    return line;
-  }
-
-  int getColumn() => sourceFile.getColumn(getLine(), offset);
-
-  String getSourceName();
-
-  bool isValid() => offset < sourceFile.length;
-}
-
-class TokenSourceFileLocation extends SourceFileLocation {
-  final Token token;
-  final String name;
-
-  TokenSourceFileLocation(SourceFile sourceFile, this.token, this.name)
-    : super(sourceFile);
-
-  int get offset => token.charOffset;
-
-  String getSourceName() {
-    return name;
-  }
-}
diff --git a/pkg/compiler/lib/src/js/builder.dart b/pkg/compiler/lib/src/js/builder.dart
index 3eeca5e..72b4507 100644
--- a/pkg/compiler/lib/src/js/builder.dart
+++ b/pkg/compiler/lib/src/js/builder.dart
@@ -5,7 +5,7 @@
 // Utilities for building JS ASTs at runtime.  Contains a builder class
 // and a parser that parses part of the language.
 
-part of js;
+part of js_ast;
 
 
 /**
@@ -349,8 +349,8 @@
 ArrayInitializer numArray(Iterable<int> list) => js.numArray(list);
 ArrayInitializer stringArray(Iterable<String> list) => js.stringArray(list);
 Call propertyCall(Expression receiver,
-                    String fieldName,
-                    List<Expression> arguments) {
+                  String fieldName,
+                  List<Expression> arguments) {
   return js.propertyCall(receiver, fieldName, arguments);
 }
 
@@ -716,6 +716,8 @@
         return new LiteralNull();
       } else if (last == "function") {
         return parseFunctionExpression();
+      } else if (last == "this") {
+        return new This();
       } else {
         return new VariableUse(last);
       }
@@ -1116,9 +1118,12 @@
 
       if (lastToken == 'default') error("Default outside switch.");
 
+      if (lastToken == 'yield') return parseYield();
+
       if (lastToken == 'with') {
         error('Not implemented in mini parser');
       }
+
     }
 
     bool checkForInterpolatedStatement = lastCategory == HASH;
@@ -1153,6 +1158,13 @@
     return new Return(expression);
   }
 
+  Statement parseYield() {
+    bool hasStar = acceptString('*');
+    Expression expression = parseExpression();
+    expectSemicolon();
+    return new DartYield(expression, hasStar);
+  }
+
   Statement parseThrow() {
     if (skippedNewline) error('throw expression must be on same line');
     Expression expression = parseExpression();
diff --git a/pkg/compiler/lib/src/js/characters.dart b/pkg/compiler/lib/src/js/characters.dart
new file mode 100644
index 0000000..ae6740f
--- /dev/null
+++ b/pkg/compiler/lib/src/js/characters.dart
@@ -0,0 +1,117 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library js_character_codes;
+
+const int $EOF = 0;
+const int $STX = 2;
+const int $BS  = 8;
+const int $TAB = 9;
+const int $LF = 10;
+const int $VTAB = 11;
+const int $FF = 12;
+const int $CR = 13;
+const int $SPACE = 32;
+const int $BANG = 33;
+const int $DQ = 34;
+const int $HASH = 35;
+const int $$ = 36;
+const int $PERCENT = 37;
+const int $AMPERSAND = 38;
+const int $SQ = 39;
+const int $OPEN_PAREN = 40;
+const int $CLOSE_PAREN = 41;
+const int $STAR = 42;
+const int $PLUS = 43;
+const int $COMMA = 44;
+const int $MINUS = 45;
+const int $PERIOD = 46;
+const int $SLASH = 47;
+const int $0 = 48;
+const int $1 = 49;
+const int $2 = 50;
+const int $3 = 51;
+const int $4 = 52;
+const int $5 = 53;
+const int $6 = 54;
+const int $7 = 55;
+const int $8 = 56;
+const int $9 = 57;
+const int $COLON = 58;
+const int $SEMICOLON = 59;
+const int $LT = 60;
+const int $EQ = 61;
+const int $GT = 62;
+const int $QUESTION = 63;
+const int $AT = 64;
+const int $A = 65;
+const int $B = 66;
+const int $C = 67;
+const int $D = 68;
+const int $E = 69;
+const int $F = 70;
+const int $G = 71;
+const int $H = 72;
+const int $I = 73;
+const int $J = 74;
+const int $K = 75;
+const int $L = 76;
+const int $M = 77;
+const int $N = 78;
+const int $O = 79;
+const int $P = 80;
+const int $Q = 81;
+const int $R = 82;
+const int $S = 83;
+const int $T = 84;
+const int $U = 85;
+const int $V = 86;
+const int $W = 87;
+const int $X = 88;
+const int $Y = 89;
+const int $Z = 90;
+const int $OPEN_SQUARE_BRACKET = 91;
+const int $BACKSLASH = 92;
+const int $CLOSE_SQUARE_BRACKET = 93;
+const int $CARET = 94;
+const int $_ = 95;
+const int $BACKPING = 96;
+const int $a = 97;
+const int $b = 98;
+const int $c = 99;
+const int $d = 100;
+const int $e = 101;
+const int $f = 102;
+const int $g = 103;
+const int $h = 104;
+const int $i = 105;
+const int $j = 106;
+const int $k = 107;
+const int $l = 108;
+const int $m = 109;
+const int $n = 110;
+const int $o = 111;
+const int $p = 112;
+const int $q = 113;
+const int $r = 114;
+const int $s = 115;
+const int $t = 116;
+const int $u = 117;
+const int $v = 118;
+const int $w = 119;
+const int $x = 120;
+const int $y = 121;
+const int $z = 122;
+const int $OPEN_CURLY_BRACKET = 123;
+const int $BAR = 124;
+const int $CLOSE_CURLY_BRACKET = 125;
+const int $TILDE = 126;
+const int $DEL = 127;
+const int $NBSP = 160;
+const int $LS = 0x2028;
+const int $PS = 0x2029;
+
+const int $FIRST_SURROGATE = 0xd800;
+const int $LAST_SURROGATE = 0xdfff;
+const int $LAST_CODE_POINT = 0x10ffff;
diff --git a/pkg/compiler/lib/src/js/js.dart b/pkg/compiler/lib/src/js/js.dart
index dc67462..a5bbec4 100644
--- a/pkg/compiler/lib/src/js/js.dart
+++ b/pkg/compiler/lib/src/js/js.dart
@@ -1,22 +1,64 @@
-// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
 library js;
 
-import 'precedence.dart';
-import '../util/characters.dart' as charCodes;
-import '../util/util.dart';
+// TODO(sra): This will become a package import.
+import 'js_ast.dart';
+export 'js_ast.dart';
+
 import '../io/code_output.dart' show CodeBuffer;
+import '../io/source_information.dart' show SourceInformation;
 import '../js_emitter/js_emitter.dart' show USE_NEW_EMITTER;
-
-// TODO(floitsch): remove this dependency (currently necessary for the
-// CodeBuffer).
 import '../dart2jslib.dart' as leg;
+import '../util/util.dart' show NO_LOCATION_SPANNABLE;
+import '../dump_info.dart' show DumpInfoTask;
 
-import '../dump_info.dart';
 
-part 'nodes.dart';
-part 'builder.dart';
-part 'printer.dart';
-part 'template.dart';
+CodeBuffer prettyPrint(Node node, leg.Compiler compiler,
+                       {DumpInfoTask monitor,
+                        bool allowVariableMinification: true}) {
+  JavaScriptPrintingOptions options = new JavaScriptPrintingOptions(
+      shouldCompressOutput: compiler.enableMinification,
+      minifyLocalVariables: allowVariableMinification,
+      preferSemicolonToNewlineInMinifiedOutput: USE_NEW_EMITTER);
+  Dart2JSJavaScriptPrintingContext context =
+      new Dart2JSJavaScriptPrintingContext(compiler, monitor);
+  Printer printer = new Printer(options, context);
+  printer.visit(node);
+  return context.outBuffer;
+}
+
+class Dart2JSJavaScriptPrintingContext implements JavaScriptPrintingContext {
+  final leg.Compiler compiler;
+  final DumpInfoTask monitor;
+  final CodeBuffer outBuffer = new CodeBuffer();
+
+  Dart2JSJavaScriptPrintingContext(leg.Compiler this.compiler,
+      DumpInfoTask this.monitor);
+
+  void error(String message) {
+    compiler.internalError(NO_LOCATION_SPANNABLE, message);
+  }
+
+  void emit(String string) {
+    outBuffer.add(string);
+  }
+
+  void enterNode(Node node) {
+    SourceInformation sourceInformation = node.sourceInformation;
+    if (sourceInformation != null) {
+      sourceInformation.beginMapping(outBuffer);
+    }
+    if (monitor != null) monitor.enteringAst(node, outBuffer.length);
+  }
+
+  void exitNode(Node node) {
+    if (monitor != null) monitor.exitingAst(node, outBuffer.length);
+    SourceInformation sourceInformation = node.sourceInformation;
+    if (sourceInformation != null) {
+      sourceInformation.endMapping(outBuffer);
+    }
+  }
+}
diff --git a/pkg/compiler/lib/src/js/js_ast.dart b/pkg/compiler/lib/src/js/js_ast.dart
new file mode 100644
index 0000000..ee8289e
--- /dev/null
+++ b/pkg/compiler/lib/src/js/js_ast.dart
@@ -0,0 +1,13 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library js_ast;
+
+import 'precedence.dart';
+import 'characters.dart' as charCodes;
+
+part 'nodes.dart';
+part 'builder.dart';
+part 'printer.dart';
+part 'template.dart';
diff --git a/pkg/compiler/lib/src/js/nodes.dart b/pkg/compiler/lib/src/js/nodes.dart
index e5b57f6..92446fa 100644
--- a/pkg/compiler/lib/src/js/nodes.dart
+++ b/pkg/compiler/lib/src/js/nodes.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.
 
-part of js;
+part of js_ast;
 
 abstract class NodeVisitor<T> {
   T visitProgram(Program node);
@@ -27,8 +27,8 @@
   T visitFunctionDeclaration(FunctionDeclaration node);
   T visitLabeledStatement(LabeledStatement node);
   T visitLiteralStatement(LiteralStatement node);
+  T visitDartYield(DartYield node);
 
-  T visitBlob(Blob node);
   T visitLiteralExpression(LiteralExpression node);
   T visitVariableDeclarationList(VariableDeclarationList node);
   T visitAssignment(Assignment node);
@@ -108,7 +108,6 @@
   T visitDefault(Default node) => visitNode(node);
 
   T visitExpression(Expression node) => visitNode(node);
-  T visitBlob(Blob node) => visitExpression(node);
   T visitVariableReference(VariableReference node) => visitExpression(node);
 
   T visitLiteralExpression(LiteralExpression node) => visitExpression(node);
@@ -169,14 +168,17 @@
   T visitComment(Comment node) => null;
 
   T visitAwait(Await node) => visitExpression(node);
+  T visitDartYield(DartYield node) => visitStatement(node);
 }
 
-abstract class Node {
-  get sourcePosition => _sourcePosition;
-  get endSourcePosition => _endSourcePosition;
+/// This tag interface has no behaviour but must be implemented by any class
+/// that is to be stored on a [Node] as source information.
+abstract class JavaScriptNodeSourceInformation {}
 
-  var _sourcePosition;
-  var _endSourcePosition;
+abstract class Node {
+  JavaScriptNodeSourceInformation get sourceInformation => _sourceInformation;
+
+  JavaScriptNodeSourceInformation _sourceInformation;
 
   accept(NodeVisitor visitor);
   void visitChildren(NodeVisitor visitor);
@@ -187,24 +189,18 @@
 
   // Returns a node equivalent to [this], but with new source position and end
   // source position.
-  Node withPosition(var sourcePosition, var endSourcePosition) {
-    if (sourcePosition == _sourcePosition &&
-        endSourcePosition == _endSourcePosition) {
+  Node withSourceInformation(
+      JavaScriptNodeSourceInformation sourceInformation) {
+    if (sourceInformation == _sourceInformation) {
       return this;
     }
     Node clone = _clone();
     // TODO(sra): Should existing data be 'sticky' if we try to overwrite with
     // `null`?
-    clone._sourcePosition = sourcePosition;
-    clone._endSourcePosition = endSourcePosition;
+    clone._sourceInformation = sourceInformation;
     return clone;
   }
 
-  // Returns a node equivalent to [this], but with new [this.sourcePositions],
-  // keeping the existing [endPosition]
-  Node withLocation(var sourcePosition) =>
-      withPosition(sourcePosition, this.endSourcePosition);
-
   VariableUse asVariableUse() => null;
 
   bool get isCommaOperator => false;
@@ -227,9 +223,6 @@
 
 abstract class Statement extends Node {
   Statement toStatement() => this;
-
-  Statement withPosition(var sourcePosition, var endSourcePosition) =>
-      super.withPosition(sourcePosition, endSourcePosition);
 }
 
 class Block extends Statement {
@@ -532,32 +525,28 @@
   LiteralStatement _clone() => new LiteralStatement(code);
 }
 
+// Not a real JavaScript node, but represents the yield statement from a dart
+// program translated to JavaScript.
+class DartYield extends Statement {
+  final Expression expression;
+
+  final bool hasStar;
+
+  DartYield(this.expression, this.hasStar);
+
+  accept(NodeVisitor visitor) => visitor.visitDartYield(this);
+
+  void visitChildren(NodeVisitor visitor) {
+    expression.accept(visitor);
+  }
+
+  DartYield _clone() => new DartYield(expression, hasStar);
+}
+
 abstract class Expression extends Node {
   int get precedenceLevel;
 
   Statement toStatement() => new ExpressionStatement(this);
-
-  Expression withPosition(var sourcePosition, var endSourcePosition) =>
-      super.withPosition(sourcePosition, endSourcePosition);
-}
-
-/// Wrap a CodeBuffer as an expression.
-class Blob extends Expression {
-  // TODO(ahe): This class is an aid to convert everything to ASTs, remove when
-  // not needed anymore.
-
-  final CodeBuffer buffer;
-
-  Blob(this.buffer);
-
-  accept(NodeVisitor visitor) => visitor.visitBlob(this);
-
-  void visitChildren(NodeVisitor visitor) {}
-
-  Blob _clone() => new Blob(buffer);
-
-  int get precedenceLevel => PRIMARY;
-
 }
 
 class LiteralExpression extends Expression {
@@ -862,7 +851,7 @@
     body.accept(visitor);
   }
 
-  Fun _clone() => new Fun(params, body);
+  Fun _clone() => new Fun(params, body, asyncModifier: asyncModifier);
 
   int get precedenceLevel => CALL;
 }
@@ -870,11 +859,25 @@
 class AsyncModifier {
   final bool isAsync;
   final bool isYielding;
+  final String description;
 
-  const AsyncModifier.sync() : isAsync = false, isYielding = false;
-  const AsyncModifier.async() : isAsync = true, isYielding = false;
-  const AsyncModifier.asyncStar() : isAsync = true, isYielding = true;
-  const AsyncModifier.syncStar() : isAsync = false, isYielding = true;
+  const AsyncModifier.sync()
+      : isAsync = false,
+        isYielding = false,
+        description = "sync";
+  const AsyncModifier.async()
+      : isAsync = true,
+        isYielding = false,
+        description = "async";
+  const AsyncModifier.asyncStar()
+      : isAsync = true,
+        isYielding = true,
+        description = "async*";
+  const AsyncModifier.syncStar()
+      : isAsync = false,
+        isYielding = true,
+        description = "sync*";
+  toString() => description;
 }
 
 class PropertyAccess extends Expression {
@@ -928,9 +931,12 @@
   /**
    * Constructs a LiteralString from a string value.
    *
-   * The constructor does not add the required quotes.  If [value] is
-   * not surrounded by quotes, the resulting object is invalid as a JS
-   * value.
+   * The constructor does not add the required quotes.  If [value] is not
+   * surrounded by quotes and property escaped, the resulting object is invalid
+   * as a JS value.
+   *
+   * TODO(sra): Introduce variants for known valid strings that don't allocate a
+   * new string just to add quotes.
    */
   LiteralString(this.value);
 
@@ -939,7 +945,7 @@
 }
 
 class LiteralNumber extends Literal {
-  final String value;
+  final String value;  // Must be a valid JavaScript number literal.
 
   LiteralNumber(this.value);
 
@@ -1108,7 +1114,7 @@
 /**
  * An asynchronous await.
  *
- * Not part of javascript. We desugar this expression before outputting.
+ * Not part of JavaScript. We desugar this expression before outputting.
  * Should only occur in a [Fun] with `asyncModifier` async or asyncStar.
  */
 class Await extends Expression {
diff --git a/pkg/compiler/lib/src/js/printer.dart b/pkg/compiler/lib/src/js/printer.dart
index 19d6c3f..6ee3f3e 100644
--- a/pkg/compiler/lib/src/js/printer.dart
+++ b/pkg/compiler/lib/src/js/printer.dart
@@ -2,32 +2,77 @@
 // for 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 js;
+part of js_ast;
 
-class Printer extends Indentation implements NodeVisitor {
+
+class JavaScriptPrintingOptions {
   final bool shouldCompressOutput;
-  leg.DiagnosticListener diagnosticListener;
-  CodeBuffer outBuffer;
-  bool inForInit = false;
-  bool atStatementBegin = false;
+  final bool minifyLocalVariables;
+  final bool preferSemicolonToNewlineInMinifiedOutput;
+
+  JavaScriptPrintingOptions(
+      {this.shouldCompressOutput: false,
+       this.minifyLocalVariables: false,
+       this.preferSemicolonToNewlineInMinifiedOutput: false});
+}
+
+
+/// An environment in which JavaScript printing is done.  Provides emitting of
+/// text and pre- and post-visit callbacks.
+abstract class JavaScriptPrintingContext {
+  /// Signals an error.  This should happen only for serious internal errors.
+  void error(String message) { throw message; }
+
+  /// Adds [string] to the output.
+  void emit(String string);
+
+  /// Callback immediately before printing [node].  Whitespace may be printed
+  /// after this callback before the first non-whitespace character for [node].
+  void enterNode(Node node) {}
+  /// Callback after printing the last character representing [node].
+  void exitNode(Node node) {}
+}
+
+/// A simple implementation of [JavaScriptPrintingContext] suitable for tests.
+class SimpleJavaScriptPrintingContext extends JavaScriptPrintingContext {
+  final StringBuffer buffer = new StringBuffer();
+
+  void emit(String string) {
+    buffer.write(string);
+  }
+
+  String getText() => buffer.toString();
+}
+
+
+class Printer implements NodeVisitor {
+  final JavaScriptPrintingOptions options;
+  final JavaScriptPrintingContext context;
+  final bool shouldCompressOutput;
   final DanglingElseVisitor danglingElseVisitor;
   final LocalNamer localNamer;
+
+  bool inForInit = false;
+  bool atStatementBegin = false;
   bool pendingSemicolon = false;
   bool pendingSpace = false;
-  DumpInfoTask monitor = null;
+
+  // The current indentation level.
+  int _indentLevel = 0;
+  // A cache of all indentation strings used so far.
+  List<String> _indentList = <String>[""];
 
   static final identifierCharacterRegExp = new RegExp(r'^[a-zA-Z_0-9$]');
   static final expressionContinuationRegExp = new RegExp(r'^[-+([]');
 
-  Printer(leg.DiagnosticListener diagnosticListener, DumpInfoTask monitor,
-          { bool enableMinification: false, allowVariableMinification: true })
-      : shouldCompressOutput = enableMinification,
-        monitor = monitor,
-        diagnosticListener = diagnosticListener,
-        outBuffer = new CodeBuffer(),
-        danglingElseVisitor = new DanglingElseVisitor(diagnosticListener),
-        localNamer = determineRenamer(enableMinification,
-                                      allowVariableMinification);
+  Printer(JavaScriptPrintingOptions options,
+          JavaScriptPrintingContext context)
+      : options = options,
+        context = context,
+        shouldCompressOutput = options.shouldCompressOutput,
+        danglingElseVisitor = new DanglingElseVisitor(context),
+        localNamer = determineRenamer(options.shouldCompressOutput,
+                                      options.minifyLocalVariables);
 
   static LocalNamer determineRenamer(bool shouldCompressOutput,
                                      bool allowVariableMinification) {
@@ -35,6 +80,25 @@
         ? new MinifyRenamer() : new IdentityNamer();
   }
 
+
+  // The current indentation string.
+  String get indentation {
+    // Lazily add new indentation strings as required.
+    while (_indentList.length <= _indentLevel) {
+      _indentList.add(_indentList.last + "  ");
+    }
+    return _indentList[_indentLevel];
+  }
+
+  void indentMore() {
+    _indentLevel++;
+  }
+
+  void indentLess() {
+    _indentLevel--;
+  }
+
+
   /// Always emit a newline, even under `enableMinification`.
   void forceLine() {
     out("\n");
@@ -58,7 +122,7 @@
     if (str != "") {
       if (pendingSemicolon) {
         if (!shouldCompressOutput) {
-          outBuffer.add(";");
+          context.emit(";");
         } else if (str != "}") {
           // We want to output newline instead of semicolon because it makes
           // the raw stack traces much easier to read and it also makes line-
@@ -71,20 +135,21 @@
           // If we're using the new emitter where most pretty printed code
           // is escaped in strings, it is a lot easier to deal with semicolons
           // than newlines because the former doesn't need escaping.
-          if (USE_NEW_EMITTER || expressionContinuationRegExp.hasMatch(str)) {
-            outBuffer.add(";");
+          if (options.preferSemicolonToNewlineInMinifiedOutput ||
+              expressionContinuationRegExp.hasMatch(str)) {
+            context.emit(";");
           } else {
-            outBuffer.add("\n");
+            context.emit("\n");
           }
         }
       }
       if (pendingSpace &&
           (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) {
-        outBuffer.add(" ");
+        context.emit(" ");
       }
       pendingSpace = false;
       pendingSemicolon = false;
-      outBuffer.add(str);
+      context.emit(str);
       lastAddedString = str;
     }
   }
@@ -111,30 +176,10 @@
     }
   }
 
-  void beginSourceRange(Node node) {
-    if (node.sourcePosition != null) {
-      outBuffer.beginMappedRange();
-      outBuffer.setSourceLocation(node.sourcePosition);
-    }
-  }
-
-  void endSourceRange(Node node) {
-    if (node.endSourcePosition != null) {
-      outBuffer.setSourceLocation(node.endSourcePosition);
-    }
-    if (node.sourcePosition != null) {
-      outBuffer.endMappedRange();
-    }
-  }
-
   visit(Node node) {
-    beginSourceRange(node);
-    if (monitor != null) monitor.enteringAst(node, outBuffer.length);
-
+    context.enterNode(node);
     node.accept(this);
-
-    if (monitor != null) monitor.exitingAst(node, outBuffer.length);
-    endSourceRange(node);
+    context.exitNode(node);
   }
 
   visitCommaSeparated(List<Node> nodes, int hasRequiredType,
@@ -159,10 +204,6 @@
     visitAll(program.body);
   }
 
-  visitBlob(Blob node) {
-    outBuffer.addBuffer(node.buffer);
-  }
-
   bool blockBody(Node body, {bool needsSeparation, bool needsNewline}) {
     if (body is Block) {
       spaceOut();
@@ -176,16 +217,18 @@
     } else {
       lineOut();
     }
-    indentBlock(() => visit(body));
+    indentMore();
+    visit(body);
+    indentLess();
     return false;
   }
 
   void blockOutWithoutBraces(Node node) {
     if (node is Block) {
-      beginSourceRange(node);
+      context.enterNode(node);
       Block block = node;
       block.statements.forEach(blockOutWithoutBraces);
-      endSourceRange(node);
+      context.exitNode(node);
     } else {
       visit(node);
     }
@@ -193,13 +236,15 @@
 
   void blockOut(Block node, bool shouldIndent, bool needsNewline) {
     if (shouldIndent) indent();
-    beginSourceRange(node);
+    context.enterNode(node);
     out("{");
     lineOut();
-    indentBlock(() => node.statements.forEach(blockOutWithoutBraces));
+    indentMore();
+    node.statements.forEach(blockOutWithoutBraces);
+    indentLess();
     indent();
     out("}");
-    endSourceRange(node);
+    context.exitNode(node);
     if (needsNewline) lineOut();
   }
 
@@ -355,6 +400,19 @@
     outSemicolonLn();
   }
 
+  visitDartYield(DartYield node) {
+    if (node.hasStar) {
+      outIndent("yield*");
+    } else {
+      outIndent("yield");
+    }
+    pendingSpace = true;
+    visitNestedExpression(node.expression, EXPRESSION,
+                          newInForInit: false, newAtStatementBegin: false);
+    outSemicolonLn();
+  }
+
+
   visitThrow(Throw node) {
     outIndent("throw");
     pendingSpace = true;
@@ -398,7 +456,9 @@
     out(")");
     spaceOut();
     outLn("{");
-    indentBlock(() => visitAll(node.cases));
+    indentMore();
+    visitAll(node.cases);
+    indentLess();
     outIndentLn("}");
   }
 
@@ -409,14 +469,18 @@
                           newInForInit: false, newAtStatementBegin: false);
     outLn(":");
     if (!node.body.statements.isEmpty) {
-      indentBlock(() => blockOutWithoutBraces(node.body));
+      indentMore();
+      blockOutWithoutBraces(node.body);
+      indentLess();
     }
   }
 
   visitDefault(Default node) {
     outIndentLn("default:");
     if (!node.body.statements.isEmpty) {
-      indentBlock(() => blockOutWithoutBraces(node.body));
+      indentMore();
+      blockOutWithoutBraces(node.body);
+      indentLess();
     }
   }
 
@@ -633,8 +697,7 @@
         rightPrecedenceRequirement = UNARY;
         break;
       default:
-        diagnosticListener
-            .internalError(NO_LOCATION_SPANNABLE, "Forgot operator: $op");
+        context.error("Forgot operator: $op");
     }
 
     visitNestedExpression(left, leftPrecedenceRequirement,
@@ -871,8 +934,7 @@
     List<String> parts = template.split('#');
     int inputsLength = inputs == null ? 0 : inputs.length;
     if (parts.length != inputsLength + 1) {
-      diagnosticListener.internalError(NO_LOCATION_SPANNABLE,
-          'Wrong number of arguments for JS: $template');
+      context.error('Wrong number of arguments for JS: $template');
     }
     // Code that uses JS must take care of operator precedences, and
     // put parenthesis if needed.
@@ -999,15 +1061,14 @@
  * as then-statement in an [If] that has an else branch.
  */
 class DanglingElseVisitor extends BaseVisitor<bool> {
-  leg.DiagnosticListener diagnosticListener;
+  JavaScriptPrintingContext context;
 
-  DanglingElseVisitor(this.diagnosticListener);
+  DanglingElseVisitor(this.context);
 
   bool visitProgram(Program node) => false;
 
   bool visitNode(Statement node) {
-    diagnosticListener
-        .internalError(NO_LOCATION_SPANNABLE, "Forgot node: $node");
+    context.error("Forgot node: $node");
     return null;
   }
 
@@ -1046,18 +1107,6 @@
 }
 
 
-CodeBuffer prettyPrint(Node node, leg.Compiler compiler,
-                       {DumpInfoTask monitor,
-                        bool allowVariableMinification: true}) {
-  Printer printer =
-      new Printer(compiler, monitor,
-                  enableMinification: compiler.enableMinification,
-                  allowVariableMinification: allowVariableMinification);
-  printer.visit(node);
-  return printer.outBuffer;
-}
-
-
 abstract class LocalNamer {
   String getName(String oldName);
   String declareVariable(String oldName);
diff --git a/pkg/compiler/lib/src/js/rewrite_async.dart b/pkg/compiler/lib/src/js/rewrite_async.dart
new file mode 100644
index 0000000..656b1af
--- /dev/null
+++ b/pkg/compiler/lib/src/js/rewrite_async.dart
@@ -0,0 +1,2149 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library rewrite_async;
+
+// TODO(sigurdm): Throws in catch-handlers are handled wrong.
+// TODO(sigurdm): Avoid using variables in templates. It could blow up memory
+// use.
+
+import "dart:math" show max;
+import 'dart:collection';
+
+import "js.dart" as js;
+
+import '../util/util.dart';
+import '../dart2jslib.dart' show DiagnosticListener;
+
+import "../helpers/helpers.dart";
+
+/// Rewrites a [js.Fun] with async/sync*/async* functions and await and yield
+/// (with dart-like semantics) to an equivalent function without these.
+/// await-for is not handled and must be rewritten before. (Currently handled
+/// in ssa/builder.dart).
+///
+/// When generating the input to this, special care must be taken that
+/// parameters to sync* functions that are mutated in the body must be boxed.
+/// (Currently handled in closure.dart).
+///
+/// Look at [visitFun], [visitDartYield] and [visitAwait] for more explanation.
+class AsyncRewriter extends js.NodeVisitor {
+
+  // Local variables are hoisted to the top of the function, so they are
+  // collected here.
+  List<js.VariableDeclaration> localVariables =
+      new List<js.VariableDeclaration>();
+
+  Map<js.Node, int> continueLabels = new Map<js.Node, int>();
+  Map<js.Node, int> breakLabels = new Map<js.Node, int>();
+  Map<js.Node, int> finallyLabels = new Map<js.Node, int>();
+  int exitLabel;
+
+  // A stack of all enclosing jump targets (including the function for
+  // representing the target of a return, and all enclosing try-blocks that have
+  // finally part, this way ensuring all the finally blocks between a jump and
+  // its target are run before the jump.
+  List<js.Node> targetsAndTries = new List<js.Node>();
+
+  List<int> continueStack = new List<int>();
+  List<int> breakStack = new List<int>();
+  List<int> returnStack = new List<int>();
+
+  List<Pair<String, String>> variableRenamings =
+      new List<Pair<String, String>>();
+
+  PreTranslationAnalysis analysis;
+
+  List<int> errorHandlerLabels = new List<int>();
+
+  final Function safeVariableName;
+
+  // All the <x>Name variables are names of Javascript variables used in the
+  // transformed code.
+
+  /// Contains the result of an awaited expression, or a conditional or
+  /// lazy boolean operator.
+  ///
+  /// For example a conditional expression is roughly translated like:
+  /// [[cond ? a : b]]
+  ///
+  /// Becomes:
+  ///
+  /// while true { // outer while loop
+  ///   switch (goto) { // Simulates goto
+  ///     ...
+  ///       goto = [[cond]] ? thenLabel : elseLabel
+  ///       break;
+  ///     case thenLabel:
+  ///       result = [[a]];
+  ///       goto = joinLabel;
+  ///     case elseLabel:
+  ///       result = [[b]];
+  ///     case joinLabel:
+  ///       // Now the result of computing the condition is in result.
+  ///     ....
+  ///   }
+  /// }
+  ///
+  /// It is a parameter to the [helperName] function, so that [thenHelper] and
+  /// [streamHelper] can call [helperName] with the result of an awaited Future.
+  String resultName;
+
+  /// The name of the inner function that is scheduled to do each await/yield,
+  /// and called to do a new iteration for sync*.
+  String helperName;
+
+  /// The Completer that will finish an async function.
+  ///
+  /// Not used for sync* or async* functions.
+  String completerName;
+
+  /// The StreamController that controls an async* function.
+  ///
+  /// Not used for async and sync* functions
+  String controllerName;
+
+  /// Used to simulate a goto.
+  ///
+  /// To "goto" a label, the label is assigned to this
+  /// variable, and break out of the switch to take another iteration in the
+  /// while loop. See [addGoto]
+  String gotoName;
+
+  /// The label of the current error handler.
+  String handlerName;
+
+  /// Current caught error.
+  String errorName;
+
+  /// A stack of labels of finally blocks to visit, and the label to go to after
+  /// the last.
+  String nextName;
+
+  /// The current returned value (a finally block may overwrite it).
+  String returnValueName;
+
+  /// The label of the outer loop.
+  ///
+  /// Used if there are untransformed loops containing break or continues to
+  /// targets outside the loop.
+  String outerLabelName;
+
+  /// If javascript `this` is used, it is accessed via this variable, in the
+  /// [helperName] function.
+  String selfName;
+
+  // These expressions are hooks for communicating with the runtime.
+
+  /// The function called by an async function to simulate an await or return.
+  ///
+  /// For an await it is called with:
+  ///
+  /// - The value to await
+  /// - The [helperName]
+  /// - The [completerName]
+  /// - A JavaScript function that is executed if the future completed with
+  ///   an error. That function is responsible for executing the right error
+  ///   handler and/or finally blocks).
+  ///
+  /// For a return it is called with:
+  ///
+  /// - The value to complete the completer with.
+  /// - null
+  /// - The [completerName]
+  /// - null.
+  final js.Expression thenHelper;
+
+  /// The function called by an async* function to simulate an await, yield or
+  /// yield*.
+  ///
+  /// For an await/yield/yield* it is called with:
+  ///
+  /// - The value to await/yieldExpression(value to yield)/
+  /// yieldStarExpression(stream to yield)
+  /// - The [helperName]
+  /// - The [controllerName]
+  /// - A JavaScript function that is executed if the future completed with
+  ///   an error. That function is responsible for executing the right error
+  ///   handler and/or finally blocks).
+  ///
+  /// For a return it is called with:
+  ///
+  /// - null
+  /// - null
+  /// - The [controllerName]
+  /// - null.
+  final js.Expression streamHelper;
+
+  /// Contructor used to initialize the [completerName] variable.
+  ///
+  /// Specific to async methods.
+  final js.Expression newCompleter;
+
+  /// Contructor used to initialize the [controllerName] variable.
+  ///
+  /// Specific to async* methods.
+  final js.Expression newController;
+
+  /// Used to get the `Stream` out of the [controllerName] variable.
+  ///
+  /// Specific to async* methods.
+  final js.Expression streamOfController;
+
+  /// Contructor creating the Iterable for a sync* method. Called with
+  /// [helperName].
+  final js.Expression newIterable;
+
+  /// A JS Expression that creates a marker showing that iteration is over.
+  ///
+  /// Called without arguments.
+  final js.Expression endOfIteration;
+
+  /// A JS Expression that creates a marker indicating a 'yield' statement.
+  ///
+  /// Called with the value to yield.
+  final js.Expression yieldExpression;
+
+  /// A JS Expression that creates a marker indication a 'yield*' statement.
+  ///
+  /// Called with the stream to yield from.
+  final js.Expression yieldStarExpression;
+
+  final DiagnosticListener diagnosticListener;
+  // For error reporting only.
+  Spannable get spannable {
+    return (_spannable == null) ? NO_LOCATION_SPANNABLE : _spannable;
+  }
+
+  Spannable _spannable;
+
+  int _currentLabel = 0;
+
+  // The highest temporary variable index currently in use.
+  int currentTempVarIndex = 0;
+  // The highest temporary variable index ever in use in this function.
+  int tempVarHighWaterMark = 0;
+  Map<int, js.Expression> tempVarNames = new Map<int, js.Expression>();
+
+  js.AsyncModifier async;
+
+  bool get isSync => async == const js.AsyncModifier.sync();
+  bool get isAsync => async == const js.AsyncModifier.async();
+  bool get isSyncStar => async == const js.AsyncModifier.syncStar();
+  bool get isAsyncStar => async == const js.AsyncModifier.asyncStar();
+
+  AsyncRewriter(this.diagnosticListener,
+                spannable,
+                {this.thenHelper,
+                 this.streamHelper,
+                 this.streamOfController,
+                 this.newCompleter,
+                 this.newController,
+                 this.endOfIteration,
+                 this.newIterable,
+                 this.yieldExpression,
+                 this.yieldStarExpression,
+                 this.safeVariableName})
+      : _spannable = spannable;
+
+  /// Main entry point.
+  /// Rewrites a sync*/async/async* function to an equivalent normal function.
+  ///
+  /// [spannable] can be passed to have a location for error messages.
+  js.Fun rewrite(js.Fun node, [Spannable spannable]) {
+    _spannable = spannable;
+
+    async = node.asyncModifier;
+    assert(!isSync);
+
+    analysis = new PreTranslationAnalysis(unsupported);
+    analysis.analyze(node);
+
+    // To avoid name collisions with existing names, the fresh names are
+    // generated after the analysis.
+    resultName = freshName("result");
+    completerName = freshName("completer");
+    controllerName = freshName("controller");
+    helperName = freshName("helper");
+    gotoName = freshName("goto");
+    handlerName = freshName("handler");
+    errorName = freshName("error");
+    nextName = freshName("next");
+    returnValueName = freshName("returnValue");
+    outerLabelName = freshName("outer");
+    selfName = freshName("self");
+
+    return node.accept(this);
+  }
+
+  js.Expression get currentErrorHandler {
+    return errorHandlerLabels.isEmpty
+        ? new js.LiteralNull()
+        : js.number(errorHandlerLabels.last);
+  }
+
+  int allocateTempVar() {
+    assert(tempVarHighWaterMark >= currentTempVarIndex);
+    currentTempVarIndex++;
+    tempVarHighWaterMark = max(currentTempVarIndex, tempVarHighWaterMark);
+    return currentTempVarIndex;
+  }
+
+  js.VariableUse useTempVar(int i) {
+    return tempVarNames.putIfAbsent(
+        i, () => new js.VariableUse(freshName("temp$i")));
+  }
+
+  /// Generates a variable name with [safeVariableName] based on [originalName]
+  /// with a suffix to guarantee it does not collide with already used names.
+  String freshName(String originalName) {
+    String safeName = safeVariableName(originalName);
+    String result = safeName;
+    int counter = 1;
+    while (analysis.usedNames.contains(result)) {
+      result = "$safeName$counter";
+      ++counter;
+    }
+    analysis.usedNames.add(result);
+    return result;
+  }
+
+  /// All the pieces are collected in this map, to create a switch with a case
+  /// for each label.
+  ///
+  /// The order is important, therefore the type is explicitly LinkedHashMap.
+  LinkedHashMap<int, List<js.Statement>> labelledParts =
+      new LinkedHashMap<int, List<js.Statement>>();
+
+  /// Description of each label for readability of the non-minified output.
+  Map<int, String> labelComments = new Map<int, String>();
+
+  /// True if the function has any try blocks containing await.
+  bool hasTryBlocks = false;
+
+  /// True if any return, break or continue passes through a finally.
+  bool hasJumpThroughFinally = false;
+
+  /// True if the traversion currently is inside a loop or switch for which
+  /// [shouldTransform] is false.
+  bool insideUntranslatedBreakable = false;
+
+  /// True if a label is used to break to an outer switch-statement.
+  bool hasJumpThoughOuterLabel = false;
+
+  /// True if there is a catch-handler protected by a finally with no enclosing
+  /// catch-handlers.
+  bool needsRethrow = false;
+
+  /// Buffer for collecting translated statements belonging to the same switch
+  /// case.
+  List<js.Statement> currentStatementBuffer;
+
+  // Labels will become cases in the big switch expression, and `goto label`
+  // is expressed by assigning to the switch key [gotoName] and breaking out of
+  // the switch.
+
+  int newLabel([String comment]) {
+    int result = _currentLabel;
+    _currentLabel++;
+    if (comment != null) {
+      labelComments[result] = comment;
+    }
+    return result;
+  }
+
+  /// Begins outputting statements to a new buffer with label [label].
+  ///
+  /// Each buffer ends up as its own case part in the big state-switch.
+  void beginLabel(int label) {
+    assert(!labelledParts.containsKey(label));
+    currentStatementBuffer = new List<js.Statement>();
+    labelledParts[label] = currentStatementBuffer;
+    addStatement(new js.Comment(labelComments[label]));
+  }
+
+  /// Returns a statement assigning to the variable named [gotoName].
+  /// This should be followed by a break for the goto to be executed. Use
+  /// [gotoWithBreak] or [addGoto] for this.
+  js.Statement setGotoVariable(int label) {
+    return new js.ExpressionStatement(
+        new js.Assignment(new js.VariableUse(gotoName), js.number(label)));
+  }
+
+  /// Returns a block that has a goto to [label] including the break.
+  ///
+  /// Also inserts a comment describing the label if available.
+  js.Block gotoAndBreak(int label) {
+    List<js.Statement> statements = new List<js.Statement>();
+    if (labelComments.containsKey(label)) {
+      statements.add(new js.Comment("goto ${labelComments[label]}"));
+    }
+    statements.add(setGotoVariable(label));
+    if (insideUntranslatedBreakable) {
+      hasJumpThoughOuterLabel = true;
+      statements.add(new js.Break(outerLabelName));
+    } else {
+      statements.add(new js.Break(null));
+    }
+    return new js.Block(statements);
+  }
+
+  /// Adds a goto to [label] including the break.
+  ///
+  /// Also inserts a comment describing the label if available.
+  void addGoto(int label) {
+    if (labelComments.containsKey(label)) {
+      addStatement(new js.Comment("goto ${labelComments[label]}"));
+    }
+    addStatement(setGotoVariable(label));
+
+    addBreak();
+  }
+
+  void addStatement(js.Statement node) {
+    currentStatementBuffer.add(node);
+  }
+
+  void addExpressionStatement(js.Expression node) {
+    addStatement(new js.ExpressionStatement(node));
+  }
+
+  /// True if there is an await or yield in [node] or some subexpression.
+  bool shouldTransform(js.Node node) {
+    return analysis.hasAwaitOrYield.contains(node);
+  }
+
+  void unsupported(js.Node node) {
+    throw new UnsupportedError(
+        "Node $node cannot be transformed by the await-sync transformer");
+  }
+
+  void unreachable(js.Node node) {
+    diagnosticListener.internalError(
+        spannable, "Internal error, trying to visit $node");
+  }
+
+  visitStatement(js.Statement node) {
+    node.accept(this);
+  }
+
+  /// Visits [node] to ensure its sideeffects are performed, but throwing away
+  /// the result.
+  ///
+  /// If the return value of visiting [node] is an expression guaranteed to have
+  /// no side effect, it is dropped.
+  void visitExpressionIgnoreResult(js.Expression node) {
+    js.Expression result = node.accept(this);
+    if (!(result is js.Literal || result is js.VariableUse)) {
+      addExpressionStatement(result);
+    }
+  }
+
+  js.Expression visitExpression(js.Expression node) {
+    return node.accept(this);
+  }
+
+
+  /// Calls [fn] with the value of evaluating [node1] and [node2].
+  ///
+  /// Both nodes are evaluated in order.
+  ///
+  /// If node2 must be transformed (see [shouldTransform]), then the evaluation
+  /// of node1 is added to the current statement-list and the result is stored
+  /// in a temporary variable. The evaluation of node2 is then free to emit
+  /// statements without affecting the result of node1.
+  ///
+  /// This is necessary, because await or yield expressions have to emit
+  /// statements, and these statements could affect the value of node1.
+  ///
+  /// For example:
+  ///
+  /// - _storeIfNecessary(someLiteral) returns someLiteral.
+  /// - _storeIfNecessary(someVariable)
+  ///   inserts: var tempX = someVariable
+  ///   returns: tempX
+  ///   where tempX is a fresh temporary variable.
+  js.Expression _storeIfNecessary(js.Expression result) {
+    // Note that RegExes, js.ArrayInitializer and js.ObjectInitializer are not
+    // [js.Literal]s.
+    if (result is js.Literal) return result;
+    js.Expression tempVar = useTempVar(allocateTempVar());
+    addExpressionStatement(new js.Assignment(tempVar, result));
+    return tempVar;
+  }
+
+  withExpression(js.Expression node, fn(js.Expression result), {bool store}) {
+    int oldTempVarIndex = currentTempVarIndex;
+    js.Expression visited = visitExpression(node);
+    if (store) {
+      visited = _storeIfNecessary(visited);
+    }
+    var result = fn(visited);
+    currentTempVarIndex = oldTempVarIndex;
+    return result;
+  }
+
+  /// Calls [fn] with the value of evaluating [node1] and [node2].
+  ///
+  /// If `shouldTransform(node2)` the first expression is stored in a temporary
+  /// variable.
+  ///
+  /// This is because node1 must be evaluated before visiting node2,
+  /// because the evaluation of an await or yield cannot be expressed as
+  /// an expression, visiting node2 it will output statements that
+  /// might have an influence on the value of node1.
+  withExpression2(js.Expression node1, js.Expression node2,
+      fn(js.Expression result1, js.Expression result2)) {
+    int oldTempVarIndex = currentTempVarIndex;
+    js.Expression r1 = visitExpression(node1);
+    if (shouldTransform(node2)) {
+      r1 = _storeIfNecessary(r1);
+    }
+    js.Expression r2 = visitExpression(node2);
+    var result = fn(r1, r2);
+    currentTempVarIndex = oldTempVarIndex;
+    return result;
+  }
+
+  /// Calls [fn] with the value of evaluating all [nodes].
+  ///
+  /// All results before the last node where `shouldTransform(node)` are stored
+  /// in temporary variables.
+  ///
+  /// See more explanation on [withExpression2].
+  ///
+  /// If any of the nodes are null, they are ignored, and a null is passed to
+  /// [fn] in that place.
+  withExpressions(List<js.Expression> nodes, fn(List<js.Expression> results)) {
+    int oldTempVarIndex = currentTempVarIndex;
+    // Find last occurence of a 'transform' expression in [nodes].
+    // All expressions before that must be stored in temp-vars.
+    int lastTransformIndex = 0;
+    for (int i = nodes.length - 1; i >= 0; --i) {
+      if (nodes[i] == null) continue;
+      if (shouldTransform(nodes[i])) {
+        lastTransformIndex = i;
+        break;
+      }
+    }
+    List<js.Node> visited = nodes.take(lastTransformIndex).map((js.Node node) {
+      return node == null ? null : _storeIfNecessary(visitExpression(node));
+    }).toList();
+    visited.addAll(nodes.skip(lastTransformIndex).map((js.Node node) {
+      return node == null ? null : visitExpression(node);
+    }));
+    var result = fn(visited);
+    currentTempVarIndex = oldTempVarIndex;
+    return result;
+  }
+
+  /// Emits the return block that all returns should jump to (after going
+  /// through all the enclosing finally blocks). The jump to here is made in
+  /// [visitReturn].
+  ///
+  /// Returning from an async method calls the [thenHelper] with the result.
+  /// (the result might have been stored in [returnValueName] by some finally
+  /// block).
+  ///
+  /// Returning from a sync* function returns an [endOfIteration] marker.
+  ///
+  /// Returning from an async* function calls the [streamHelper] with an
+  /// [endOfIteration] marker.
+  void addExit() {
+    if (analysis.hasExplicitReturns || isAsyncStar) {
+      beginLabel(exitLabel);
+    } else {
+      addStatement(new js.Comment("implicit return"));
+    }
+    switch (async) {
+      case const js.AsyncModifier.async():
+        String returnValue =
+            analysis.hasExplicitReturns ? returnValueName : "null";
+        addStatement(js.js.statement(
+            "return #thenHelper($returnValue, null, $completerName, null)", {
+          "thenHelper": thenHelper
+        }));
+        break;
+      case const js.AsyncModifier.syncStar():
+        addStatement(new js.Return(new js.Call(endOfIteration, [])));
+        break;
+      case const js.AsyncModifier.asyncStar():
+        addStatement(js.js.statement(
+            "return #streamHelper(null, null, $controllerName, null)", {
+          "streamHelper": streamHelper
+        }));
+        break;
+      default:
+        diagnosticListener.internalError(
+            spannable, "Internal error, unexpected asyncmodifier $async");
+    }
+  }
+
+  /// The initial call to [thenHelper]/[streamHelper].
+  ///
+  /// There is no value to await/yield, so the first argument is `null` and
+  /// also the errorCallback is `null`.
+  ///
+  /// Returns the [Future]/[Stream] coming from [completerName]/
+  /// [controllerName].
+  js.Statement generateInitializer() {
+    if (isAsync) {
+      return js.js.statement(
+          "return #thenHelper(null, $helperName, $completerName, null);", {
+        "thenHelper": thenHelper
+      });
+    } else if (isAsyncStar) {
+      return js.js.statement(
+          "return #streamOfController($controllerName);", {
+        "streamOfController": streamOfController
+      });
+    } else {
+      throw diagnosticListener.internalError(
+          spannable, "Unexpected asyncModifier: $async");
+    }
+  }
+
+  /// Rewrites an async/sync*/async* function to a normal Javascript function.
+  ///
+  /// The control flow is flattened by simulating 'goto' using a switch in a
+  /// loop and a state variable [gotoName] inside a helper-function that can be
+  /// called back by [thenHelper]/[streamHelper]/the [Iterator].
+  ///
+  /// Local variables are hoisted outside the helper.
+  ///
+  /// Awaits in async/async* are translated to code that remembers the current
+  /// location (so the function can resume from where it was) followed by a call
+  /// to the [thenHelper]. The helper sets up the waiting for the awaited value
+  /// and returns a future which is immediately returned by the translated
+  /// await.
+  /// Yields in async* are translated to a call to the [streamHelper]. They,
+  /// too, need to be prepared to be interrupted in case the stream is paused or
+  /// canceled. (Currently we always suspend - this is different from the spec,
+  /// see `streamHelper` in `js_helper.dart`).
+  ///
+  /// Yield/yield* in a sync* function is translated to a return of the value,
+  /// wrapped into a "IterationMarker" that signals the type (yield or yield*).
+  /// Sync* functions are executed on demand (when the user requests a value) by
+  /// the Iterable that knows how to handle these values.
+  ///
+  /// Simplified examples (not the exact translation, but intended to show the
+  /// ideas):
+  ///
+  /// function (x, y, z) async {
+  ///   var p = await foo();
+  ///   return bar(p);
+  /// }
+  ///
+  /// Becomes:
+  ///
+  /// function(x, y, z) {
+  ///   var goto = 0, returnValue, completer = new Completer(), p;
+  ///   function helper(result) {
+  ///     while (true) {
+  ///       switch (goto) {
+  ///         case 0:
+  ///           goto = 1 // Remember where to continue when the future succeeds.
+  ///           return thenHelper(foo(), helper, completer, null);
+  ///         case 1:
+  ///           p = result;
+  ///           returnValue = bar(p);
+  ///           goto = 2;
+  ///           break;
+  ///         case 2:
+  ///           return thenHelper(returnValue, null, completer, null)
+  ///       }
+  ///     }
+  ///     return thenHelper(null, helper, completer, null);
+  ///   }
+  /// }
+  ///
+  /// Try/catch is implemented by maintaining [handlerName] to contain the label
+  /// of the current handler. The switch is nested inside a try/catch that will
+  /// redirect the flow to the current handler.
+  ///
+  /// A `finally` clause is compiled similar to normal code, with the additional
+  /// complexity that `finally` clauses need to know where to jump to after the
+  /// clause is done. In the translation, each flow-path that enters a `finally`
+  /// sets up the variable [nextName] with a stack of finally-blocks and a final
+  /// jump-target (exit, catch, ...).
+  ///
+  /// function (x, y, z) async {
+  ///   try {
+  ///     try {
+  ///       throw "error";
+  ///     } finally {
+  ///       finalize1();
+  ///     }
+  ///   } catch (e) {
+  ///     handle(e);
+  ///   } finally {
+  ///     finalize2();
+  ///   }
+  /// }
+  ///
+  /// Translates into (besides the fact that structures not containing
+  /// await/yield/yield* are left intact):
+  ///
+  /// function(x, y, z) {
+  ///   var goto = 0;
+  ///   var returnValue;
+  ///   var completer = new Completer();
+  ///   var handler = null;
+  ///   var p;
+  ///   // The result can be either the result of an awaited future, or an
+  ///   // error if the future completed with an error.
+  ///   function helper(result) {
+  ///     while (true) {
+  ///       try {
+  ///         switch (goto) {
+  ///           case 0:
+  ///             handler = 4; // The outer catch-handler
+  ///             handler = 1; // The inner (implicit) catch-handler
+  ///             throw "error";
+  ///             next = [3];
+  ///             // After the finally (2) continue normally after the try.
+  ///             goto = 2;
+  ///             break;
+  ///           case 1: // (implicit) catch handler for inner try.
+  ///             next = [3]; // destination after the finally.
+  ///             // fall-though to the finally handler.
+  ///           case 2: // finally for inner try
+  ///             handler = 4; // catch-handler for outer try.
+  ///             finalize1();
+  ///             goto = next.pop();
+  ///             break;
+  ///           case 3: // exiting inner try.
+  ///             next = [6];
+  ///             goto = 5; // finally handler for outer try.
+  ///             break;
+  ///           case 4: // catch handler for outer try.
+  ///             e = result;
+  ///             handle(e);
+  ///             // Fall through to finally.
+  ///           case 5: // finally handler for outer try.
+  ///             handler = null;
+  ///             finalize2();
+  ///             goto = next.pop();
+  ///             break;
+  ///           case 6: // Exiting outer try.
+  ///           case 7: // return
+  ///             return thenHelper(returnValue, null, completer, null);
+  ///         }
+  ///       } catch (error) {
+  ///         result = error;
+  ///         goto = handler;
+  ///       }
+  ///     }
+  ///     return thenHelper(null, helper, completer, null);
+  ///   }
+  /// }
+  ///
+  @override
+  js.Expression visitFun(js.Fun node) {
+    if (isSync) return node;
+
+    beginLabel(newLabel("Function start"));
+    // AsyncStar needs a returnlabel for its handling of cancelation. See
+    // [visitDartYield].
+    exitLabel =
+        analysis.hasExplicitReturns || isAsyncStar ? newLabel("return") : null;
+    js.Statement body = node.body;
+    targetsAndTries.add(node);
+    visitStatement(body);
+    targetsAndTries.removeLast();
+    addExit();
+
+    List<js.SwitchClause> clauses = labelledParts.keys.map((label) {
+      return new js.Case(js.number(label), new js.Block(labelledParts[label]));
+    }).toList();
+    js.Statement helperBody =
+        new js.Switch(new js.VariableUse(gotoName), clauses);
+    if (hasJumpThoughOuterLabel) {
+      helperBody = js.js.statement("$outerLabelName: #", [helperBody]);
+    }
+    if (hasTryBlocks) {
+      helperBody = js.js.statement("""
+          try {
+            #body
+          } catch ($errorName){
+            if ($handlerName === null)
+              throw $errorName;
+            $resultName = $errorName;
+            $gotoName = $handlerName;
+          }""", {"body": helperBody});
+    }
+    List<js.VariableInitialization> inits = <js.VariableInitialization>[];
+
+    js.VariableInitialization makeInit(String name, js.Expression initValue) {
+      return new js.VariableInitialization(
+          new js.VariableDeclaration(name), initValue);
+    }
+
+    inits.add(makeInit(gotoName, js.number(0)));
+    if (isAsync) {
+      inits.add(makeInit(completerName, new js.New(newCompleter, [])));
+    } else if (isAsyncStar) {
+      inits.add(makeInit(controllerName,
+          new js.Call(newController, [new js.VariableUse(helperName)])));
+    }
+    if (hasTryBlocks) {
+      inits.add(makeInit(handlerName, new js.LiteralNull()));
+    }
+    if (hasJumpThroughFinally) {
+      inits.add(makeInit(nextName, null));
+    }
+    if (analysis.hasExplicitReturns && isAsync) {
+      inits.add(makeInit(returnValueName, null));
+    }
+    if (analysis.hasThis && !isSyncStar) {
+      // Sync* functions must remember `this` on the level of the outer
+      // function.
+      inits.add(makeInit(selfName, new js.This()));
+    }
+    inits.addAll(localVariables.map((js.VariableDeclaration decl) {
+      return new js.VariableInitialization(decl, null);
+    }));
+    inits.addAll(new Iterable.generate(tempVarHighWaterMark,
+        (int i) => makeInit(useTempVar(i + 1).name, null)));
+    js.VariableDeclarationList varDecl = new js.VariableDeclarationList(inits);
+    if (isSyncStar) {
+      return js.js("""
+          function (#params) {
+            if (#needsThis)
+              var $selfName = this;
+            return new #newIterable(function () {
+              #varDecl;
+              return function $helperName($resultName) {
+                while (true)
+                  #helperBody;
+              };
+            });
+          }
+          """, {
+        "params": node.params,
+        "needsThis": analysis.hasThis,
+        "helperBody": helperBody,
+        "varDecl": varDecl,
+        "newIterable": newIterable
+      });
+    }
+    return js.js("""
+        function (#params) {
+          #varDecl;
+          function $helperName($resultName) {
+            while (true)
+              #helperBody;
+          }
+          #init;
+        }""", {
+      "params": node.params,
+      "helperBody": helperBody,
+      "varDecl": varDecl,
+      "init": generateInitializer()
+    });
+  }
+
+  @override
+  js.Expression visitAccess(js.PropertyAccess node) {
+    return withExpression2(node.receiver, node.selector,
+        (receiver, selector) => new js.PropertyAccess(receiver, selector));
+  }
+
+  @override
+  js.Expression visitArrayHole(js.ArrayHole node) {
+    return node;
+  }
+
+  @override
+  js.Expression visitArrayInitializer(js.ArrayInitializer node) {
+    return withExpressions(node.elements, (elements) {
+      return new js.ArrayInitializer(elements);
+    });
+  }
+
+  @override
+  js.Expression visitAssignment(js.Assignment node) {
+    if (!shouldTransform(node)) {
+      return new js.Assignment.compound(visitExpression(node.leftHandSide),
+          node.op, visitExpression(node.value));
+    }
+    js.Expression leftHandSide = node.leftHandSide;
+    if (leftHandSide is js.VariableUse) {
+      return withExpression(node.value, (js.Expression value) {
+        return new js.Assignment(leftHandSide, value);
+      }, store: false);
+    } else if (leftHandSide is js.PropertyAccess) {
+      return withExpressions([
+        leftHandSide.receiver,
+        leftHandSide.selector,
+        node.value
+      ], (evaluated) {
+        return new js.Assignment.compound(
+            new js.PropertyAccess(evaluated[0], evaluated[1]), node.op,
+            evaluated[2]);
+      });
+    } else {
+      throw "Unexpected assignment left hand side $leftHandSide";
+    }
+  }
+
+  /// An await is translated to a call to [thenHelper]/[streamHelper].
+  ///
+  /// See the comments of [visitFun] for an example.
+  @override
+  js.Expression visitAwait(js.Await node) {
+    assert(isAsync || isAsyncStar);
+    int afterAwait = newLabel("returning from await.");
+    withExpression(node.expression, (js.Expression value) {
+      addStatement(setGotoVariable(afterAwait));
+      js.Expression errorCallback = errorHandlerLabels.isEmpty
+          ? new js.LiteralNull()
+          : js.js("""
+            function($errorName) {
+              $gotoName = #currentHandler;
+              $helperName($errorName);
+            }""", {"currentHandler": currentErrorHandler});
+
+      addStatement(js.js.statement("""
+          return #thenHelper(#value,
+                             $helperName,
+                             ${isAsync ? completerName : controllerName},
+                             #errorCallback);
+          """, {
+        "thenHelper": isAsync ? thenHelper : streamHelper,
+        "value": value,
+        "errorCallback": errorCallback
+      }));
+    }, store: false);
+    beginLabel(afterAwait);
+    return new js.VariableUse(resultName);
+  }
+
+  /// Checks if [node] is the variable named [resultName].
+  ///
+  /// [resultName] is used to hold the result of a transformed computation
+  /// for example the result of awaiting, or the result of a conditional or
+  /// short-circuiting expression.
+  /// If the subexpression of some transformed node already is transformed and
+  /// visiting it returns [resultName], it is not redundantly assigned to itself
+  /// again.
+  bool isResult(js.Expression node) {
+    return node is js.VariableUse && node.name == resultName;
+  }
+
+  @override
+  js.Expression visitBinary(js.Binary node) {
+    if (shouldTransform(node.right) && (node.op == "||" || node.op == "&&")) {
+      int thenLabel = newLabel("then");
+      int joinLabel = newLabel("join");
+      withExpression(node.left, (js.Expression left) {
+        js.Statement assignLeft = isResult(left)
+            ? new js.Block.empty()
+            : new js.ExpressionStatement(
+                new js.Assignment(new js.VariableUse(resultName), left));
+        if (node.op == "||") {
+          addStatement(new js.If(left, gotoAndBreak(thenLabel), assignLeft));
+        } else {
+          assert(node.op == "&&");
+          addStatement(new js.If(left, assignLeft, gotoAndBreak(thenLabel)));
+        }
+      }, store: true);
+      addGoto(joinLabel);
+      beginLabel(thenLabel);
+      withExpression(node.right, (js.Expression value) {
+        if (!isResult(value)) {
+          addExpressionStatement(
+              new js.Assignment(new js.VariableUse(resultName), value));
+        }
+      }, store: false);
+      beginLabel(joinLabel);
+      return new js.VariableUse(resultName);
+    }
+
+    return withExpression2(node.left, node.right,
+        (left, right) => new js.Binary(node.op, left, right));
+  }
+
+  @override
+  void visitBlock(js.Block node) {
+    for (js.Statement statement in node.statements) {
+      visitStatement(statement);
+    }
+  }
+
+  @override
+  void visitBreak(js.Break node) {
+    js.Node target = analysis.targets[node];
+    if (!shouldTransform(target)) {
+      addStatement(node);
+      return;
+    }
+    translateJump(target, breakLabels[target]);
+  }
+
+  @override
+  js.Expression visitCall(js.Call node) {
+    bool storeTarget = node.arguments.any(shouldTransform);
+    return withExpression(node.target, (target) {
+      return withExpressions(node.arguments, (List<js.Expression> arguments) {
+        return new js.Call(target, arguments);
+      });
+    }, store: storeTarget);
+  }
+
+  @override
+  void visitCase(js.Case node) {
+    return unreachable(node);
+  }
+
+  @override
+  void visitCatch(js.Catch node) {
+    return unreachable(node);
+  }
+
+  @override
+  void visitComment(js.Comment node) {
+    addStatement(node);
+  }
+
+  @override
+  js.Expression visitConditional(js.Conditional node) {
+    if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
+      return withExpression(node.condition, (js.Expression condition) {
+        return new js.Conditional(condition, node.then, node.otherwise);
+      });
+    }
+    int thenLabel = newLabel("then");
+    int joinLabel = newLabel("join");
+    int elseLabel = newLabel("else");
+    withExpression(node.condition, (js.Expression condition) {
+      addExpressionStatement(new js.Assignment(new js.VariableUse(gotoName),
+          new js.Conditional(
+              condition, js.number(thenLabel), js.number(elseLabel))));
+    }, store: false);
+    addBreak();
+    beginLabel(thenLabel);
+    withExpression(node.then, (js.Expression value) {
+      if (!isResult(value)) {
+        addExpressionStatement(
+            new js.Assignment(new js.VariableUse(resultName), value));
+      }
+    }, store: false);
+    addGoto(joinLabel);
+    beginLabel(elseLabel);
+    withExpression(node.otherwise, (js.Expression value) {
+      if (!isResult(value)) {
+        addExpressionStatement(
+            new js.Assignment(new js.VariableUse(resultName), value));
+      }
+    }, store: false);
+    beginLabel(joinLabel);
+    return new js.VariableUse(resultName);
+  }
+
+  @override
+  void visitContinue(js.Continue node) {
+    js.Node target = analysis.targets[node];
+    if (!shouldTransform(target)) {
+      addStatement(node);
+      return;
+    }
+    translateJump(target, continueLabels[target]);
+  }
+
+  /// Emits a break statement that exits the big switch statement.
+  void addBreak() {
+    if (insideUntranslatedBreakable) {
+      hasJumpThoughOuterLabel = true;
+      addStatement(new js.Break(outerLabelName));
+    } else {
+      addStatement(new js.Break(null));
+    }
+  }
+
+  /// Common code for handling break, continue, return.
+  ///
+  /// It is necessary to run all nesting finally-handlers between the jump and
+  /// the target. For that [nextName] is used as a stack of places to go.
+  ///
+  /// See also [visitFun].
+  void translateJump(js.Node target, int targetLabel) {
+    // Compute a stack of all the 'finally' nodes that must be visited before
+    // the jump.
+    // The bottom of the stack is the label where the jump goes to.
+    List<int> jumpStack = new List<int>();
+    for (js.Node node in targetsAndTries.reversed) {
+      if (node is js.Try) {
+        assert(node.finallyPart != null);
+        jumpStack.add(finallyLabels[node]);
+      } else if (node == target) {
+        jumpStack.add(targetLabel);
+        break;
+      }
+      // Ignore other nodes.
+    }
+    jumpStack = jumpStack.reversed.toList();
+    // As the program jumps directly to the top of the stack, it is taken off
+    // now.
+    int firstTarget = jumpStack.removeLast();
+    if (jumpStack.isNotEmpty) {
+      hasJumpThroughFinally = true;
+      js.Expression jsJumpStack = new js.ArrayInitializer(
+          jumpStack.map((int label) => js.number(label)).toList());
+      addStatement(js.js.statement("$nextName = #", [jsJumpStack]));
+    }
+    addGoto(firstTarget);
+  }
+
+  @override
+  void visitDefault(js.Default node) => unreachable(node);
+
+  @override
+  void visitDo(js.Do node) {
+    if (!shouldTransform(node)) {
+      bool oldInsideUntranslatedBreakable = insideUntranslatedBreakable;
+      insideUntranslatedBreakable = true;
+      withExpression(node.condition, (js.Expression condition) {
+        addStatement(new js.Do(translateInBlock(node.body), condition));
+      }, store: false);
+      insideUntranslatedBreakable = oldInsideUntranslatedBreakable;
+      return;
+    }
+    int startLabel = newLabel("do body");
+
+    int continueLabel = newLabel("do condition");
+    continueLabels[node] = continueLabel;
+
+    int afterLabel = newLabel("after do");
+    breakLabels[node] = afterLabel;
+
+    beginLabel(startLabel);
+
+    targetsAndTries.add(node);
+    visitStatement(node.body);
+    targetsAndTries.removeLast();
+
+    beginLabel(continueLabel);
+    withExpression(node.condition, (js.Expression condition) {
+      addStatement(new js.If.noElse(condition, gotoAndBreak(startLabel)));
+    }, store: false);
+    beginLabel(afterLabel);
+  }
+
+  @override
+  void visitEmptyStatement(js.EmptyStatement node) {
+    addStatement(node);
+  }
+
+  void visitExpressionInStatementContext(js.Expression node) {
+    if (node is js.VariableDeclarationList) {
+      // Treat js.VariableDeclarationList as a statement.
+      visitVariableDeclarationList(node);
+    } else {
+      visitExpressionIgnoreResult(node);
+    }
+  }
+
+  @override
+  void visitExpressionStatement(js.ExpressionStatement node) {
+    visitExpressionInStatementContext(node.expression);
+  }
+
+  @override
+  void visitFor(js.For node) {
+    if (!shouldTransform(node)) {
+      bool oldInsideUntranslated = insideUntranslatedBreakable;
+      insideUntranslatedBreakable = true;
+      // Note that node.init, node.condition, node.update all can be null, but
+      // withExpressions handles that.
+      withExpressions([
+        node.init,
+        node.condition,
+        node.update
+      ], (List<js.Expression> transformed) {
+        addStatement(new js.For(transformed[0], transformed[1], transformed[2],
+            translateInBlock(node.body)));
+      });
+      insideUntranslatedBreakable = oldInsideUntranslated;
+      return;
+    }
+
+    if (node.init != null) {
+      visitExpressionInStatementContext(node.init);
+    }
+    int startLabel = newLabel("for condition");
+    // If there is no update, continuing the loop is the same as going to the
+    // start.
+    int continueLabel =
+        (node.update == null) ? startLabel : newLabel("for update");
+    continueLabels[node] = continueLabel;
+    int afterLabel = newLabel("after for");
+    breakLabels[node] = afterLabel;
+    beginLabel(startLabel);
+    js.Expression condition = node.condition;
+    if (condition == null ||
+        (condition is js.LiteralBool && condition.value == true)) {
+      addStatement(new js.Comment("trivial condition"));
+    } else {
+      withExpression(condition, (js.Expression condition) {
+        addStatement(new js.If.noElse(
+            new js.Prefix("!", condition), gotoAndBreak(afterLabel)));
+      }, store: false);
+    }
+    targetsAndTries.add(node);
+    visitStatement(node.body);
+    targetsAndTries.removeLast();
+    if (node.update != null) {
+      beginLabel(continueLabel);
+      visitExpressionIgnoreResult(node.update);
+    }
+    addGoto(startLabel);
+    beginLabel(afterLabel);
+  }
+
+  @override
+  void visitForIn(js.ForIn node) {
+    // The dart output currently never uses for-in loops.
+    throw "Javascript for-in not implemented yet in the await transformation";
+  }
+
+  @override
+  void visitFunctionDeclaration(js.FunctionDeclaration node) {
+    unsupported(node);
+  }
+
+  // Only used for code where `!shouldTransform(node)`.
+  js.Block translateInBlock(js.Statement node) {
+    assert(!shouldTransform(node));
+    List<js.Statement> oldBuffer = currentStatementBuffer;
+    currentStatementBuffer = new List();
+    List<js.Statement> resultBuffer = currentStatementBuffer;
+    visitStatement(node);
+    currentStatementBuffer = oldBuffer;
+    return new js.Block(resultBuffer);
+  }
+
+  @override
+  void visitIf(js.If node) {
+    if (!shouldTransform(node.then) && !shouldTransform(node.otherwise)) {
+      withExpression(node.condition, (js.Expression condition) {
+        addStatement(new js.If(condition, translateInBlock(node.then),
+            translateInBlock(node.otherwise)));
+      }, store: false);
+      return;
+    }
+    int thenLabel = newLabel("then");
+    int joinLabel = newLabel("join");
+    int elseLabel =
+        node.otherwise is js.EmptyStatement ? joinLabel : newLabel("else");
+
+    withExpression(node.condition, (js.Expression condition) {
+      addExpressionStatement(
+          new js.Assignment(
+              new js.VariableUse(gotoName),
+              new js.Conditional(
+                  condition,
+                  js.number(thenLabel),
+                  js.number(elseLabel))));
+    }, store: false);
+    addBreak();
+    beginLabel(thenLabel);
+    visitStatement(node.then);
+    if (node.otherwise is! js.EmptyStatement) {
+      addGoto(joinLabel);
+      beginLabel(elseLabel);
+      visitStatement(node.otherwise);
+    }
+    beginLabel(joinLabel);
+  }
+
+  @override
+  visitInterpolatedExpression(js.InterpolatedExpression node) {
+    return unsupported(node);
+  }
+
+  @override
+  visitInterpolatedLiteral(js.InterpolatedLiteral node) => unsupported(node);
+
+  @override
+  visitInterpolatedParameter(js.InterpolatedParameter node) {
+    return unsupported(node);
+  }
+
+  @override
+  visitInterpolatedSelector(js.InterpolatedSelector node) {
+    return unsupported(node);
+  }
+
+  @override
+  visitInterpolatedStatement(js.InterpolatedStatement node) {
+    return unsupported(node);
+  }
+
+  @override
+  void visitLabeledStatement(js.LabeledStatement node) {
+    if (!shouldTransform(node)) {
+      addStatement(
+          new js.LabeledStatement(node.label, translateInBlock(node.body)));
+      return;
+    }
+    int breakLabel = newLabel("break ${node.label}");
+    int continueLabel = newLabel("continue ${node.label}");
+    breakLabels[node] = breakLabel;
+    continueLabels[node] = continueLabel;
+
+    beginLabel(continueLabel);
+    targetsAndTries.add(node);
+    visitStatement(node.body);
+    targetsAndTries.removeLast();
+    beginLabel(breakLabel);
+  }
+
+  @override
+  js.Expression visitLiteralBool(js.LiteralBool node) => node;
+
+  @override
+  visitLiteralExpression(js.LiteralExpression node) => unsupported(node);
+
+  @override
+  js.Expression visitLiteralNull(js.LiteralNull node) => node;
+
+  @override
+  js.Expression visitLiteralNumber(js.LiteralNumber node) => node;
+
+  @override
+  visitLiteralStatement(js.LiteralStatement node) => unsupported(node);
+
+  @override
+  js.Expression visitLiteralString(js.LiteralString node) => node;
+
+  @override
+  visitNamedFunction(js.NamedFunction node) {
+    unsupported(node);
+  }
+
+  @override
+  js.Expression visitNew(js.New node) {
+    bool storeTarget = node.arguments.any(shouldTransform);
+    return withExpression(node.target, (target) {
+      return withExpressions(node.arguments, (List<js.Expression> arguments) {
+        return new js.New(target, arguments);
+      });
+    }, store: storeTarget);
+  }
+
+  @override
+  js.Expression visitObjectInitializer(js.ObjectInitializer node) {
+    return withExpressions(
+        node.properties.map((js.Property property) => property.value).toList(),
+        (List<js.Node> values) {
+      List<js.Property> properties = new List.generate(values.length, (int i) {
+        return new js.Property(node.properties[i].name, values[i]);
+      });
+      return new js.ObjectInitializer(properties);
+    });
+  }
+
+  @override
+  visitParameter(js.Parameter node) => unreachable(node);
+
+  @override
+  js.Expression visitPostfix(js.Postfix node) {
+    if (node.op == "++" || node.op == "--") {
+      js.Expression argument = node.argument;
+      if (argument is js.VariableUse) {
+        return new js.Postfix(node.op, argument);
+      } else if (argument is js.PropertyAccess) {
+        return withExpression2(argument.receiver, argument.selector,
+            (receiver, selector) {
+          return new js.Postfix(
+              node.op, new js.PropertyAccess(receiver, selector));
+        });
+      } else {
+        throw "Unexpected postfix ${node.op} "
+            "operator argument ${node.argument}";
+      }
+    }
+    return withExpression(node.argument,
+        (js.Expression argument) => new js.Postfix(node.op, argument),
+        store: false);
+  }
+
+  @override
+  js.Expression visitPrefix(js.Prefix node) {
+    if (node.op == "++" || node.op == "--") {
+      js.Expression argument = node.argument;
+      if (argument is js.VariableUse) {
+        return new js.Prefix(node.op, argument);
+      } else if (argument is js.PropertyAccess) {
+        return withExpression2(argument.receiver, argument.selector,
+            (receiver, selector) {
+          return new js.Prefix(
+              node.op, new js.PropertyAccess(receiver, selector));
+        });
+      } else {
+        throw "Unexpected prefix ${node.op} operator "
+            "argument ${node.argument}";
+      }
+    }
+    return withExpression(node.argument,
+        (js.Expression argument) => new js.Prefix(node.op, argument),
+        store: false);
+  }
+
+  @override
+  visitProgram(js.Program node) => unsupported(node);
+
+  @override
+  js.Property visitProperty(js.Property node) {
+    return withExpression(
+        node.value, (js.Expression value) => new js.Property(node.name, value),
+        store: false);
+  }
+
+  @override
+  js.Expression visitRegExpLiteral(js.RegExpLiteral node) => node;
+
+  @override
+  void visitReturn(js.Return node) {
+    assert(node.value == null || !isSyncStar && !isAsyncStar);
+    js.Node target = analysis.targets[node];
+    if (node.value != null) {
+      withExpression(node.value, (js.Expression value) {
+        addStatement(js.js.statement("$returnValueName = #", [value]));
+      }, store: false);
+    }
+    translateJump(target, exitLabel);
+  }
+
+  @override
+  void visitSwitch(js.Switch node) {
+    if (!node.cases.any(shouldTransform)) {
+      // If only the key has an await, translation can be simplified.
+      bool oldInsideUntranslated = insideUntranslatedBreakable;
+      insideUntranslatedBreakable = true;
+      withExpression(node.key, (js.Expression key) {
+        List<js.SwitchClause> cases = node.cases.map((js.SwitchClause clause) {
+          if (clause is js.Case) {
+            return new js.Case(
+                clause.expression, translateInBlock(clause.body));
+          } else if (clause is js.Default) {
+            return new js.Default(translateInBlock(clause.body));
+          }
+        }).toList();
+        addStatement(new js.Switch(key, cases));
+      }, store: false);
+      insideUntranslatedBreakable = oldInsideUntranslated;
+      return;
+    }
+    int before = newLabel("switch");
+    int after = newLabel("after switch");
+    breakLabels[node] = after;
+
+    beginLabel(before);
+    List<int> labels = new List<int>(node.cases.length);
+
+    bool anyCaseExpressionTransformed = node.cases.any(
+        (js.SwitchClause x) => x is js.Case && shouldTransform(x.expression));
+    if (anyCaseExpressionTransformed) {
+      int defaultIndex = null; // Null means no default was found.
+      // If there is an await in one of the keys, a chain of ifs has to be used.
+
+      withExpression(node.key, (js.Expression key) {
+        int i = 0;
+        for (js.SwitchClause clause in node.cases) {
+          if (clause is js.Default) {
+            // The goto for the default case is added after all non-default
+            // clauses have been handled.
+            defaultIndex = i;
+            labels[i] = newLabel("default");
+            continue;
+          } else if (clause is js.Case) {
+            labels[i] = newLabel("case");
+            withExpression(clause.expression, (expression) {
+              addStatement(new js.If.noElse(
+                  new js.Binary("===", key, expression),
+                  gotoAndBreak(labels[i])));
+            }, store: false);
+          }
+          i++;
+        }
+      }, store: true);
+
+      if (defaultIndex == null) {
+        addGoto(after);
+      } else {
+        addGoto(labels[defaultIndex]);
+      }
+    } else {
+      bool hasDefault = false;
+      int i = 0;
+      List<js.SwitchClause> clauses = new List<js.SwitchClause>();
+      for (js.SwitchClause clause in node.cases) {
+        if (clause is js.Case) {
+          labels[i] = newLabel("case");
+          clauses.add(new js.Case(clause.expression, gotoAndBreak(labels[i])));
+        } else if (i is js.Default) {
+          labels[i] = newLabel("default");
+          clauses.add(new js.Default(gotoAndBreak(labels[i])));
+          hasDefault = true;
+        } else {
+          diagnosticListener.internalError(
+              spannable, "Unknown clause type $clause");
+        }
+        i++;
+      }
+      withExpression(node.key, (js.Expression key) {
+        addStatement(new js.Switch(key, clauses));
+      }, store: false);
+      if (!hasDefault) {
+        addGoto(after);
+      }
+    }
+
+    targetsAndTries.add(node);
+    for (int i = 0; i < labels.length; i++) {
+      beginLabel(labels[i]);
+      visitStatement(node.cases[i].body);
+    }
+    beginLabel(after);
+    targetsAndTries.removeLast();
+  }
+
+  @override
+  js.Expression visitThis(js.This node) {
+    return new js.VariableUse(selfName);
+  }
+
+  @override
+  void visitThrow(js.Throw node) {
+    withExpression(node.expression, (js.Expression expression) {
+      addStatement(new js.Throw(expression));
+    }, store: false);
+  }
+
+  setErrorHandler() {
+    addExpressionStatement(new js.Assignment(
+        new js.VariableUse(handlerName), currentErrorHandler));
+  }
+
+  @override
+  void visitTry(js.Try node) {
+    if (!shouldTransform(node)) {
+      js.Block body = translateInBlock(node.body);
+      js.Catch catchPart = (node.catchPart == null)
+          ? null
+          : new js.Catch(node.catchPart.declaration,
+              translateInBlock(node.catchPart.body));
+      js.Block finallyPart = (node.finallyPart == null)
+          ? null
+          : translateInBlock(node.finallyPart);
+      addStatement(new js.Try(body, catchPart, finallyPart));
+      return;
+    }
+    hasTryBlocks = true;
+    int handlerLabel = newLabel("catch");
+    int finallyLabel = newLabel("finally");
+    int afterFinallyLabel = newLabel("after finally");
+    errorHandlerLabels.add(handlerLabel);
+    // Set the error handler here. It must be cleared on every path out;
+    // normal and error exit.
+    setErrorHandler();
+    if (node.finallyPart != null) {
+      finallyLabels[node] = finallyLabel;
+      targetsAndTries.add(node);
+    }
+    visitStatement(node.body);
+    errorHandlerLabels.removeLast();
+    addStatement(
+        js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)]));
+    if (node.finallyPart == null) {
+      setErrorHandler();
+      addGoto(afterFinallyLabel);
+    } else {
+      // The handler is set as the first thing in the finally block.
+      addGoto(finallyLabel);
+    }
+    beginLabel(handlerLabel);
+    if (node.catchPart != null) {
+      setErrorHandler();
+      // The catch declaration name can shadow outer variables, so a fresh name
+      // is needed to avoid collisions.  See Ecma 262, 3rd edition,
+      // section 12.14.
+      String errorRename = freshName(node.catchPart.declaration.name);
+      localVariables.add(new js.VariableDeclaration(errorRename));
+      variableRenamings
+          .add(new Pair(node.catchPart.declaration.name, errorRename));
+      addExpressionStatement(new js.Assignment(
+          new js.VariableUse(errorRename), new js.VariableUse(resultName)));
+      visitStatement(node.catchPart.body);
+      variableRenamings.removeLast();
+    }
+    if (node.finallyPart != null) {
+      targetsAndTries.removeLast();
+      setErrorHandler();
+      // This belongs to the catch-part, but is only needed if there is a
+      // `finally`. Therefore it is in this branch.
+      // This is needed even if there is no explicit catch-branch, because
+      // if an exception is raised the finally part has to be run.
+      addStatement(
+          js.js.statement("$nextName = [#];", [js.number(afterFinallyLabel)]));
+      beginLabel(finallyLabel);
+      setErrorHandler();
+      visitStatement(node.finallyPart);
+      addStatement(new js.Comment("// goto the next finally handler"));
+      addStatement(js.js.statement("$gotoName = $nextName.pop();"));
+      addBreak();
+    }
+    beginLabel(afterFinallyLabel);
+  }
+
+  @override
+  visitVariableDeclaration(js.VariableDeclaration node) {
+    unreachable(node);
+  }
+
+  @override
+  void visitVariableDeclarationList(js.VariableDeclarationList node) {
+    // Declaration of local variables is hoisted outside the helper but the
+    // initialization is done here.
+    for (js.VariableInitialization initialization in node.declarations) {
+      js.VariableDeclaration declaration = initialization.declaration;
+      localVariables.add(declaration);
+      if (initialization.value != null) {
+        withExpression(initialization.value, (js.Expression value) {
+          addStatement(new js.ExpressionStatement(
+              new js.Assignment(new js.VariableUse(declaration.name), value)));
+        }, store: false);
+      }
+    }
+  }
+
+  @override
+  void visitVariableInitialization(js.VariableInitialization node) {
+    unreachable(node);
+  }
+
+  @override
+  js.Expression visitVariableUse(js.VariableUse node) {
+    Pair<String, String> renaming = variableRenamings.lastWhere(
+        (Pair renaming) => renaming.a == node.name, orElse: () => null);
+    if (renaming == null) return node;
+    return new js.VariableUse(renaming.b);
+  }
+
+  @override
+  void visitWhile(js.While node) {
+    if (!shouldTransform(node)) {
+      bool oldInsideUntranslated = insideUntranslatedBreakable;
+      insideUntranslatedBreakable = true;
+      withExpression(node.condition, (js.Expression condition) {
+        addStatement(new js.While(condition, translateInBlock(node.body)));
+      }, store: false);
+      insideUntranslatedBreakable = oldInsideUntranslated;
+      return;
+    }
+    int continueLabel = newLabel("while condition");
+    continueLabels[node] = continueLabel;
+    beginLabel(continueLabel);
+
+    int afterLabel = newLabel("after while");
+    breakLabels[node] = afterLabel;
+    js.Expression condition = node.condition;
+    // If the condition is `true`, a test is not needed.
+    if (!(condition is js.LiteralBool && condition.value == true)) {
+      withExpression(node.condition, (js.Expression condition) {
+        addStatement(new js.If.noElse(
+            new js.Prefix("!", condition), gotoAndBreak(afterLabel)));
+      }, store: false);
+    }
+    targetsAndTries.add(node);
+    visitStatement(node.body);
+    targetsAndTries.removeLast();
+    addGoto(continueLabel);
+    beginLabel(afterLabel);
+  }
+
+  /// Translates a yield/yield* in an sync*.
+  ///
+  /// `yield` in a sync* function just returns [value].
+  /// `yield*` wraps [value] in a [yieldStarExpression] and returns it.
+  void addSyncYield(js.DartYield node, js.Expression expression) {
+    assert(isSyncStar);
+    if (node.hasStar) {
+      addStatement(
+          new js.Return(new js.Call(yieldStarExpression, [expression])));
+    } else {
+      addStatement(new js.Return(expression));
+    }
+  }
+
+  /// Translates a yield/yield* in an async* function.
+  ///
+  /// yield/yield* in an async* function is translated much like the `await` is
+  /// translated in [visitAwait], only the object is wrapped in a
+  /// [yieldExpression]/[yieldStarExpression] to let [streamHelper] distinguish.
+  ///
+  /// Because there is no Future that can fail (as there is in await) null is
+  /// passed as the errorCallback.
+  void addAsyncYield(js.DartYield node, js.Expression expression) {
+    assert(isAsyncStar);
+    // Find all the finally blocks that should be performed if the stream is
+    // canceled during the yield.
+    // At the bottom of the stack is the return label.
+    List<int> enclosingFinallyLabels = <int>[exitLabel];
+    enclosingFinallyLabels.addAll(targetsAndTries
+        .where((js.Node node) => node is js.Try)
+        .map((js.Try node) => finallyLabels[node]));
+    int destinationOnCancel = enclosingFinallyLabels.removeLast();
+    js.ArrayInitializer finallyListInitializer = new js.ArrayInitializer(
+        enclosingFinallyLabels.map(js.number).toList());
+    addStatement(js.js.statement("""
+        return #streamHelper(#yieldExpression(#expression),
+            $helperName, $controllerName, function () {
+              if (#notEmptyFinallyList)
+                $nextName = #finallyList;
+              $gotoName = #destinationOnCancel;
+              $helperName();
+            });""", {
+      "streamHelper": streamHelper,
+      "yieldExpression": node.hasStar ? yieldStarExpression : yieldExpression,
+      "expression": expression,
+      "notEmptyFinallyList": enclosingFinallyLabels.isNotEmpty,
+      "finallyList": finallyListInitializer,
+      "destinationOnCancel": js.number(destinationOnCancel)
+    }));
+  }
+
+  @override
+  void visitDartYield(js.DartYield node) {
+    assert(isSyncStar || isAsyncStar);
+    int label = newLabel("after yield");
+    // Don't do a break here for the goto, but instead a return in either
+    // addSynYield or addAsyncYield.
+    withExpression(node.expression, (js.Expression expression) {
+      addStatement(setGotoVariable(label));
+      if (isSyncStar) {
+        addSyncYield(node, expression);
+      } else {
+        addAsyncYield(node, expression);
+      }
+    }, store: false);
+    beginLabel(label);
+  }
+}
+
+/// Finds out
+///
+/// - which expressions have yield or await nested in them.
+/// - targets of jumps
+/// - a set of used names.
+/// - if any [This]-expressions are used.
+class PreTranslationAnalysis extends js.NodeVisitor<bool> {
+  Set<js.Node> hasAwaitOrYield = new Set<js.Node>();
+
+  Map<js.Node, js.Node> targets = new Map<js.Node, js.Node>();
+  List<js.Node> loopsAndSwitches = new List<js.Node>();
+  List<js.LabeledStatement> labelledStatements =
+      new List<js.LabeledStatement>();
+  Set<String> usedNames = new Set<String>();
+
+  bool hasExplicitReturns = false;
+
+  bool hasThis = false;
+
+  // The function currently being analyzed.
+  js.Fun currentFunction;
+
+  // For error messages.
+  final Function unsupported;
+
+  PreTranslationAnalysis(void this.unsupported(js.Node node));
+
+  bool visit(js.Node node) {
+    bool containsAwait = node.accept(this);
+    if (containsAwait) {
+      hasAwaitOrYield.add(node);
+    }
+    return containsAwait;
+  }
+
+  analyze(js.Fun node) {
+    currentFunction = node;
+    node.params.forEach(visit);
+    visit(node.body);
+  }
+
+  @override
+  bool visitAccess(js.PropertyAccess node) {
+    bool receiver = visit(node.receiver);
+    bool selector = visit(node.selector);
+    return receiver || selector;
+  }
+
+  @override
+  bool visitArrayHole(js.ArrayHole node) {
+    return false;
+  }
+
+  @override
+  bool visitArrayInitializer(js.ArrayInitializer node) {
+    bool containsAwait = false;
+    for (js.Expression element in node.elements) {
+      if (visit(element)) containsAwait = true;
+    }
+    return containsAwait;
+  }
+
+  @override
+  bool visitAssignment(js.Assignment node) {
+    bool leftHandSide = visit(node.leftHandSide);
+    bool value = (node.value == null) ? false : visit(node.value);
+    return leftHandSide || value;
+  }
+
+  @override
+  bool visitAwait(js.Await node) {
+    visit(node.expression);
+    return true;
+  }
+
+  @override
+  bool visitBinary(js.Binary node) {
+    bool left = visit(node.left);
+    bool right = visit(node.right);
+    return left || right;
+  }
+
+  @override
+  bool visitBlock(js.Block node) {
+    bool containsAwait = false;
+    for (js.Statement statement in node.statements) {
+      if (visit(statement)) containsAwait = true;
+    }
+    return containsAwait;
+  }
+
+  @override
+  bool visitBreak(js.Break node) {
+    if (node.targetLabel != null) {
+      targets[node] = labelledStatements.lastWhere(
+          (js.LabeledStatement statement) {
+        return statement.label == node.targetLabel;
+      });
+    } else {
+      targets[node] = loopsAndSwitches.last;
+    }
+    return false;
+  }
+
+  @override
+  bool visitCall(js.Call node) {
+    bool containsAwait = visit(node.target);
+    for (js.Expression argument in node.arguments) {
+      if (visit(argument)) containsAwait = true;
+    }
+    return containsAwait;
+  }
+
+  @override
+  bool visitCase(js.Case node) {
+    bool expression = visit(node.expression);
+    bool body = visit(node.body);
+    return expression || body;
+  }
+
+  @override
+  bool visitCatch(js.Catch node) {
+    bool declaration = visit(node.declaration);
+    bool body = visit(node.body);
+    return declaration || body;
+  }
+
+  @override
+  bool visitComment(js.Comment node) {
+    return false;
+  }
+
+  @override
+  bool visitConditional(js.Conditional node) {
+    bool condition = visit(node.condition);
+    bool then = visit(node.then);
+    bool otherwise = visit(node.otherwise);
+    return condition || then || otherwise;
+  }
+
+  @override
+  bool visitContinue(js.Continue node) {
+    if (node.targetLabel != null) {
+      targets[node] = labelledStatements.lastWhere(
+          (js.LabeledStatement stm) => stm.label == node.targetLabel);
+    } else {
+      targets[node] =
+          loopsAndSwitches.lastWhere((js.Node node) => node is! js.Switch);
+    }
+    assert(() {
+      js.Node target = targets[node];
+      return target is js.Loop ||
+          (target is js.LabeledStatement && target.body is js.Loop);
+    });
+    return false;
+  }
+
+  @override
+  bool visitDefault(js.Default node) {
+    return visit(node.body);
+  }
+
+  @override
+  bool visitDo(js.Do node) {
+    loopsAndSwitches.add(node);
+    bool body = visit(node.body);
+    bool condition = visit(node.condition);
+    loopsAndSwitches.removeLast();
+    return body || condition;
+  }
+
+  @override
+  bool visitEmptyStatement(js.EmptyStatement node) {
+    return false;
+  }
+
+  @override
+  bool visitExpressionStatement(js.ExpressionStatement node) {
+    return visit(node.expression);
+  }
+
+  @override
+  bool visitFor(js.For node) {
+    bool init = (node.init == null) ? false : visit(node.init);
+    bool condition = (node.condition == null) ? false : visit(node.condition);
+    bool update = (node.update == null) ? false : visit(node.update);
+    loopsAndSwitches.add(node);
+    bool body = visit(node.body);
+    loopsAndSwitches.removeLast();
+    return init || condition || update || body;
+  }
+
+  @override
+  bool visitForIn(js.ForIn node) {
+    bool object = visit(node.object);
+    loopsAndSwitches.add(node);
+    bool body = visit(node.body);
+    loopsAndSwitches.removeLast();
+    return object || body;
+  }
+
+  @override
+  bool visitFun(js.Fun node) {
+    return false;
+  }
+
+  @override
+  bool visitFunctionDeclaration(js.FunctionDeclaration node) {
+    return false;
+  }
+
+  @override
+  bool visitIf(js.If node) {
+    bool condition = visit(node.condition);
+    bool then = visit(node.then);
+    bool otherwise = visit(node.otherwise);
+    return condition || then || otherwise;
+  }
+
+  @override
+  bool visitInterpolatedExpression(js.InterpolatedExpression node) {
+    return unsupported(node);
+  }
+
+  @override
+  bool visitInterpolatedLiteral(js.InterpolatedLiteral node) {
+    return unsupported(node);
+  }
+
+  @override
+  bool visitInterpolatedParameter(js.InterpolatedParameter node) {
+    return unsupported(node);
+  }
+
+  @override
+  bool visitInterpolatedSelector(js.InterpolatedSelector node) {
+    return unsupported(node);
+  }
+
+  @override
+  bool visitInterpolatedStatement(js.InterpolatedStatement node) {
+    return unsupported(node);
+  }
+
+  @override
+  bool visitLabeledStatement(js.LabeledStatement node) {
+    usedNames.add(node.label);
+    labelledStatements.add(node);
+    bool containsAwait = visit(node.body);
+    labelledStatements.removeLast();
+    return containsAwait;
+  }
+
+  @override
+  bool visitLiteralBool(js.LiteralBool node) {
+    return false;
+  }
+
+  @override
+  bool visitLiteralExpression(js.LiteralExpression node) {
+    return unsupported(node);
+  }
+
+  @override
+  bool visitLiteralNull(js.LiteralNull node) {
+    return false;
+  }
+
+  @override
+  bool visitLiteralNumber(js.LiteralNumber node) {
+    return false;
+  }
+
+  @override
+  bool visitLiteralStatement(js.LiteralStatement node) {
+    return unsupported(node);
+  }
+
+  @override
+  bool visitLiteralString(js.LiteralString node) {
+    return false;
+  }
+
+  @override
+  bool visitNamedFunction(js.NamedFunction node) {
+    return false;
+  }
+
+  @override
+  bool visitNew(js.New node) {
+    return visitCall(node);
+  }
+
+  @override
+  bool visitObjectInitializer(js.ObjectInitializer node) {
+    bool containsAwait = false;
+    for (js.Property property in node.properties) {
+      if (visit(property)) containsAwait = true;
+    }
+    return containsAwait;
+  }
+
+  @override
+  bool visitParameter(js.Parameter node) {
+    usedNames.add(node.name);
+    return false;
+  }
+
+  @override
+  bool visitPostfix(js.Postfix node) {
+    return visit(node.argument);
+  }
+
+  @override
+  bool visitPrefix(js.Prefix node) {
+    return visit(node.argument);
+  }
+
+  @override
+  bool visitProgram(js.Program node) {
+    throw "Unexpected";
+  }
+
+  @override
+  bool visitProperty(js.Property node) {
+    return visit(node.value);
+  }
+
+  @override
+  bool visitRegExpLiteral(js.RegExpLiteral node) {
+    return false;
+  }
+
+  @override
+  bool visitReturn(js.Return node) {
+    hasExplicitReturns = true;
+    targets[node] = currentFunction;
+    if (node.value == null) return false;
+    return visit(node.value);
+  }
+
+  @override
+  bool visitSwitch(js.Switch node) {
+    loopsAndSwitches.add(node);
+    bool result = visit(node.key);
+    for (js.SwitchClause clause in node.cases) {
+      if (visit(clause)) result = true;
+    }
+    loopsAndSwitches.removeLast();
+    return result;
+  }
+
+  @override
+  bool visitThis(js.This node) {
+    hasThis = true;
+    return false;
+  }
+
+  @override
+  bool visitThrow(js.Throw node) {
+    return visit(node.expression);
+  }
+
+  @override
+  bool visitTry(js.Try node) {
+    bool body = visit(node.body);
+    bool catchPart = (node.catchPart == null) ? false : visit(node.catchPart);
+    bool finallyPart =
+        (node.finallyPart == null) ? false : visit(node.finallyPart);
+    return body || catchPart || finallyPart;
+  }
+
+  @override
+  bool visitVariableDeclaration(js.VariableDeclaration node) {
+    usedNames.add(node.name);
+    return false;
+  }
+
+  @override
+  bool visitVariableDeclarationList(js.VariableDeclarationList node) {
+    bool result = false;
+    for (js.VariableInitialization init in node.declarations) {
+      if (visit(init)) result = true;
+    }
+    return result;
+  }
+
+  @override
+  bool visitVariableInitialization(js.VariableInitialization node) {
+    return visitAssignment(node);
+  }
+
+  @override
+  bool visitVariableUse(js.VariableUse node) {
+    usedNames.add(node.name);
+    return false;
+  }
+
+  @override
+  bool visitWhile(js.While node) {
+    loopsAndSwitches.add(node);
+    bool condition = visit(node.condition);
+    bool body = visit(node.body);
+    loopsAndSwitches.removeLast();
+    return condition || body;
+  }
+
+  @override
+  bool visitDartYield(js.DartYield node) {
+    visit(node.expression);
+    return true;
+  }
+}
diff --git a/pkg/compiler/lib/src/js/template.dart b/pkg/compiler/lib/src/js/template.dart
index ce57d09..7740431 100644
--- a/pkg/compiler/lib/src/js/template.dart
+++ b/pkg/compiler/lib/src/js/template.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.
 
-part of js;
+part of js_ast;
 
 class TemplateManager {
   Map<String, Template> expressionTemplates = new Map<String, Template>();
@@ -435,6 +435,11 @@
     return (arguments) => new Return(makeExpression(arguments));
   }
 
+  Instantiator visitDartYield(DartYield node) {
+    Instantiator makeExpression = visit(node.expression);
+    return (arguments) => new DartYield(makeExpression(arguments), node.hasStar);
+  }
+
   Instantiator visitThrow(Throw node) {
     Instantiator makeExpression = visit(node.expression);
     return (arguments) => new Throw(makeExpression(arguments));
@@ -487,12 +492,13 @@
         new FunctionDeclaration(makeName(arguments), makeFunction(arguments));
   }
 
-  Instantiator visitLabeledStatement(LabeledStatement node) =>
-      TODO('visitLabeledStatement');
+  Instantiator visitLabeledStatement(LabeledStatement node) {
+    Instantiator makeBody = visit(node.body);
+    return (arguments) => new LabeledStatement(node.label, makeBody(arguments));
+  }
+
   Instantiator visitLiteralStatement(LiteralStatement node) =>
       TODO('visitLiteralStatement');
-  Instantiator visitBlob(Blob node) =>
-      TODO('visitBlob');
   Instantiator visitLiteralExpression(LiteralExpression node) =>
       TODO('visitLiteralExpression');
 
@@ -701,12 +707,12 @@
 }
 
 /**
- * InterpolatedNodeAnalysis extract [InterpolatedNode]s from AST.
+ * InterpolatedNodeAnalysis determines which AST trees contain
+ * [InterpolatedNode]s, and the names of the named interpolated nodes.
  */
 class InterpolatedNodeAnalysis extends BaseVisitor {
-  final Setlet<Node> containsInterpolatedNode = new Setlet<Node>();
-  final List<InterpolatedNode> interpolatedNodes = <InterpolatedNode>[];
-  final Setlet<String> holeNames = new Setlet<String>();
+  final Set<Node> containsInterpolatedNode = new Set<Node>();
+  final Set<String> holeNames = new Set<String>();
   int count = 0;
 
   InterpolatedNodeAnalysis();
@@ -726,7 +732,6 @@
   }
 
   visitInterpolatedNode(InterpolatedNode node) {
-    interpolatedNodes.add(node);
     containsInterpolatedNode.add(node);
     if (node.isNamed) holeNames.add(node.nameOrPosition);
     ++count;
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index b3ec3fd..e396899 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -943,6 +943,7 @@
       Element e = findHelper('boolConversionCheck');
       if (e != null) enqueue(world, e, registry);
     }
+
     if (TRACE_CALLS) {
       traceHelper = findHelper('traceHelper');
       assert(traceHelper != null);
@@ -1599,6 +1600,71 @@
     return findHelper("throwCyclicInit");
   }
 
+  Element getThenHelper() {
+    return findHelper("thenHelper");
+  }
+
+  Element getYieldStar() {
+    ClassElement classElement = findHelper("IterationMarker");
+    classElement.ensureResolved(compiler);
+    return classElement.lookupLocalMember("yieldStar");
+  }
+
+  Element getYieldSingle() {
+    ClassElement classElement = findHelper("IterationMarker");
+    classElement.ensureResolved(compiler);
+    return classElement.lookupLocalMember("yieldSingle");
+  }
+
+  Element getStreamHelper() {
+    return findHelper("streamHelper");
+  }
+
+  Element getStreamOfController() {
+    return findHelper("streamOfController");
+  }
+
+  Element getEndOfIteration() {
+    ClassElement classElement = findHelper("IterationMarker");
+    classElement.ensureResolved(compiler);
+    return classElement.lookupLocalMember("endOfIteration");
+  }
+
+  Element getSyncStarIterable() {
+    ClassElement classElement = findHelper("SyncStarIterable");
+    classElement.ensureResolved(compiler);
+    return classElement;
+  }
+
+  Element getSyncStarIterableConstructor() {
+    ClassElement classElement = getSyncStarIterable();
+    classElement.ensureResolved(compiler);
+    return classElement.lookupConstructor("");
+  }
+
+  Element getCompleterConstructor() {
+    ClassElement classElement = find(compiler.asyncLibrary, "Completer");
+    classElement.ensureResolved(compiler);
+    return classElement.lookupConstructor("");
+  }
+
+  Element getASyncStarController() {
+    ClassElement classElement = findHelper("AsyncStarStreamController");
+    classElement.ensureResolved(compiler);
+    return classElement;
+  }
+
+  Element getASyncStarControllerConstructor() {
+    ClassElement classElement = getASyncStarController();
+    return classElement.lookupConstructor("");
+  }
+
+  Element getStreamIteratorConstructor() {
+    ClassElement classElement = find(compiler.asyncLibrary, "StreamIterator");
+    classElement.ensureResolved(compiler);
+    return classElement.lookupConstructor("");
+  }
+
   bool isNullImplementation(ClassElement cls) {
     return cls == jsNullClass;
   }
@@ -2334,6 +2400,29 @@
     String extension = addExtension ? ".part.js" : "";
     return "${outName}_$name$extension";
   }
+
+  void registerAsyncMarker(FunctionElement element,
+                           Enqueuer enqueuer,
+                           Registry registry) {
+    if (element.asyncMarker == AsyncMarker.ASYNC) {
+      enqueue(enqueuer, getThenHelper(), registry);
+      enqueue(enqueuer, getCompleterConstructor(), registry);
+      enqueue(enqueuer, getStreamIteratorConstructor(), registry);
+    } else if (element.asyncMarker == AsyncMarker.SYNC_STAR) {
+      enqueuer.registerInstantiatedClass(getSyncStarIterable(), registry);
+      enqueue(enqueuer, getSyncStarIterableConstructor(), registry);
+      enqueue(enqueuer, getEndOfIteration(), registry);
+      enqueue(enqueuer, getYieldStar(), registry);
+    } else if (element.asyncMarker == AsyncMarker.ASYNC_STAR) {
+      enqueuer.registerInstantiatedClass(getASyncStarController(), registry);
+      enqueue(enqueuer, getStreamHelper(), registry);
+      enqueue(enqueuer, getStreamOfController(), registry);
+      enqueue(enqueuer, getYieldSingle(), registry);
+      enqueue(enqueuer, getYieldStar(), registry);
+      enqueue(enqueuer, getASyncStarControllerConstructor(), registry);
+      enqueue(enqueuer, getStreamIteratorConstructor(), registry);
+    }
+  }
 }
 
 /// Handling of special annotations for tests.
diff --git a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart
index 1237350..b773f95 100644
--- a/pkg/compiler/lib/src/js_backend/codegen/codegen.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen/codegen.dart
@@ -135,8 +135,27 @@
 
   @override
   js.Expression visitConcatenateStrings(tree_ir.ConcatenateStrings node) {
-    return giveup(node);
-    // TODO: implement visitConcatenateStrings
+    js.Expression addStrings(js.Expression left, js.Expression right) {
+      return new js.Binary('+', left, right);
+    }
+
+    js.Expression toString(tree_ir.Expression input) {
+      bool useDirectly = input is tree_ir.Constant &&
+          (input.expression.value.isString ||
+           input.expression.value.isInt ||
+           input.expression.value.isBool);
+      js.Expression value = visitExpression(input);
+      if (useDirectly) {
+        return value;
+      } else {
+        Element convertToString = glue.getStringConversion();
+        registry.registerStaticUse(convertToString);
+        js.Expression access = glue.staticFunctionAccess(convertToString);
+        return (new js.Call(access, <js.Expression>[value]));
+      }
+    }
+
+    return node.arguments.map(toString).reduce(addStrings);
   }
 
   @override
@@ -188,16 +207,6 @@
     }
   }
 
-  List<js.Expression> compileStaticArgumentList(
-      Selector selector,
-      Element target, /* TODO(karlklose): this should be the signature. */
-      List<tree_ir.Expression> arguments) {
-    return selector.makeArgumentsList(
-        target.implementation,
-        visitArguments(arguments),
-        compileConstant);
-  }
-
   @override
   js.Expression visitInvokeConstructor(tree_ir.InvokeConstructor node) {
     checkStaticTargetIsValid(node, node.target);
@@ -206,8 +215,7 @@
     registry.registerInstantiatedClass(node.target.enclosingClass);
     Selector selector = node.selector;
     FunctionElement target = node.target;
-    List<js.Expression> arguments =
-        compileStaticArgumentList(selector, target, node.arguments);
+    List<js.Expression> arguments = visitArguments(node.arguments);
     return buildStaticInvoke(selector, target, arguments);
   }
 
@@ -259,8 +267,7 @@
     }
     Selector selector = node.selector;
     FunctionElement target = node.target;
-    List<js.Expression> arguments =
-        compileStaticArgumentList(selector, target, node.arguments);
+    List<js.Expression> arguments = visitArguments(node.arguments);
     return buildStaticInvoke(selector, target, arguments);
   }
 
diff --git a/pkg/compiler/lib/src/js_backend/codegen/glue.dart b/pkg/compiler/lib/src/js_backend/codegen/glue.dart
index 2b86147..0c8310c 100644
--- a/pkg/compiler/lib/src/js_backend/codegen/glue.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen/glue.dart
@@ -30,6 +30,10 @@
     return _emitter.constantReference(value);
   }
 
+  Element getStringConversion() {
+    return _backend.getStringInterpolationHelper();
+  }
+
   reportInternalError(String message) {
     _compiler.internalError(_compiler.currentElement, message);
   }
diff --git a/pkg/compiler/lib/src/js_backend/codegen/task.dart b/pkg/compiler/lib/src/js_backend/codegen/task.dart
index 4797173..dbfb871 100644
--- a/pkg/compiler/lib/src/js_backend/codegen/task.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen/task.dart
@@ -11,17 +11,14 @@
 
 import '../js_backend.dart';
 import '../../dart2jslib.dart';
-import '../../io/source_file.dart';
 import '../../cps_ir/cps_ir_nodes.dart' as cps;
 import '../../cps_ir/cps_ir_builder.dart';
 import '../../tree_ir/tree_ir_nodes.dart' as tree_ir;
-import '../../tree/tree.dart' as ast;
 import '../../types/types.dart' show TypeMask, UnionTypeMask, FlatTypeMask,
     ForwardingTypeMask;
-import '../../scanner/scannerlib.dart' as scanner;
 import '../../elements/elements.dart';
 import '../../js/js.dart' as js;
-import '../../io/source_map_builder.dart';
+import '../../io/source_information.dart' show StartEndSourceInformation;
 import '../../tree_ir/tree_ir_builder.dart' as tree_builder;
 import '../../dart_backend/backend_ast_emitter.dart' as backend_ast_emitter;
 import '../../cps_ir/optimizers.dart';
@@ -207,36 +204,7 @@
   }
 
   js.Node attachPosition(js.Node node, AstElement element) {
-    // TODO(sra): Attaching positions might be cleaner if the source position
-    // was on a wrapping node.
-    SourceFile sourceFile = sourceFileOfElement(element);
-    String name = element.name;
-    AstElement implementation = element.implementation;
-    ast.Node expression = implementation.node;
-    scanner.Token beginToken;
-    scanner.Token endToken;
-    if (expression == null) {
-      // Synthesized node. Use the enclosing element for the location.
-      beginToken = endToken = element.position;
-    } else {
-      beginToken = expression.getBeginToken();
-      endToken = expression.getEndToken();
-    }
-    // TODO(podivilov): find the right sourceFile here and remove offset
-    // checks below.
-    var sourcePosition, endSourcePosition;
-    if (beginToken.charOffset < sourceFile.length) {
-      sourcePosition =
-          new TokenSourceFileLocation(sourceFile, beginToken, name);
-    }
-    if (endToken.charOffset < sourceFile.length) {
-      endSourcePosition =
-          new TokenSourceFileLocation(sourceFile, endToken, name);
-    }
-    return node.withPosition(sourcePosition, endSourcePosition);
-  }
-
-  SourceFile sourceFileOfElement(Element element) {
-    return element.implementation.compilationUnit.script.file;
+    return node.withSourceInformation(
+        StartEndSourceInformation.computeSourceInformation(element));
   }
 }
diff --git a/pkg/compiler/lib/src/js_backend/patch_resolver.dart b/pkg/compiler/lib/src/js_backend/patch_resolver.dart
index d538fe5..84d67cc 100644
--- a/pkg/compiler/lib/src/js_backend/patch_resolver.dart
+++ b/pkg/compiler/lib/src/js_backend/patch_resolver.dart
@@ -26,7 +26,6 @@
       });
       checkMatchingPatchSignatures(element, patch);
       element = patch;
-      ResolverTask.processAsyncMarker(compiler, element);
     } else {
       compiler.reportError(
          element, MessageKind.PATCH_EXTERNAL_WITHOUT_IMPLEMENTATION);
diff --git a/pkg/compiler/lib/src/js_emitter/js_emitter.dart b/pkg/compiler/lib/src/js_emitter/js_emitter.dart
index f4f172c..d92af17 100644
--- a/pkg/compiler/lib/src/js_emitter/js_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/js_emitter.dart
@@ -92,11 +92,12 @@
 part 'code_emitter_task.dart';
 part 'helpers.dart';
 part 'interceptor_stub_generator.dart';
+part 'main_call_stub_generator.dart';
 part 'metadata_collector.dart';
 part 'native_emitter.dart';
 part 'native_generator.dart';
 part 'parameter_stub_generator.dart';
-part 'type_test_generator.dart';
+part 'runtime_type_generator.dart';
 part 'type_test_registry.dart';
 
 part 'old_emitter/class_builder.dart';
diff --git a/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
new file mode 100644
index 0000000..4748854
--- /dev/null
+++ b/pkg/compiler/lib/src/js_emitter/main_call_stub_generator.dart
@@ -0,0 +1,82 @@
+// 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 dart2js.js_emitter;
+
+class MainCallStubGenerator {
+  final Compiler compiler;
+  final JavaScriptBackend backend;
+  final CodeEmitterTask emitterTask;
+
+  MainCallStubGenerator(this.compiler, this.backend, this.emitterTask);
+
+  /// Returns the code equivalent to:
+  ///   `function(args) { $.startRootIsolate(X.main$closure(), args); }`
+  jsAst.Expression _buildIsolateSetupClosure(Element appMain,
+                                            Element isolateMain) {
+    jsAst.Expression mainAccess =
+        emitterTask.isolateStaticClosureAccess(appMain);
+    // Since we pass the closurized version of the main method to
+    // the isolate method, we must make sure that it exists.
+    return js('function(a){ #(#, a); }',
+        [emitterTask.staticFunctionAccess(isolateMain), mainAccess]);
+  }
+
+
+  jsAst.Statement generateInvokeMain() {
+    Element main = compiler.mainFunction;
+    jsAst.Expression mainCallClosure = null;
+    if (compiler.hasIsolateSupport) {
+      Element isolateMain =
+        backend.isolateHelperLibrary.find(JavaScriptBackend.START_ROOT_ISOLATE);
+      mainCallClosure = _buildIsolateSetupClosure(main, isolateMain);
+    } else if (compiler.hasIncrementalSupport) {
+      mainCallClosure = js(
+          'function() { return #(); }',
+          emitterTask.staticFunctionAccess(main));
+    } else {
+      mainCallClosure = emitterTask.staticFunctionAccess(main);
+    }
+
+    jsAst.Expression currentScriptAccess =
+        emitterTask.generateEmbeddedGlobalAccess(embeddedNames.CURRENT_SCRIPT);
+
+    // This code finds the currently executing script by listening to the
+    // onload event of all script tags and getting the first script which
+    // finishes. Since onload is called immediately after execution this should
+    // not substantially change execution order.
+    return js.statement('''
+      (function (callback) {
+        if (typeof document === "undefined") {
+          callback(null);
+          return;
+        }
+        if (document.currentScript) {
+          callback(document.currentScript);
+          return;
+        }
+
+        var scripts = document.scripts;
+        function onLoad(event) {
+          for (var i = 0; i < scripts.length; ++i) {
+            scripts[i].removeEventListener("load", onLoad, false);
+          }
+          callback(event.target);
+        }
+        for (var i = 0; i < scripts.length; ++i) {
+          scripts[i].addEventListener("load", onLoad, false);
+        }
+      })(function(currentScript) {
+        #currentScript = currentScript;
+
+        if (typeof dartMainRunner === "function") {
+          dartMainRunner(#mainCallClosure, []);
+        } else {
+          #mainCallClosure([]);
+        }
+      })''',
+      {'currentScript': currentScriptAccess,
+       'mainCallClosure': mainCallClosure});
+  }
+}
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index 313fb80..96e0da9 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -4,7 +4,7 @@
 
 library dart2js.new_js_emitter.model;
 
-import '../js/js.dart' as js show Expression;
+import '../js/js.dart' as js show Expression, Statement;
 import '../constants/values.dart' show ConstantValue;
 
 import '../deferred_load.dart' show OutputUnit;
@@ -17,6 +17,7 @@
   final List<Fragment> fragments;
   final bool outputContainsConstantList;
   final bool outputContainsNativeClasses;
+  final bool hasIsolateSupport;
   /// A map from load id to the list of fragments that need to be loaded.
   final Map<String, List<Fragment>> loadMap;
 
@@ -33,9 +34,11 @@
           this.typeToInterceptorMap,
           this._metadataCollector,
           {this.outputContainsNativeClasses,
-           this.outputContainsConstantList}) {
+           this.outputContainsConstantList,
+           this.hasIsolateSupport}) {
     assert(outputContainsNativeClasses != null);
     assert(outputContainsConstantList != null);
+    assert(hasIsolateSupport != null);
   }
 
   /// A list of pretty-printed JavaScript expressions.
@@ -103,12 +106,12 @@
  * other [DeferredFragment]s.
  */
 class MainFragment extends Fragment {
-  final js.Expression main;
+  final js.Statement invokeMain;
   final List<Holder> holders;
 
   MainFragment(OutputUnit outputUnit,
                String outputFileName,
-               this.main,
+               this.invokeMain,
                List<Library> libraries,
                List<StaticField> staticNonFinalFields,
                List<StaticField> staticLazilyInitializedFields,
@@ -209,6 +212,9 @@
   /// Stub methods for this class that are call stubs for getters.
   final List<StubMethod> callStubs;
 
+  /// Stub methods for this class handling reads to type variables.
+  final List<StubMethod> typeVariableReaderStubs;
+
   /// noSuchMethod stubs in the special case that the class is Object.
   final List<StubMethod> noSuchMethodStubs;
   final List<Field> staticFieldsForReflection;
@@ -231,6 +237,7 @@
         this.fields,
         this.staticFieldsForReflection,
         this.callStubs,
+        this.typeVariableReaderStubs,
         this.noSuchMethodStubs,
         this.isChecks,
         this.functionTypeIndex,
@@ -262,6 +269,7 @@
                    List<Field> instanceFields,
                    List<Field> staticFieldsForReflection,
                    List<StubMethod> callStubs,
+                   List<StubMethod> typeVariableReaderStubs,
                    List<StubMethod> isChecks,
                    int functionTypeIndex,
                    {bool onlyForRti,
@@ -272,6 +280,7 @@
               instanceFields,
               staticFieldsForReflection,
               callStubs,
+              typeVariableReaderStubs,
               const <StubMethod>[],
               isChecks, functionTypeIndex,
               onlyForRti: onlyForRti,
diff --git a/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart
index 6e37f27..de9ea11 100644
--- a/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/new_emitter/model_emitter.dart
@@ -17,6 +17,7 @@
     NativeEmitter;
 
 import 'package:_internal/compiler/js_lib/shared/embedded_names.dart' show
+    CREATE_NEW_ISOLATE,
     DEFERRED_LIBRARY_URIS,
     DEFERRED_LIBRARY_HASHES,
     GET_TYPE_FROM_NAME,
@@ -33,7 +34,6 @@
 import '../model.dart';
 
 
-
 class ModelEmitter {
   final Compiler compiler;
   final Namer namer;
@@ -62,14 +62,12 @@
   int emitProgram(Program program) {
     List<Fragment> fragments = program.fragments;
     MainFragment mainFragment = fragments.first;
-    js.Statement mainAst = emitMainFragment(program);
-    String mainCode = js.prettyPrint(mainAst, compiler).getText();
-    compiler.outputProvider(mainFragment.outputFileName, 'js')
-        ..add(buildGeneratedBy(compiler))
-        ..add(mainCode)
-        ..close();
-    int totalSize = mainCode.length;
 
+    int totalSize = 0;
+
+    // We have to emit the deferred fragments first, since we need their
+    // deferred hash (which depends on the output) when emitting the main
+    // fragment.
     fragments.skip(1).forEach((DeferredFragment deferredUnit) {
       js.Expression ast =
           emitDeferredFragment(deferredUnit, mainFragment.holders);
@@ -79,10 +77,19 @@
           ..add(code)
           ..close();
     });
+
+    js.Statement mainAst = emitMainFragment(program);
+    String mainCode = js.prettyPrint(mainAst, compiler).getText();
+    compiler.outputProvider(mainFragment.outputFileName, 'js')
+        ..add(buildGeneratedBy(compiler))
+        ..add(mainCode)
+        ..close();
+    totalSize += mainCode.length;
+
     return totalSize;
   }
 
-  js.LiteralString unparse(Compiler compiler, js.Expression value) {
+  js.LiteralString unparse(Compiler compiler, js.Node value) {
     String text = js.prettyPrint(value, compiler).getText();
     if (value is js.Fun) text = '($text)';
     return js.js.escapedString(text);
@@ -117,7 +124,7 @@
          emitStaticNonFinalFields(fragment.staticNonFinalFields),
        'operatorIsPrefix': js.string(namer.operatorIsPrefix),
        'eagerClasses': emitEagerClassInitializations(fragment.libraries),
-       'main': fragment.main,
+       'invokeMain': fragment.invokeMain,
        'code': code};
 
     holes.addAll(nativeHoles(program));
@@ -201,9 +208,7 @@
     List<js.Property> globals = <js.Property>[];
 
     if (program.loadMap.isNotEmpty) {
-      globals.addAll(emitLoadUrisAndHashes(program.loadMap));
-      globals.add(emitIsHunkLoadedFunction());
-      globals.add(emitInitializeLoadedHunk());
+      globals.addAll(emitEmbeddedGlobalsForDeferredLoading(program.loadMap));
     }
 
     if (program.typeToInterceptorMap != null) {
@@ -211,6 +216,14 @@
                                   program.typeToInterceptorMap));
     }
 
+    if (program.hasIsolateSupport) {
+      String isolateName = namer.currentIsolate;
+      globals.add(
+          new js.Property(js.string(CREATE_NEW_ISOLATE),
+                          js.js('function () { return $isolateName; }')));
+      // TODO(floitsch): add remaining isolate functions.
+    }
+
     globals.add(emitMangledGlobalNames());
 
     globals.add(emitGetTypeFromName());
@@ -253,12 +266,24 @@
                            new js.ObjectInitializer(names));
   }
 
-  List<js.Property> emitLoadUrisAndHashes(Map<String, List<Fragment>> loadMap) {
-    js.ArrayInitializer outputUris(List<Fragment> fragments) {
+  js.Statement emitDeferredInitializerGlobal(Map loadMap) {
+    if (loadMap.isEmpty) return new js.Block.empty();
+
+    return js.js.statement("""
+  if (typeof($deferredInitializersGlobal) === 'undefined')
+    var $deferredInitializersGlobal = Object.create(null);""");
+  }
+
+  Iterable<js.Property> emitEmbeddedGlobalsForDeferredLoading(
+      Map<String, List<Fragment>> loadMap) {
+
+    List<js.Property> globals = <js.Property>[];
+
+    js.ArrayInitializer fragmentUris(List<Fragment> fragments) {
       return js.stringArray(fragments.map((DeferredFragment fragment) =>
-          "${fragment.outputFileName}$deferredExtension"));
+          "${fragment.outputFileName}.$deferredExtension"));
     }
-    js.ArrayInitializer outputHashes(List<Fragment> fragments) {
+    js.ArrayInitializer fragmentHashes(List<Fragment> fragments) {
       // TODO(floitsch): the hash must depend on the generated code.
       return js.numArray(
           fragments.map((DeferredFragment fragment) => fragment.hashCode));
@@ -269,38 +294,41 @@
     int count = 0;
     loadMap.forEach((String loadId, List<Fragment> fragmentList) {
       uris[count] =
-          new js.Property(js.string(loadId), outputUris(fragmentList));
+          new js.Property(js.string(loadId), fragmentUris(fragmentList));
       hashes[count] =
-          new js.Property(js.string(loadId), outputHashes(fragmentList));
+          new js.Property(js.string(loadId), fragmentHashes(fragmentList));
       count++;
     });
 
-    return <js.Property>[
-         new js.Property(js.string(DEFERRED_LIBRARY_URIS),
-                         new js.ObjectInitializer(uris)),
-         new js.Property(js.string(DEFERRED_LIBRARY_HASHES),
-                         new js.ObjectInitializer(hashes))
-         ];
-  }
+    globals.add(new js.Property(js.string(DEFERRED_LIBRARY_URIS),
+                                new js.ObjectInitializer(uris)));
+    globals.add(new js.Property(js.string(DEFERRED_LIBRARY_HASHES),
+                                new js.ObjectInitializer(hashes)));
 
-  js.Statement emitDeferredInitializerGlobal(Map loadMap) {
-    if (loadMap.isEmpty) return new js.Block.empty();
-
-    return js.js.statement("""
-  if (typeof($deferredInitializersGlobal) === 'undefined')
-    var $deferredInitializersGlobal = Object.create(null);""");
-  }
-
-  js.Property emitIsHunkLoadedFunction() {
-    js.Expression function =
+    js.Expression isHunkLoadedFunction =
         js.js("function(hash) { return !!$deferredInitializersGlobal[hash]; }");
-    return new js.Property(js.string(IS_HUNK_LOADED), function);
-  }
+    globals.add(new js.Property(js.string(IS_HUNK_LOADED),
+                                isHunkLoadedFunction));
 
-  js.Property emitInitializeLoadedHunk() {
-    js.Expression function =
-        js.js("function(hash) { eval($deferredInitializersGlobal[hash]); }");
-    return new js.Property(js.string(INITIALIZE_LOADED_HUNK), function);
+    js.Expression isHunkInitializedFunction =
+        js.js("function(hash) { return false; }");
+    globals.add(new js.Property(js.string(IS_HUNK_INITIALIZED),
+                                isHunkInitializedFunction));
+
+    /// See [emitEmbeddedGlobalsForDeferredLoading] for the format of the
+    /// deferred hunk.
+    js.Expression initializeLoadedHunkFunction =
+        js.js("""
+          function(hash) {
+            var hunk = $deferredInitializersGlobal[hash];
+            $setupProgramName(hunk[0]);
+            eval(hunk[1]);
+          }""");
+
+    globals.add(new js.Property(js.string(INITIALIZE_LOADED_HUNK),
+                                initializeLoadedHunkFunction));
+
+    return globals;
   }
 
   js.Property emitGetTypeFromName() {
@@ -323,14 +351,30 @@
                                      List<Holder> holders) {
     // TODO(floitsch): initialize eager classes.
     // TODO(floitsch): the hash must depend on the output.
-    int hash = this.hashCode;
-    if (fragment.constants.isNotEmpty) {
-      throw new UnimplementedError("constants in deferred units");
-    }
-    js.ArrayInitializer content =
-        new js.ArrayInitializer(fragment.libraries.map(emitLibrary)
-                                                  .toList(growable: false));
-    return js.js("$deferredInitializersGlobal[$hash] = #", content);
+    int hash = fragment.hashCode;
+
+    List<js.Expression> deferredCode =
+        fragment.libraries.map(emitLibrary).toList();
+
+    deferredCode.add(
+        emitLazilyInitializedStatics(fragment.staticLazilyInitializedFields));
+
+    js.ArrayInitializer deferredArray = new js.ArrayInitializer(deferredCode);
+
+    // This is the code that must be evaluated after all deferred classes have
+    // been setup.
+    js.Statement immediateCode = js.js.statement('''{
+          #constants;
+          #eagerClasses;
+        }''',
+        {'constants': emitConstants(fragment.constants),
+         'eagerClasses': emitEagerClassInitializations(fragment.libraries)});
+
+    js.LiteralString immediateString = unparse(compiler, immediateCode);
+    js.ArrayInitializer hunk =
+        new js.ArrayInitializer([deferredArray, immediateString]);
+
+    return js.js("$deferredInitializersGlobal[$hash] = #", hunk);
   }
 
   js.Block emitConstants(List<Constant> constants) {
@@ -466,7 +510,9 @@
   // This string should be referenced wherever JavaScript code makes assumptions
   // on the mixin format.
   static final String mixinFormatDescription =
-      "Mixins have no constructor, but a reference to their mixin class.";
+      "Mixins have a reference to their mixin class at the place of the usual"
+      "constructor. If they are instantiated the constructor follows the"
+      "reference.";
 
   js.Expression emitClass(Class cls) {
     List elements = [js.string(cls.superclassName),
@@ -476,17 +522,21 @@
       MixinApplication mixin = cls;
       elements.add(js.string(mixin.mixinClass.name));
       elements.add(js.number(mixin.mixinClass.holder.index));
+      if (cls.isDirectlyInstantiated) {
+        elements.add(_generateConstructor(cls));
+      }
     } else {
       elements.add(_generateConstructor(cls));
     }
     Iterable<Method> methods = cls.methods;
     Iterable<Method> isChecks = cls.isChecks;
     Iterable<Method> callStubs = cls.callStubs;
+    Iterable<Method> typeVariableReaderStubs = cls.typeVariableReaderStubs;
     Iterable<Method> noSuchMethodStubs = cls.noSuchMethodStubs;
     Iterable<Method> gettersSetters = _generateGettersSetters(cls);
     Iterable<Method> allMethods =
-        [methods, isChecks, callStubs, noSuchMethodStubs, gettersSetters]
-            .expand((x) => x);
+        [methods, isChecks, callStubs, typeVariableReaderStubs,
+         noSuchMethodStubs, gettersSetters].expand((x) => x);
     elements.addAll(allMethods.expand(emitInstanceMethod));
 
     return unparse(compiler, new js.ArrayInitializer(elements));
@@ -503,12 +553,12 @@
   /// facilitate the generation of tearOffs at runtime. The format is an array
   /// with the following fields:
   ///
+  /// [InstanceMethod.aliasName] (optional).
   /// [Method.code]
   /// [DartMethod.callName]
-  /// [DartMethod.tearOffName]
-  /// [JavaScriptBackend.isInterceptedMethod]
-  /// functionType
-  /// [InstanceMethod.aliasName]
+  /// isInterceptedMethod (optional, present if [DartMethod.needsTearOff]).
+  /// [DartMethod.tearOffName] (optional, present if [DartMethod.needsTearOff]).
+  /// functionType (optional, present if [DartMethod.needsTearOff]).
   ///
   /// followed by
   ///
@@ -520,25 +570,40 @@
   static final String parseFunctionDescriptorBoilerplate = r"""
 function parseFunctionDescriptor(proto, name, descriptor) {
   if (descriptor instanceof Array) {
-    proto[name] = descriptor[0];
-    var funs = [descriptor[0]];
-    funs[0].$callName = descriptor[1];
-    for (var pos = 6; pos < descriptor.length; pos += 3) {
+    // 'pos' points to the last read entry.
+    var f, pos = -1;
+    var aliasOrFunction = descriptor[++pos];
+    if (typeof aliasOrFunction == "string") {
+      // Install the alias for super calls on the prototype chain.
+      proto[aliasOrFunction] = f = descriptor[++pos];
+    } else {
+      f = aliasOrFunction;
+    }
+
+    proto[name] = f;
+    var funs = [f];
+    f.$callName = descriptor[++pos];
+
+    var isInterceptedOrParameterStubName = descriptor[pos + 1];
+    var isIntercepted, tearOffName, reflectionInfo;
+    if (typeof isInterceptedOrParameterStubName == "boolean") {
+      isIntercepted = descriptor[++pos];
+      tearOffName = descriptor[++pos];
+      reflectionInfo = descriptor[++pos];
+    }
+
+    for (++pos; pos < descriptor.length; pos += 3) {
       var stub = descriptor[pos + 2];
       stub.$callName = descriptor[pos + 1];
       proto[descriptor[pos]] = stub;
       funs.push(stub);
     }
-    if (descriptor[2] != null) {
-      var isIntercepted = descriptor[3];
-      var reflectionInfo = descriptor[4];
-      proto[descriptor[2]] = 
+
+    if (tearOffName) {
+      proto[tearOffName] =
           tearOff(funs, reflectionInfo, false, name, isIntercepted);
     }
-    // Install the alias for super calls on the prototype chain.
-    if (descriptor[5] != null) {
-      proto[descriptor[5]] = descriptor[0];
-    }
+
   } else {
     proto[name] = descriptor;
   }
@@ -571,19 +636,22 @@
       if (method.needsTearOff || method.aliasName != null) {
         /// See [parseFunctionDescriptorBoilerplate] for a full description of
         /// the format.
-        // [name, [function, callName, tearOffName, isIntercepted, functionType,
-        //     aliasName, stub1_name, stub1_callName, stub1_code, ...]
-        bool isIntercepted = backend.isInterceptedMethod(method.element);
-        var data = [method.code];
-        data.add(js.string(method.callName));
-        data.add(js.string(method.tearOffName));
-        data.add(new js.LiteralBool(isIntercepted));
-        data.add(_generateFunctionType(method.type));
+        // [name, [aliasName, function, callName, isIntercepted, tearOffName,
+        // functionType, stub1_name, stub1_callName, stub1_code, ...]
+        var data = [];
         if (method.aliasName != null) {
           data.add(js.string(method.aliasName));
-        } else {
-          data.add(new js.LiteralNull());
         }
+        data.add(method.code);
+        data.add(js.string(method.callName));
+
+        if (method.needsTearOff) {
+          bool isIntercepted = backend.isInterceptedMethod(method.element);
+          data.add(new js.LiteralBool(isIntercepted));
+          data.add(js.string(method.tearOffName));
+          data.add(_generateFunctionType(method.type));
+        }
+
         data.addAll(method.parameterStubs.expand(makeNameCallNameCodeTriplet));
         return [js.string(method.name), new js.ArrayInitializer(data)];
       } else {
@@ -637,6 +705,8 @@
     return output;
   }
 
+  static final String setupProgramName = "setupProgram";
+
   static final String boilerplate = """
 {
 // Declare deferred-initializer global.
@@ -650,7 +720,7 @@
   // Counter to generate unique names for tear offs.
   var functionCounter = 0;
 
-  function setupProgram() {
+  function $setupProgramName(program) {
     for (var i = 0; i < program.length - 1; i++) {
       setupLibrary(program[i]);
     }
@@ -800,18 +870,28 @@
     descriptor = compile(name, descriptor);
     var prototype = determinePrototype(descriptor);
     var constructor;
+    var functionsIndex;
     // $mixinFormatDescription.
     if (typeof descriptor[2] !== 'function') {
-      constructor = compileMixinConstructor(name, prototype, descriptor);
-      for (var i = 4; i < descriptor.length; i += 2) {
-        parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1]);
+      fillPrototypeWithMixedIn(descriptor[2], descriptor[3], prototype);
+      // descriptor[4] contains the constructor if the mixin application is
+      // directly instantiated.
+      if (typeof descriptor[4] === 'function') {
+        constructor = descriptor[4];
+        functionsIndex = 5;
+      } else {
+        constructor = function() {};
+        functionsIndex = 4;
       }
     } else {
       constructor = descriptor[2];
-      for (var i = 3; i < descriptor.length; i += 2) {
-        parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1]);
-      }
+      functionsIndex = 3;
     }
+
+    for (var i = functionsIndex; i < descriptor.length; i += 2) {
+      parseFunctionDescriptor(prototype, descriptor[i], descriptor[i + 1]);
+    }
+
     constructor.builtin\$cls = name;  // Needed for RTI.
     constructor.prototype = prototype;
     prototype[#operatorIsPrefix + name] = constructor;
@@ -819,10 +899,7 @@
     return constructor;
   }
 
-  function compileMixinConstructor(name, prototype, descriptor) {
-    // $mixinFormatDescription.
-    var mixinName = descriptor[2];
-    var mixinHolderIndex = descriptor[3];
+  function fillPrototypeWithMixedIn(mixinName, mixinHolderIndex, prototype) {
     var mixin = holders[mixinHolderIndex][mixinName].ensureResolved();
     var mixinPrototype = mixin.prototype;
 
@@ -832,10 +909,6 @@
       var p = mixinProperties[i];
       prototype[p] = mixinPrototype[p];
     }
-    // Since this is a mixin application the constructor will actually never
-    // be invoked. We only use its prototype for the application's subclasses. 
-    var constructor = function() {};
-    return constructor;
   }
 
   function determinePrototype(descriptor) {
@@ -880,7 +953,7 @@
     }
   }
 
-  setupProgram();
+  $setupProgramName(program);
 
   // Initialize constants.
   #constants;
@@ -905,9 +978,9 @@
   #eagerClasses;
 
   var end = Date.now();
-  print('Setup: ' + (end - start) + ' ms.');
+  // print('Setup: ' + (end - start) + ' ms.');
 
-  #main();  // Start main.
+  #invokeMain;  // Start main.
 
 }(Date.now(), #code)
 }""";
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/class_builder.dart b/pkg/compiler/lib/src/js_emitter/old_emitter/class_builder.dart
index 2cc8254..3be27cf 100644
--- a/pkg/compiler/lib/src/js_emitter/old_emitter/class_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/old_emitter/class_builder.dart
@@ -20,7 +20,6 @@
 
   ClassBuilder(this.element, this.namer);
 
-  // Has the same signature as [DefineStubFunction].
   jsAst.Property addProperty(String name, jsAst.Expression value) {
     jsAst.Property property = new jsAst.Property(js.string(name), value);
     properties.add(property);
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart b/pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart
index 216b7d0..0193ba2 100644
--- a/pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/old_emitter/class_emitter.dart
@@ -39,7 +39,8 @@
     emitCheckedClassSetters(cls, builder);
     emitClassGettersSettersForCSP(cls, builder);
     emitInstanceMembers(cls, builder);
-    emitCallStubs(cls, builder);
+    emitStubs(cls.callStubs, builder);
+    emitStubs(cls.typeVariableReaderStubs, builder);
     emitRuntimeTypeInformation(cls, builder);
     emitNativeInfo(cls, builder);
 
@@ -51,8 +52,6 @@
       builder.addProperty(namer.getterNameFromAccessorName(name), function);
     }
 
-    emitTypeVariableReaders(classElement, builder);
-
     emitClassBuilderWithReflectionData(cls, builder, enclosingBuilder);
   }
   /**
@@ -213,8 +212,8 @@
     }
   }
 
-  void emitCallStubs(Class cls, ClassBuilder builder) {
-    for (Method method in cls.callStubs) {
+  void emitStubs(Iterable<StubMethod> stubs, ClassBuilder builder) {
+    for (Method method in stubs) {
       jsAst.Property property = builder.addProperty(method.name, method.code);
       compiler.dumpInfoTask.registerElementAst(method.element, property);
     }
@@ -553,45 +552,4 @@
       builder.addProperty('+$reflectionName', reflectable);
     }
   }
-
-  void emitTypeVariableReaders(ClassElement cls, ClassBuilder builder) {
-    List typeVariables = [];
-    ClassElement superclass = cls;
-    while (superclass != null) {
-      for (TypeVariableType parameter in superclass.typeVariables) {
-        if (backend.emitter.readTypeVariables.contains(parameter.element)) {
-          emitTypeVariableReader(cls, builder, parameter.element);
-        }
-      }
-      superclass = superclass.superclass;
-    }
-  }
-
-  void emitTypeVariableReader(ClassElement cls,
-                              ClassBuilder builder,
-                              TypeVariableElement element) {
-    String name = namer.readTypeVariableName(element);
-    int index = RuntimeTypes.getTypeVariableIndex(element);
-    jsAst.Expression computeTypeVariable;
-
-    Substitution substitution =
-        backend.rti.computeSubstitution(
-            cls, element.typeDeclaration, alwaysGenerateFunction: true);
-    if (substitution != null) {
-      computeTypeVariable =
-          js(r'#.apply(null, this.$builtinTypeInfo)',
-             substitution.getCodeForVariable(index, backend.rti));
-    } else {
-      // TODO(ahe): These can be generated dynamically.
-      computeTypeVariable =
-          js(r'this.$builtinTypeInfo && this.$builtinTypeInfo[#]',
-              js.number(index));
-    }
-    jsAst.Expression convertRtiToRuntimeType = emitter
-        .staticFunctionAccess(backend.findHelper('convertRtiToRuntimeType'));
-    compiler.dumpInfoTask.registerElementAst(element,
-        builder.addProperty(name,
-            js('function () { return #(#) }',
-                [convertRtiToRuntimeType, computeTypeVariable])));
-  }
 }
diff --git a/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
index 25d4b43..a575e1c 100644
--- a/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/old_emitter/emitter.dart
@@ -366,6 +366,12 @@
     }
 
     if (hasIsolateSupport) {
+      jsAst.Expression createNewIsolateFunctionAccess =
+          generateEmbeddedGlobalAccess(embeddedNames.CREATE_NEW_ISOLATE);
+      var createIsolateAssignment =
+          js('# = function() { return new ${namer.isolateName}(); }',
+             createNewIsolateFunctionAccess);
+
       jsAst.Expression classIdExtractorAccess =
           generateEmbeddedGlobalAccess(embeddedNames.CLASS_ID_EXTRACTOR);
       var classIdExtractorAssignment =
@@ -402,7 +408,8 @@
         return o;
       }''', [ initializeEmptyInstanceAccess, allClassesAccess ]);
 
-      result.addAll([classIdExtractorAssignment,
+      result.addAll([createIsolateAssignment,
+                     classIdExtractorAssignment,
                      classFieldsExtractorAssignment,
                      instanceFromClassIdAssignment,
                      initializeEmptyInstanceAssignment]);
@@ -861,8 +868,8 @@
         cachedEmittedConstants.add(constantValue);
       }
       jsAst.Expression init = buildConstantInitializer(constantValue);
-      constantOutput.addBuffer(jsAst.prettyPrint(init, compiler,
-                                         monitor: compiler.dumpInfoTask));
+      constantOutput.addBuffer(
+          jsAst.prettyPrint(init, compiler, monitor: compiler.dumpInfoTask));
       constantOutput.add('$N');
     }
     if (compiler.hasIncrementalSupport && isMainFragment) {
@@ -914,32 +921,8 @@
                       backend.rti.getFunctionThatReturnsNullName]);
   }
 
-  /// Returns the code equivalent to:
-  ///   `function(args) { $.startRootIsolate(X.main$closure(), args); }`
-  jsAst.Expression buildIsolateSetupClosure(Element appMain,
-                                            Element isolateMain) {
-    jsAst.Expression mainAccess = isolateStaticClosureAccess(appMain);
-    // Since we pass the closurized version of the main method to
-    // the isolate method, we must make sure that it exists.
-    return js('function(a){ #(#, a); }',
-        [backend.emitter.staticFunctionAccess(isolateMain), mainAccess]);
-  }
-
-  emitMain(CodeOutput output) {
+  emitMain(CodeOutput output, jsAst.Statement invokeMain) {
     if (compiler.isMockCompilation) return;
-    Element main = compiler.mainFunction;
-    jsAst.Expression mainCallClosure = null;
-    if (compiler.hasIsolateSupport) {
-      Element isolateMain =
-        backend.isolateHelperLibrary.find(JavaScriptBackend.START_ROOT_ISOLATE);
-      mainCallClosure = buildIsolateSetupClosure(main, isolateMain);
-    } else if (compiler.hasIncrementalSupport) {
-      mainCallClosure = js(
-          'function() { return #(); }',
-          backend.emitter.staticFunctionAccess(main));
-    } else {
-      mainCallClosure = backend.emitter.staticFunctionAccess(main);
-    }
 
     if (NativeGenerator.needsIsolateAffinityTagInitialization(backend)) {
       jsAst.Statement nativeBoilerPlate =
@@ -951,47 +934,8 @@
           nativeBoilerPlate, compiler, monitor: compiler.dumpInfoTask));
     }
 
-    jsAst.Expression currentScriptAccess =
-        generateEmbeddedGlobalAccess(embeddedNames.CURRENT_SCRIPT);
-
-    addComment('BEGIN invoke [main].', output);
-    // This code finds the currently executing script by listening to the
-    // onload event of all script tags and getting the first script which
-    // finishes. Since onload is called immediately after execution this should
-    // not substantially change execution order.
-    jsAst.Statement invokeMain = js.statement('''
-(function (callback) {
-  if (typeof document === "undefined") {
-    callback(null);
-    return;
-  }
-  if (document.currentScript) {
-    callback(document.currentScript);
-    return;
-  }
-
-  var scripts = document.scripts;
-  function onLoad(event) {
-    for (var i = 0; i < scripts.length; ++i) {
-      scripts[i].removeEventListener("load", onLoad, false);
-    }
-    callback(event.target);
-  }
-  for (var i = 0; i < scripts.length; ++i) {
-    scripts[i].addEventListener("load", onLoad, false);
-  }
-})(function(currentScript) {
-  #currentScript = currentScript;
-
-  if (typeof dartMainRunner === "function") {
-    dartMainRunner(#mainCallClosure, []);
-  } else {
-    #mainCallClosure([]);
-  }
-})''', {'currentScript': currentScriptAccess,
-        'mainCallClosure': mainCallClosure});
-
     output.add(';');
+    addComment('BEGIN invoke [main].', output);
     output.addBuffer(jsAst.prettyPrint(invokeMain,
                      compiler, monitor: compiler.dumpInfoTask));
     output.add(N);
@@ -1358,7 +1302,7 @@
 
   void emitMainOutputUnit(Program program,
                           Map<OutputUnit, String> deferredLoadHashes) {
-    Fragment mainFragment = program.fragments.first;
+    MainFragment mainFragment = program.fragments.first;
     OutputUnit mainOutputUnit = mainFragment.outputUnit;
 
     LineColumnCollector lineColumnCollector;
@@ -1572,7 +1516,7 @@
     jsAst.FunctionDeclaration precompiledFunctionAst =
         buildCspPrecompiledFunctionFor(mainOutputUnit);
     emitInitFunction(mainOutput);
-    emitMain(mainOutput);
+    emitMain(mainOutput, mainFragment.invokeMain);
     mainOutput.add('})()\n');
 
     if (compiler.useContentSecurityPolicy) {
diff --git a/pkg/compiler/lib/src/js_emitter/program_builder.dart b/pkg/compiler/lib/src/js_emitter/program_builder.dart
index a6d6fe8..7036bb7 100644
--- a/pkg/compiler/lib/src/js_emitter/program_builder.dart
+++ b/pkg/compiler/lib/src/js_emitter/program_builder.dart
@@ -19,8 +19,9 @@
     ClassStubGenerator,
     CodeEmitterTask,
     InterceptorStubGenerator,
+    MainCallStubGenerator,
     ParameterStubGenerator,
-    TypeTestGenerator,
+    RuntimeTypeGenerator,
     TypeTestProperties;
 
 import '../universe/universe.dart' show Universe;
@@ -122,7 +123,8 @@
         _buildTypeToInterceptorMap(),
         _task.metadataCollector,
         outputContainsNativeClasses: containsNativeClasses,
-        outputContainsConstantList: _task.outputContainsConstantList);
+        outputContainsConstantList: _task.outputContainsConstantList,
+        hasIsolateSupport: _compiler.hasIsolateSupport);
   }
 
   void _markEagerClasses() {
@@ -152,7 +154,7 @@
     MainFragment result = new MainFragment(
         librariesMap.outputUnit,
         "",  // The empty string is the name for the main output file.
-        backend.emitter.staticFunctionAccess(_compiler.mainFunction),
+        _buildInvokeMain(),
         _buildLibraries(librariesMap),
         _buildStaticNonFinalFields(librariesMap),
         _buildStaticLazilyInitializedFields(librariesMap),
@@ -162,6 +164,12 @@
     return result;
   }
 
+  js.Statement _buildInvokeMain() {
+    MainCallStubGenerator generator =
+        new MainCallStubGenerator(_compiler, backend, backend.emitter);
+    return generator.generateInvokeMain();
+  }
+
   DeferredFragment _buildDeferredOutput(MainFragment mainOutput,
                                       LibrariesMap librariesMap) {
     DeferredFragment result = new DeferredFragment(
@@ -290,7 +298,7 @@
     String name = namer.getNameOfClass(element);
 
     return new Class(
-        element, name, null, [], instanceFields, [], [], [], [], null,
+        element, name, null, [], instanceFields, [], [], [], [], [], null,
         isDirectlyInstantiated: true,
         onlyForRti: false,
         isNative: element.isNative);
@@ -304,6 +312,8 @@
 
     ClassStubGenerator classStubGenerator =
         new ClassStubGenerator(_compiler, namer, backend);
+    RuntimeTypeGenerator runtimeTypeGenerator =
+        new RuntimeTypeGenerator(_compiler, _task, namer);
 
     void visitMember(ClassElement enclosing, Element member) {
       assert(invariant(element, member.isDeclaration));
@@ -329,6 +339,9 @@
       }
     }
 
+    List<StubMethod> typeVariableReaderStubs =
+        runtimeTypeGenerator.generateTypeVariableReaderStubs(element);
+
     List<StubMethod> noSuchMethodStubs = <StubMethod>[];
     if (element == _compiler.objectClass) {
       Map<String, Selector> selectors =
@@ -353,10 +366,8 @@
     List<Field> staticFieldsForReflection =
         onlyForRti ? const <Field>[] : _buildFields(element, true);
 
-    TypeTestGenerator generator =
-        new TypeTestGenerator(_compiler, _task, namer);
     TypeTestProperties typeTests =
-        generator.generateIsTests(
+        runtimeTypeGenerator.generateIsTests(
             element,
             storeFunctionTypeInMetadata: _storeFunctionTypesInMetadata);
 
@@ -381,6 +392,7 @@
                                     instanceFields,
                                     staticFieldsForReflection,
                                     callStubs,
+                                    typeVariableReaderStubs,
                                     isChecks,
                                     typeTests.functionTypeIndex,
                                     isDirectlyInstantiated: isInstantiated,
@@ -390,6 +402,7 @@
                          name, holder, methods, instanceFields,
                          staticFieldsForReflection,
                          callStubs,
+                         typeVariableReaderStubs,
                          noSuchMethodStubs,
                          isChecks,
                          typeTests.functionTypeIndex,
diff --git a/pkg/compiler/lib/src/js_emitter/type_test_generator.dart b/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
similarity index 84%
rename from pkg/compiler/lib/src/js_emitter/type_test_generator.dart
rename to pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
index bb2b0b9..9abed6d 100644
--- a/pkg/compiler/lib/src/js_emitter/type_test_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/runtime_type_generator.dart
@@ -20,12 +20,12 @@
   final Map<String, jsAst.Node> properties = <String, jsAst.Node>{};
 }
 
-class TypeTestGenerator {
+class RuntimeTypeGenerator {
   final Compiler compiler;
   final CodeEmitterTask emitterTask;
   final Namer namer;
 
-  TypeTestGenerator(this.compiler, this.emitterTask, this.namer);
+  RuntimeTypeGenerator(this.compiler, this.emitterTask, this.namer);
 
   JavaScriptBackend get backend => compiler.backend;
   TypeTestRegistry get typeTestRegistry => emitterTask.typeTestRegistry;
@@ -264,4 +264,48 @@
                                  generateSubstitution, alreadyGenerated);
     }
   }
+
+  List<StubMethod> generateTypeVariableReaderStubs(ClassElement classElement) {
+    List<StubMethod> stubs = <StubMethod>[];
+    List typeVariables = [];
+    ClassElement superclass = classElement;
+    while (superclass != null) {
+        for (TypeVariableType parameter in superclass.typeVariables) {
+          if (backend.emitter.readTypeVariables.contains(parameter.element)) {
+            stubs.add(
+                _generateTypeVariableReader(classElement, parameter.element));
+          }
+        }
+        superclass = superclass.superclass;
+      }
+
+    return stubs;
+  }
+
+  StubMethod _generateTypeVariableReader(ClassElement cls,
+                                         TypeVariableElement element) {
+    String name = namer.readTypeVariableName(element);
+    int index = RuntimeTypes.getTypeVariableIndex(element);
+    jsAst.Expression computeTypeVariable;
+
+    Substitution substitution =
+        backend.rti.computeSubstitution(
+            cls, element.typeDeclaration, alwaysGenerateFunction: true);
+    if (substitution != null) {
+      computeTypeVariable =
+          js(r'#.apply(null, this.$builtinTypeInfo)',
+             substitution.getCodeForVariable(index, backend.rti));
+    } else {
+      // TODO(ahe): These can be generated dynamically.
+      computeTypeVariable =
+          js(r'this.$builtinTypeInfo && this.$builtinTypeInfo[#]',
+              js.number(index));
+    }
+    jsAst.Expression convertRtiToRuntimeType = backend.emitter
+         .staticFunctionAccess(backend.findHelper('convertRtiToRuntimeType'));
+
+    return new StubMethod(name,
+                          js('function () { return #(#) }',
+                             [convertRtiToRuntimeType, computeTypeVariable]));
+  }
 }
diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart
index af2b302..bd92e97 100644
--- a/pkg/compiler/lib/src/resolution/members.dart
+++ b/pkg/compiler/lib/src/resolution/members.dart
@@ -492,15 +492,11 @@
   }
 
   static void processAsyncMarker(Compiler compiler,
-                                 BaseFunctionElementX element) {
+                                 BaseFunctionElementX element,
+                                 Registry registry) {
     FunctionExpression functionExpression = element.node;
     AsyncModifier asyncModifier = functionExpression.asyncModifier;
     if (asyncModifier != null) {
-      if (!compiler.enableAsyncAwait) {
-        compiler.reportError(asyncModifier,
-            MessageKind.EXPERIMENTAL_ASYNC_AWAIT,
-            {'modifier': element.asyncMarker});
-      }
 
       if (asyncModifier.isAsynchronous) {
         element.asyncMarker = asyncModifier.isYielding
@@ -530,6 +526,7 @@
               {'modifier': element.asyncMarker});
         }
       }
+      registry.registerAsyncMarker(element);
     }
   }
 
@@ -556,6 +553,7 @@
       ResolutionRegistry registry = visitor.registry;
       registry.defineFunction(tree, element);
       visitor.setupFunction(tree, element);
+      processAsyncMarker(compiler, element, registry);
 
       if (element.isGenerativeConstructor) {
         // Even if there is no initializer list we still have to do the
@@ -630,7 +628,6 @@
       } else {
         element.parseNode(compiler);
         element.computeType(compiler);
-        processAsyncMarker(compiler, element);
         FunctionElementX implementation = element;
         if (element.isExternal) {
           implementation = compiler.backend.resolveExternalFunction(element);
@@ -2478,7 +2475,7 @@
     LocalFunctionElementX function = new LocalFunctionElementX(
         name, node, ElementKind.FUNCTION, Modifiers.EMPTY,
         enclosingElement);
-    ResolverTask.processAsyncMarker(compiler, function);
+    ResolverTask.processAsyncMarker(compiler, function, registry);
     function.functionSignatureCache = SignatureResolver.analyze(
         compiler,
         node.parameters,
diff --git a/pkg/compiler/lib/src/resolution/registry.dart b/pkg/compiler/lib/src/resolution/registry.dart
index eeaa2a8..317efbb 100644
--- a/pkg/compiler/lib/src/resolution/registry.dart
+++ b/pkg/compiler/lib/src/resolution/registry.dart
@@ -367,4 +367,8 @@
   bool isAssert(Send node) {
     return mapping.isAssert(node);
   }
+
+  void registerAsyncMarker(FunctionElement element) {
+    backend.registerAsyncMarker(element, world, this);
+  }
 }
diff --git a/pkg/compiler/lib/src/scanner/parser.dart b/pkg/compiler/lib/src/scanner/parser.dart
index fb49196..00e599f 100644
--- a/pkg/compiler/lib/src/scanner/parser.dart
+++ b/pkg/compiler/lib/src/scanner/parser.dart
@@ -1512,15 +1512,6 @@
     return token;
   }
 
-  /// `async*` and `sync*` are parsed a two tokens, [token] and [star]. This
-  /// method checks that there is no whitespace between [token] and [star].
-  void checkStarredModifier(Token token, Token star, String name) {
-    if (star.charOffset > token.charOffset + token.charCount) {
-      listener.reportError(new TokenPair(token, star),
-          MessageKind.INVALID_STARRED_KEYWORD, {'keyword': name});
-    }
-  }
-
   Token parseAsyncModifier(Token token) {
     Token async;
     Token star;
@@ -1534,7 +1525,6 @@
         yieldIsKeyword = true;
         star = token;
         token = token.next;
-        checkStarredModifier(async, star, 'async*');
       }
     } else if (optional('sync', token)) {
       async = token;
@@ -1543,8 +1533,6 @@
         yieldIsKeyword = true;
         star = token;
         token = token.next;
-
-        checkStarredModifier(async, star, 'sync*');
       } else {
         listener.reportError(async,
             MessageKind.INVALID_SYNC_MODIFIER);
@@ -1617,7 +1605,6 @@
     if (optional('*', token)) {
       starToken = token;
       token = token.next;
-      checkStarredModifier(begin, starToken, 'yield*');
     }
     token = parseExpression(token);
     listener.endYieldStatement(begin, starToken, token);
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index 43798cd..13350ca 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -19,7 +19,54 @@
   js.Fun compile(CodegenWorkItem work) {
     HGraph graph = builder.build(work);
     optimizer.optimize(work, graph);
-    return generator.generateCode(work, graph);
+    Element element = work.element;
+    js.Expression result = generator.generateCode(work, graph);
+    if (element is FunctionElement) {
+      JavaScriptBackend backend = builder.backend;
+
+      AsyncRewriter rewriter = null;
+      if (element.asyncMarker == AsyncMarker.ASYNC) {
+        rewriter = new AsyncRewriter(
+            backend.compiler,
+            backend.compiler.currentElement,
+            thenHelper:
+                backend.emitter.staticFunctionAccess(backend.getThenHelper()),
+            newCompleter: backend.emitter.staticFunctionAccess(
+                backend.getCompleterConstructor()),
+            safeVariableName: backend.namer.safeVariableName);
+      } else if (element.asyncMarker == AsyncMarker.SYNC_STAR) {
+        rewriter = new AsyncRewriter(
+            backend.compiler,
+            backend.compiler.currentElement,
+            endOfIteration: backend.emitter.staticFunctionAccess(
+                backend.getEndOfIteration()),
+            newIterable: backend.emitter.staticFunctionAccess(
+                backend.getSyncStarIterableConstructor()),
+            yieldStarExpression: backend.emitter.staticFunctionAccess(
+                backend.getYieldStar()),
+            safeVariableName: backend.namer.safeVariableName);
+      }
+      else if (element.asyncMarker == AsyncMarker.ASYNC_STAR) {
+        rewriter = new AsyncRewriter(
+            backend.compiler,
+            backend.compiler.currentElement,
+            streamHelper: backend.emitter.staticFunctionAccess(
+                backend.getStreamHelper()),
+            streamOfController: backend.emitter.staticFunctionAccess(
+                backend.getStreamOfController()),
+            newController: backend.emitter.staticFunctionAccess(
+                backend.getASyncStarControllerConstructor()),
+            safeVariableName: backend.namer.safeVariableName,
+            yieldExpression: backend.emitter.staticFunctionAccess(
+                backend.getYieldSingle()),
+            yieldStarExpression: backend.emitter.staticFunctionAccess(
+                backend.getYieldStar()));
+      }
+      if (rewriter != null) {
+        result = rewriter.rewrite(result);
+      }
+    }
+    return result;
   }
 
   Iterable<CompilerTask> get tasks {
@@ -362,7 +409,7 @@
   bool isAccessedDirectly(Local local) {
     assert(local != null);
     return !redirectionMapping.containsKey(local)
-        && !closureData.usedVariablesInTry.contains(local);
+        && !closureData.variablesUsedInTryOrGenerator.contains(local);
   }
 
   bool isStoredInClosureField(Local local) {
@@ -379,8 +426,8 @@
     return redirectionMapping.containsKey(local);
   }
 
-  bool isUsedInTry(Local local) {
-    return closureData.usedVariablesInTry.contains(local);
+  bool isUsedInTryOrGenerator(Local local) {
+    return closureData.variablesUsedInTryOrGenerator.contains(local);
   }
 
   /**
@@ -422,7 +469,7 @@
       builder.add(lookup);
       return lookup;
     } else {
-      assert(isUsedInTry(local));
+      assert(isUsedInTryOrGenerator(local));
       HLocalValue localValue = getLocal(local);
       HInstruction instruction = new HLocalGet(
           local, localValue, builder.backend.dynamicType);
@@ -478,7 +525,7 @@
       HInstruction box = readLocal(redirect.box);
       builder.add(new HFieldSet(redirect, box, value));
     } else {
-      assert(isUsedInTry(local));
+      assert(isUsedInTryOrGenerator(local));
       HLocalValue localValue = getLocal(local);
       builder.add(new HLocalSet(local, localValue, value));
     }
@@ -1916,10 +1963,10 @@
         }
         Selector selector = new Selector.callDefaultConstructor();
         List<HInstruction> arguments =
-            selector.makeArgumentsList2(const Link<ast.Node>(),
-                                        target.implementation,
-                                        null,
-                                        handleConstantForOptionalParameter);
+            selector.makeArgumentsList(const Link<ast.Node>(),
+                                       target.implementation,
+                                       null,
+                                       handleConstantForOptionalParameter);
         inlineSuperOrRedirect(target,
                               arguments,
                               constructors,
@@ -2446,11 +2493,21 @@
 
   HInstruction attachPosition(HInstruction target, ast.Node node) {
     if (generateSourceMap && node != null) {
-      target.sourcePosition = sourceFileLocationForBeginToken(node);
+      target.sourceInformation = sourceInformationForBeginToken(node);
     }
     return target;
   }
 
+  SourceInformation sourceInformationForBeginToken(ast.Node node) {
+    return new StartEndSourceInformation(sourceFileLocationForBeginToken(node));
+  }
+
+  SourceInformation sourceInformationForBeginEndToken(ast.Node node) {
+    return new StartEndSourceInformation(
+        sourceFileLocationForBeginToken(node),
+        sourceFileLocationForEndToken(node));
+  }
+
   SourceFileLocation sourceFileLocationForBeginToken(ast.Node node) =>
       sourceFileLocationForToken(node, node.getBeginToken());
 
@@ -2734,8 +2791,7 @@
               wrapExpressionGraph(updateGraph),
               conditionBlock.loopInformation.target,
               conditionBlock.loopInformation.labels,
-              sourceFileLocationForBeginToken(loop),
-              sourceFileLocationForEndToken(loop));
+              sourceInformationForBeginEndToken(loop));
 
       startBlock.setBlockFlow(info, current);
       loopInfo.loopBlockInformation = info;
@@ -2952,8 +3008,7 @@
               null,
               loopEntryBlock.loopInformation.target,
               loopEntryBlock.loopInformation.labels,
-              sourceFileLocationForBeginToken(node),
-              sourceFileLocationForEndToken(node));
+              sourceInformationForBeginEndToken(node));
       loopEntryBlock.setBlockFlow(loopBlockInfo, current);
       loopInfo.loopBlockInformation = loopBlockInfo;
     } else {
@@ -3504,10 +3559,10 @@
       return pop();
     }
 
-    return selector.makeArgumentsList2(arguments,
-                                       element,
-                                       compileArgument,
-                                       handleConstantForOptionalParameter);
+    return selector.makeArgumentsList(arguments,
+                                      element,
+                                      compileArgument,
+                                      handleConstantForOptionalParameter);
   }
 
   void addGenericSendArgumentsToList(Link<ast.Node> link, List<HInstruction> list) {
@@ -3831,16 +3886,6 @@
                       effects: sideEffects));
   }
 
-  void handleForeignCreateIsolate(ast.Send node) {
-    if (!node.arguments.isEmpty) {
-      compiler.internalError(node.argumentsNode, 'Too many arguments.');
-    }
-    String constructorName = backend.namer.isolateName;
-    push(new HForeign(js.js.parseForeignJS("new $constructorName()"),
-                      backend.dynamicType,
-                      <HInstruction>[]));
-  }
-
   void handleForeignDartObjectJsConstructorFunction(ast.Send node) {
     if (!node.arguments.isEmpty) {
       compiler.internalError(node.argumentsNode, 'Too many arguments.');
@@ -3875,8 +3920,6 @@
       handleForeignRawFunctionRef(node, 'RAW_DART_FUNCTION_REF');
     } else if (name == 'JS_SET_CURRENT_ISOLATE') {
       handleForeignSetCurrentIsolate(node);
-    } else if (name == 'JS_CREATE_ISOLATE') {
-      handleForeignCreateIsolate(node);
     } else if (name == 'JS_OPERATOR_IS_PREFIX') {
       // TODO(floitsch): this should be a JS_NAME.
       stack.add(addConstantString(backend.namer.operatorIsPrefix));
@@ -5164,14 +5207,17 @@
   }
 
   visitYield(ast.Yield node) {
-    // Dummy implementation.
     visit(node.expression);
-    pop();
+    HInstruction yielded = pop();
+    add(new HYield(yielded, node.hasStar));
   }
 
   visitAwait(ast.Await node) {
-    // Dummy implementation.
     visit(node.expression);
+    HInstruction awaited = pop();
+    // TODO(herhut): Improve this type.
+    push(new HAwait(awaited, new TypeMask.subclass(compiler.objectClass,
+                                                   compiler.world)));
   }
 
   visitTypeAnnotation(ast.TypeAnnotation node) {
@@ -5315,7 +5361,78 @@
     return new JumpHandler(this, element);
   }
 
+  buildAsyncForIn(ast.ForIn node) {
+    assert(node.isAsync);
+    // The async-for is implemented with a StreamIterator.
+    HInstruction streamIterator;
+
+    visit(node.expression);
+    HInstruction expression = pop();
+    pushInvokeStatic(node,
+                     backend.getStreamIteratorConstructor(),
+                     [expression, graph.addConstantNull(compiler)]);
+    streamIterator = pop();
+
+    void buildInitializer() {}
+
+    HInstruction buildCondition() {
+      Selector selector = elements.getMoveNextSelector(node);
+      pushInvokeDynamic(node, selector, [streamIterator]);
+      HInstruction future = pop();
+      push(new HAwait(future, new TypeMask.subclass(compiler.objectClass,
+                                                    compiler.world)));
+      return popBoolified();
+    }
+    void buildBody() {
+      Selector call = elements.getCurrentSelector(node);
+      pushInvokeDynamic(node, call, [streamIterator]);
+
+      ast.Node identifier = node.declaredIdentifier;
+      Element variable = elements.getForInVariable(node);
+      Selector selector = elements.getSelector(identifier);
+
+      HInstruction value = pop();
+      if (identifier.asSend() != null
+          && Elements.isInstanceSend(identifier, elements)) {
+        HInstruction receiver = generateInstanceSendReceiver(identifier);
+        assert(receiver != null);
+        generateInstanceSetterWithCompiledReceiver(
+            null,
+            receiver,
+            value,
+            selector: selector,
+            location: identifier);
+      } else {
+        generateNonInstanceSetter(
+            null, variable, value, location: identifier);
+      }
+      pop(); // Pop the value pushed by the setter call.
+
+      visit(node.body);
+    }
+
+    void buildUpdate() {};
+
+    buildProtectedByFinally(() {
+      handleLoop(node,
+                 buildInitializer,
+                 buildCondition,
+                 buildUpdate,
+                 buildBody);
+    }, () {
+      pushInvokeDynamic(node, new Selector.call("cancel", null, 0),
+          [streamIterator]);
+      push(new HAwait(pop(), new TypeMask.subclass(compiler.objectClass,
+          compiler.world)));
+      pop();
+    });
+  }
+
   visitForIn(ast.ForIn node) {
+    if (node.isAsync) {
+      return buildAsyncForIn(node);
+    }
+
     // Generate a structure equivalent to:
     //   Iterator<E> $iter = <iterable>.iterator;
     //   while ($iter.moveNext()) {
@@ -5851,6 +5968,85 @@
     compiler.internalError(node, 'SsaFromAstMixin.visitCaseMatch.');
   }
 
+  /// Calls [buildTry] inside a synthetic try block with [buildFinally] in the
+  /// finally block.
+  void buildProtectedByFinally(void buildTry(), void buildFinally()) {
+    HBasicBlock enterBlock = openNewBlock();
+    HTry tryInstruction = new HTry();
+    close(tryInstruction);
+    bool oldInTryStatement = inTryStatement;
+    inTryStatement = true;
+
+    HBasicBlock startTryBlock;
+    HBasicBlock endTryBlock;
+    HBasicBlock startFinallyBlock;
+    HBasicBlock endFinallyBlock;
+
+    startTryBlock = graph.addNewBlock();
+    open(startTryBlock);
+    buildTry();
+    // We use a [HExitTry] instead of a [HGoto] for the try block
+    // because it will have two successors: the join block, and
+    // the finally block.
+    if (!isAborted()) endTryBlock = close(new HExitTry());
+    SubGraph bodyGraph = new SubGraph(startTryBlock, lastOpenedBlock);
+
+    SubGraph finallyGraph = null;
+
+    startFinallyBlock = graph.addNewBlock();
+    open(startFinallyBlock);
+    buildFinally();
+    if (!isAborted()) endFinallyBlock = close(new HGoto());
+    tryInstruction.finallyBlock = startFinallyBlock;
+    finallyGraph = new SubGraph(startFinallyBlock, lastOpenedBlock);
+
+    HBasicBlock exitBlock = graph.addNewBlock();
+
+    void addExitTrySuccessor(HBasicBlock successor) {
+      // Iterate over all blocks created inside this try/catch, and
+      // attach successor information to blocks that end with
+      // [HExitTry].
+      for (int i = startTryBlock.id; i < successor.id; i++) {
+        HBasicBlock block = graph.blocks[i];
+        var last = block.last;
+        if (last is HExitTry) {
+          block.addSuccessor(successor);
+        }
+      }
+    }
+
+    // Setup all successors. The entry block that contains the [HTry]
+    // has 1) the body 2) the finally, and 4) the exit
+    // blocks as successors.
+    enterBlock.addSuccessor(startTryBlock);
+    enterBlock.addSuccessor(startFinallyBlock);
+    enterBlock.addSuccessor(exitBlock);
+
+    // The body has the finally block as successor.
+    if (endTryBlock != null) {
+      endTryBlock.addSuccessor(startFinallyBlock);
+      endTryBlock.addSuccessor(exitBlock);
+    }
+
+    // The finally block has the exit block as successor.
+    endFinallyBlock.addSuccessor(exitBlock);
+
+    // If a block inside try/catch aborts (eg with a return statement),
+    // we explicitely mark this block a predecessor of the catch
+    // block and the finally block.
+    addExitTrySuccessor(startFinallyBlock);
+
+    open(exitBlock);
+    enterBlock.setBlockFlow(
+        new HTryBlockInformation(
+          wrapStatementGraph(bodyGraph),
+          null, // No catch-variable.
+          null, // No catchGraph.
+          wrapStatementGraph(finallyGraph)),
+        exitBlock);
+    inTryStatement = oldInTryStatement;
+  }
+
   visitTryStatement(ast.TryStatement node) {
     // Save the current locals. The catch block and the finally block
     // must not reuse the existing locals handler. None of the variables
@@ -6222,6 +6418,7 @@
         new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops);
     weeder.visit(functionExpression.initializers);
     weeder.visit(functionExpression.body);
+    weeder.visit(functionExpression.asyncModifier);
     return !weeder.tooDifficult;
   }
 
@@ -6248,6 +6445,13 @@
     }
   }
 
+  @override
+  void visitAsyncModifier(ast.AsyncModifier node) {
+    if (node.isYielding || node.isAsynchronous) {
+      tooDifficult = true;
+    }
+  }
+
   void visitFunctionExpression(ast.Node node) {
     if (!registerNode()) return;
     tooDifficult = true;
diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart
index 0d4b389..21387ea 100644
--- a/pkg/compiler/lib/src/ssa/codegen.dart
+++ b/pkg/compiler/lib/src/ssa/codegen.dart
@@ -16,43 +16,23 @@
 
 
   js.Node attachPosition(js.Node node, AstElement element) {
-    // TODO(sra): Attaching positions might be cleaner if the source position
-    // was on a wrapping node.
-    SourceFile sourceFile = sourceFileOfElement(element);
-    String name = element.name;
-    AstElement implementation = element.implementation;
-    ast.Node expression = implementation.node;
-    Token beginToken;
-    Token endToken;
-    if (expression == null) {
-      // Synthesized node. Use the enclosing element for the location.
-      beginToken = endToken = element.position;
-    } else {
-      beginToken = expression.getBeginToken();
-      endToken = expression.getEndToken();
-    }
-    // TODO(podivilov): find the right sourceFile here and remove offset
-    // checks below.
-    var sourcePosition, endSourcePosition;
-    if (beginToken.charOffset < sourceFile.length) {
-      sourcePosition =
-          new TokenSourceFileLocation(sourceFile, beginToken, name);
-    }
-    if (endToken.charOffset < sourceFile.length) {
-      endSourcePosition =
-          new TokenSourceFileLocation(sourceFile, endToken, name);
-    }
-    return node.withPosition(sourcePosition, endSourcePosition);
-  }
-
-  SourceFile sourceFileOfElement(Element element) {
-    return element.implementation.compilationUnit.script.file;
+    return node.withSourceInformation(
+        StartEndSourceInformation.computeSourceInformation(element));
   }
 
   js.Fun buildJavaScriptFunction(FunctionElement element,
                                  List<js.Parameter> parameters,
                                  js.Block body) {
-    return attachPosition(new js.Fun(parameters, body), element);
+    js.AsyncModifier asyncModifier = element.asyncMarker.isAsync
+        ? (element.asyncMarker.isYielding
+            ? const js.AsyncModifier.asyncStar()
+            : const js.AsyncModifier.async())
+        : (element.asyncMarker.isYielding
+            ? const js.AsyncModifier.syncStar()
+            : const js.AsyncModifier.sync());
+
+    return attachPosition(
+        new js.Fun(parameters, body, asyncModifier: asyncModifier), element);
   }
 
   js.Expression generateCode(CodegenWorkItem work, HGraph graph) {
@@ -75,10 +55,13 @@
 
   js.Expression generateMethod(CodegenWorkItem work, HGraph graph) {
     return measure(() {
+      FunctionElement element = work.element;
+      if (element.asyncMarker != AsyncMarker.SYNC) {
+        work.registry.registerAsyncMarker(element);
+      }
       SsaCodeGenerator codegen = new SsaCodeGenerator(backend, work);
       codegen.visitGraph(graph);
       compiler.tracer.traceGraph("codegen", graph);
-      FunctionElement element = work.element;
       return buildJavaScriptFunction(element, codegen.parameters, codegen.body);
     });
   }
@@ -241,13 +224,12 @@
   }
 
   js.Node attachLocation(js.Node jsNode, HInstruction instruction) {
-    return jsNode.withLocation(instruction.sourcePosition);
+    return attachSourceInformation(jsNode, instruction.sourceInformation);
   }
 
-  js.Node attachLocationRange(js.Node jsNode,
-                              SourceFileLocation sourcePosition,
-                              SourceFileLocation endSourcePosition) {
-    return jsNode.withPosition(sourcePosition, endSourcePosition);
+  js.Node attachSourceInformation(js.Node jsNode,
+                                  SourceInformation sourceInformation) {
+    return jsNode.withSourceInformation(sourceInformation);
   }
 
   void preGenerateMethod(HGraph graph) {
@@ -956,8 +938,7 @@
         compiler.internalError(condition.conditionExpression,
             'Unexpected loop kind: ${info.kind}.');
     }
-    js.Statement result =
-        attachLocationRange(loop, info.sourcePosition, info.endSourcePosition);
+    js.Statement result = attachSourceInformation(loop, info.sourceInformation);
     if (info.kind == HLoopBlockInformation.SWITCH_CONTINUE_LOOP) {
       String continueLabelString =
           backend.namer.implicitContinueLabelName(info.target);
@@ -1969,6 +1950,16 @@
     }
   }
 
+  visitAwait(HAwait node) {
+    use(node.inputs[0]);
+    push(new js.Await(pop()), node);
+  }
+
+  visitYield(HYield node) {
+    use(node.inputs[0]);
+    pushStatement(new js.DartYield(pop(), node.hasStar), node);
+  }
+
   visitRangeConversion(HRangeConversion node) {
     // Range conversion instructions are removed by the value range
     // analyzer.
@@ -2047,7 +2038,15 @@
     if (helperName == 'wrapException') {
       pushStatement(new js.Throw(value));
     } else {
-      pushStatement(new js.Return(value));
+      Element element = work.element;
+      if (element is FunctionElement && element.asyncMarker.isYielding) {
+        // `return <expr>;` is illegal in a sync* or async* function.
+        // To have the the async-translator working, we avoid introducing
+        // `return` nodes.
+        pushStatement(new js.ExpressionStatement(value));
+      } else {
+        pushStatement(new js.Return(value));
+      }
     }
   }
 
diff --git a/pkg/compiler/lib/src/ssa/codegen_helpers.dart b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
index 88e5505..c298e06 100644
--- a/pkg/compiler/lib/src/ssa/codegen_helpers.dart
+++ b/pkg/compiler/lib/src/ssa/codegen_helpers.dart
@@ -34,8 +34,8 @@
         if (replacement.sourceElement == null) {
           replacement.sourceElement = instruction.sourceElement;
         }
-        if (replacement.sourcePosition == null) {
-          replacement.sourcePosition = instruction.sourcePosition;
+        if (replacement.sourceInformation == null) {
+          replacement.sourceInformation = instruction.sourceInformation;
         }
         if (!replacement.isInBasicBlock()) {
           // The constant folding can return an instruction that is already
diff --git a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
index 3a0f467..d8213e2 100644
--- a/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
+++ b/pkg/compiler/lib/src/ssa/interceptor_simplifier.dart
@@ -300,7 +300,7 @@
         if (backend.mayGenerateInstanceofCheck(user.typeExpression)) {
           HInstruction instanceofCheck = new HIs.instanceOf(
               user.typeExpression, user.expression, user.instructionType);
-          instanceofCheck.sourcePosition = user.sourcePosition;
+          instanceofCheck.sourceInformation = user.sourceInformation;
           instanceofCheck.sourceElement = user.sourceElement;
           return replaceUserWith(instanceofCheck);
         }
@@ -313,7 +313,7 @@
         inputs[0] = nullConstant;
         HOneShotInterceptor oneShotInterceptor = new HOneShotInterceptor(
             user.selector, inputs, user.instructionType, interceptedClasses);
-        oneShotInterceptor.sourcePosition = user.sourcePosition;
+        oneShotInterceptor.sourceInformation = user.sourceInformation;
         oneShotInterceptor.sourceElement = user.sourceElement;
         return replaceUserWith(oneShotInterceptor);
       }
diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart
index 7337b7b..e6db5e3 100644
--- a/pkg/compiler/lib/src/ssa/nodes.dart
+++ b/pkg/compiler/lib/src/ssa/nodes.dart
@@ -6,6 +6,7 @@
 
 abstract class HVisitor<R> {
   R visitAdd(HAdd node);
+  R visitAwait(HAwait node);
   R visitBitAnd(HBitAnd node);
   R visitBitNot(HBitNot node);
   R visitBitOr(HBitOr node);
@@ -71,6 +72,7 @@
   R visitTry(HTry node);
   R visitTypeConversion(HTypeConversion node);
   R visitTypeKnown(HTypeKnown node);
+  R visitYield(HYield node);
   R visitReadTypeVariable(HReadTypeVariable node);
   R visitFunctionType(HFunctionType node);
   R visitVoidType(HVoidType node);
@@ -354,6 +356,8 @@
   visitVoidType(HVoidType node) => visitInstruction(node);
   visitInterfaceType(HInterfaceType node) => visitInstruction(node);
   visitDynamicType(HDynamicType node) => visitInstruction(node);
+  visitAwait(HAwait node) => visitInstruction(node);
+  visitYield(HYield node) => visitInstruction(node);
 }
 
 class SubGraph {
@@ -759,7 +763,7 @@
 
 abstract class HInstruction implements Spannable {
   Entity sourceElement;
-  SourceFileLocation sourcePosition;
+  SourceInformation sourceInformation;
 
   final int id;
   static int idCounter;
@@ -2234,6 +2238,26 @@
   bool canThrow() => true;
 }
 
+class HAwait extends HInstruction {
+  HAwait(HInstruction value, TypeMask type)
+      : super(<HInstruction>[value], type);
+  toString() => 'await';
+  accept(HVisitor visitor) => visitor.visitAwait(this);
+  // An await will throw if its argument is not a real future.
+  bool canThrow() => true;
+  SideEffects sideEffects = new SideEffects();
+}
+
+class HYield extends HInstruction {
+  HYield(HInstruction value, this.hasStar)
+      : super(<HInstruction>[value], const TypeMask.nonNullEmpty());
+  bool hasStar;
+  toString() => 'yield';
+  accept(HVisitor visitor) => visitor.visitYield(this);
+  bool canThrow() => false;
+  SideEffects sideEffects = new SideEffects();
+}
+
 class HThrow extends HControlFlow {
   final bool isRethrow;
   HThrow(value, {this.isRethrow: false}) : super(<HInstruction>[value]);
@@ -2882,8 +2906,7 @@
   final HExpressionInformation updates;
   final JumpTarget target;
   final List<LabelDefinition> labels;
-  final SourceFileLocation sourcePosition;
-  final SourceFileLocation endSourcePosition;
+  final SourceInformation sourceInformation;
 
   HLoopBlockInformation(this.kind,
                         this.initializer,
@@ -2892,8 +2915,7 @@
                         this.updates,
                         this.target,
                         this.labels,
-                        this.sourcePosition,
-                        this.endSourcePosition) {
+                        this.sourceInformation) {
     assert(
         (kind == DO_WHILE_LOOP ? body.start : condition.start).isLoopHeader());
   }
diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart
index 9aa51f0..04ac400 100644
--- a/pkg/compiler/lib/src/ssa/optimize.dart
+++ b/pkg/compiler/lib/src/ssa/optimize.dart
@@ -167,8 +167,8 @@
         if (replacement.sourceElement == null) {
           replacement.sourceElement = instruction.sourceElement;
         }
-        if (replacement.sourcePosition == null) {
-          replacement.sourcePosition = instruction.sourcePosition;
+        if (replacement.sourceInformation == null) {
+          replacement.sourceInformation = instruction.sourceInformation;
         }
         if (!replacement.isInBasicBlock()) {
           // The constant folding can return an instruction that is already
diff --git a/pkg/compiler/lib/src/ssa/ssa.dart b/pkg/compiler/lib/src/ssa/ssa.dart
index 2b56162..3e80b8e 100644
--- a/pkg/compiler/lib/src/ssa/ssa.dart
+++ b/pkg/compiler/lib/src/ssa/ssa.dart
@@ -18,19 +18,20 @@
          VariableElementX,
          ConstructorBodyElementX;
 import '../helpers/helpers.dart';
+import '../io/source_file.dart';
+import '../io/source_information.dart';
 import '../js/js.dart' as js;
 import '../js_backend/js_backend.dart';
 import '../js_emitter/js_emitter.dart' show CodeEmitterTask, NativeEmitter;
 import '../native/native.dart' as native;
 import '../scanner/scannerlib.dart'
     show PartialFunctionElement, Token, PLUS_TOKEN;
-import '../io/source_file.dart';
-import '../io/source_map_builder.dart';
 import '../tree/tree.dart' as ast;
 import '../types/types.dart';
 import '../types/constants.dart' show computeTypeMask;
 import '../universe/universe.dart';
 import '../util/util.dart';
+import '../js/rewrite_async.dart';
 
 part 'builder.dart';
 part 'codegen.dart';
diff --git a/pkg/compiler/lib/src/ssa/ssa_tracer.dart b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
index 09d8069..103ecae 100644
--- a/pkg/compiler/lib/src/ssa/ssa_tracer.dart
+++ b/pkg/compiler/lib/src/ssa/ssa_tracer.dart
@@ -519,4 +519,12 @@
   String visitDynamicType(HDynamicType node) {
     return "DynamicType";
   }
+
+  String visitAwait(HAwait node) {
+    return "await ${temporaryId(node.inputs[0])}";
+  }
+
+  String visitYield(HYield node) {
+    return "yield${node.hasStar ? "*" : ""} ${temporaryId(node.inputs[0])}";
+  }
 }
diff --git a/pkg/compiler/lib/src/tree/unparser.dart b/pkg/compiler/lib/src/tree/unparser.dart
index 1d9c690..d7dbda7 100644
--- a/pkg/compiler/lib/src/tree/unparser.dart
+++ b/pkg/compiler/lib/src/tree/unparser.dart
@@ -408,7 +408,7 @@
     if (node.starToken != null) {
       write(node.starToken.value);
     }
-    space();
+    write(' ');
     visit(node.expression);
     write(node.endToken.value);
   }
diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart
index a688778..038ecf1 100644
--- a/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart
+++ b/pkg/compiler/lib/src/tree_ir/tree_ir_nodes.dart
@@ -181,6 +181,8 @@
   final FunctionElement target;
   final List<Expression> arguments;
   final Selector selector;
+  /// TODO(karlklose): get rid of this field.  Instead use the constant's
+  /// expression to find the constructor to be called in dart2dart.
   final values.ConstantValue constant;
 
   InvokeConstructor(this.type, this.target, this.selector, this.arguments,
@@ -197,9 +199,8 @@
 /// Calls [toString] on each argument and concatenates the results.
 class ConcatenateStrings extends Expression {
   final List<Expression> arguments;
-  final values.ConstantValue constant;
 
-  ConcatenateStrings(this.arguments, [this.constant]);
+  ConcatenateStrings(this.arguments);
 
   accept(ExpressionVisitor visitor) => visitor.visitConcatenateStrings(this);
   accept1(ExpressionVisitor1 visitor, arg) {
diff --git a/pkg/compiler/lib/src/universe/universe.dart b/pkg/compiler/lib/src/universe/universe.dart
index a7ddbb0..3deffcb 100644
--- a/pkg/compiler/lib/src/universe/universe.dart
+++ b/pkg/compiler/lib/src/universe/universe.dart
@@ -506,49 +506,7 @@
    *
    * Invariant: [element] must be the implementation element.
    */
-  /*<S, T>*/ List/*<T>*/ makeArgumentsList(
-        FunctionElement element,
-        List/*<T>*/ compiledArguments,
-        /*T*/ compileDefaultValue(ParameterElement element)) {
-    assert(invariant(element, element.isImplementation));
-    List/*<T>*/ result = new List();
-    FunctionSignature parameters = element.functionSignature;
-    int i = 0;
-    parameters.forEachRequiredParameter((ParameterElement element) {
-      result.add(compiledArguments[i]);
-      ++i;
-    });
-
-    if (!parameters.optionalParametersAreNamed) {
-      parameters.forEachOptionalParameter((ParameterElement element) {
-        if (i < compiledArguments.length) {
-          result.add(compiledArguments[i]);
-          ++i;
-        } else {
-          result.add(compileDefaultValue(element));
-        }
-      });
-    } else {
-      int offset = i;
-      // Iterate over the optional parameters of the signature, and try to
-      // find them in [compiledNamedArguments]. If found, we use the
-      // value in the temporary list, otherwise the default value.
-      parameters.orderedOptionalParameters
-          .forEach((ParameterElement element) {
-        int foundIndex = namedArguments.indexOf(element.name);
-        if (foundIndex != -1) {
-          result.add(compiledArguments[offset + foundIndex]);
-        } else {
-          result.add(compileDefaultValue(element));
-        }
-      });
-    }
-    return result;
-  }
-
-  /// This is a version of [makeArgumentsList] that works for a `Link`
-  /// representation of arguments.
-  /*<T>*/ List/*<T>*/ makeArgumentsList2(
+  /*<T>*/ List/*<T>*/ makeArgumentsList(
       Link<Node> arguments,
       FunctionElement element,
       /*T*/ compileArgument(Node argument),
@@ -593,7 +551,6 @@
     return result;
   }
 
-
   /**
    * Fills [list] with the arguments in the order expected by
    * [callee], and where [caller] is a synthesized element
@@ -661,10 +618,10 @@
                                           namedParameters);
 
     if (!selector.applies(callee, world)) return false;
-    list.addAll(selector.makeArgumentsList2(nodes,
-                                            callee,
-                                            internalCompileArgument,
-                                            compileConstant));
+    list.addAll(selector.makeArgumentsList(nodes,
+                                           callee,
+                                           internalCompileArgument,
+                                           compileConstant));
 
     return true;
   }
diff --git a/pkg/compiler/lib/src/use_unused_api.dart b/pkg/compiler/lib/src/use_unused_api.dart
index 909b000..77f0bc9 100644
--- a/pkg/compiler/lib/src/use_unused_api.dart
+++ b/pkg/compiler/lib/src/use_unused_api.dart
@@ -51,10 +51,10 @@
   useSetlet(null);
   useImmutableEmptySet(null);
   useElementVisitor(new ElementVisitor());
-  useJs(new js.Program(null));
-  useJs(new js.Blob(null));
-  useJs(new js.NamedFunction(null, null));
-  useJs(new js.ArrayHole());
+  useJsNode(new js.Program(null));
+  useJsNode(new js.NamedFunction(null, null));
+  useJsNode(new js.ArrayHole());
+  useJsOther(new js.SimpleJavaScriptPrintingContext());
   useJsBackend(null);
   useConcreteTypesInferrer(null);
   useColor();
@@ -170,10 +170,14 @@
     ..visitWarnOnUseElement(null);
 }
 
-useJs(js.Node node) {
+useJsNode(js.Node node) {
   node.asVariableUse();
 }
 
+useJsOther(js.SimpleJavaScriptPrintingContext context) {
+  context.getText();
+}
+
 useJsBackend(js_backend.JavaScriptBackend backend) {
   backend.assembleCode(null);
   backend.annotations.noInlining(null);
diff --git a/pkg/compiler/lib/src/warnings.dart b/pkg/compiler/lib/src/warnings.dart
index e21429c..75334ea 100644
--- a/pkg/compiler/lib/src/warnings.dart
+++ b/pkg/compiler/lib/src/warnings.dart
@@ -2075,19 +2075,6 @@
     " require a preamble file located in:\n"
     "  <sdk>/lib/_internal/compiler/js_lib/preambles.");
 
-  static const MessageKind EXPERIMENTAL_ASYNC_AWAIT = const MessageKind(
-      "Experimental language feature 'async/await' is not supported.");
-
-  static const MessageKind INVALID_STARRED_KEYWORD = const MessageKind(
-      "Invalid '#{keyword}' keyword.",
-      options: const ['--enable-async'],
-      howToFix: "Try removing whitespace between '#{keyword}' and '*'.",
-      examples: const [
-        "main() async * {}",
-        "main() sync * {}",
-        "main() async* { yield * null; }"
-      ]);
-
   static const MessageKind INVALID_SYNC_MODIFIER = const MessageKind(
       "Invalid modifier 'sync'.",
       options: const ['--enable-async'],
diff --git a/pkg/dart2js_incremental/lib/library_updater.dart b/pkg/dart2js_incremental/lib/library_updater.dart
index 217abdb..bc269e2 100644
--- a/pkg/dart2js_incremental/lib/library_updater.dart
+++ b/pkg/dart2js_incremental/lib/library_updater.dart
@@ -995,9 +995,13 @@
   }
 
   String prettyPrintJs(jsAst.Node node) {
-    jsAst.Printer printer = new jsAst.Printer(compiler, null);
+    jsAst.JavaScriptPrintingOptions options =
+        new jsAst.JavaScriptPrintingOptions();
+    jsAst.JavaScriptPrintingContext context =
+        new jsAst.Dart2JSJavaScriptPrintingContext(compiler, null);
+    jsAst.Printer printer = new jsAst.Printer(options, context);
     printer.blockOutWithoutBraces(node);
-    return printer.outBuffer.getText();
+    return context.outBuffer.getText();
   }
 
   String callNameFor(FunctionElement element) {
diff --git a/pkg/expect/lib/expect.dart b/pkg/expect/lib/expect.dart
index ed094dd..2ca6ed3 100644
--- a/pkg/expect/lib/expect.dart
+++ b/pkg/expect/lib/expect.dart
@@ -406,14 +406,21 @@
 /// Annotation class for testing of dart2js. Use this as metadata on method
 /// declarations to make the type inferrer trust the parameter and return types,
 /// effectively asserting the runtime values will (at least) be subtypes of the
-/// annotated types.
+/// annotated types. 
+///
+/// While the actually inferred type is guaranteed to be a subtype of the 
+/// annotation, it often is more precise. In particular, if a method is only 
+/// called with `null`, the inferrer will still infer null. To ensure that
+/// the annotated type is also the inferred type, additionally use 
+/// [AssumeDynamic].
 class TrustTypeAnnotations {
   const TrustTypeAnnotations();
 }
 
 /// Annotation class for testing of dart2js. Use this as metadata on method
 /// declarations to disable closed world assumptions on parameters, effectively
-/// assuming that the runtime arguments could be any value.
+/// assuming that the runtime arguments could be any value. Note that the
+/// constraints due to [TrustTypeAnnotations] still apply. 
 class AssumeDynamic {
   const AssumeDynamic();
-}
\ No newline at end of file
+}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 35af9a7..fb2fc499 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -182,19 +182,6 @@
 [ $compiler == dartanalyzer || $compiler == dart2analyzer ]
 third_party/angular_tests/vm_test: StaticWarning # Uses removed APIs. See issue 18733.
 
-[ $compiler == dartanalyzer ]
-analysis_server/test/edit/sort_members_test: StaticWarning # Issue 22252
-analysis_server/test/edit/assists_test: StaticWarning # Issue 22252
-analysis_server/test/edit/format_test: StaticWarning # Issue 22252
-analysis_server/test/edit/refactoring_test: StaticWarning # Issue 22252
-analysis_server/test/edit/fixes_test: StaticWarning # Issue 22252
-analysis_server/test/socket_server_test: StaticWarning # Issue 22252
-analysis_server/test/search/type_hierarchy_test: StaticWarning # Issue 22252
-analysis_server/test/search/top_level_declarations_test: StaticWarning # Issue 22252
-analysis_server/test/search/member_references_test: StaticWarning # Issue 22252
-analysis_server/test/search/member_declarations_test: StaticWarning # Issue 22252
-analysis_server/test/search/element_references_test: StaticWarning # Issue 22252
-
 [ $runtime == safari || $runtime == safarimobilesim || $runtime == chrome || $runtime == ff || $ie ]
 # Various issues due to limited browser testing in Angular.
 third_party/angular_tests/*: Skip
diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn
index 040b902..793e5ed 100644
--- a/runtime/BUILD.gn
+++ b/runtime/BUILD.gn
@@ -2,9 +2,20 @@
 # for 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): These build arguments should likely be moved to a gni file that is
+# included in BUILD.gn files that care about the values of the flags. For now,
+# since the GN build only happens as part of a Mojo build there is no need for
+# the indirection.
+declare_args() {
+  # Instead of using is_debug, we introduce a different flag for specifying a
+  # Debug build of Dart so that clients can still use a Release build of Dart
+  # while themselves doing a Debug build.
+  dart_debug = false
+}
+
 config("dart_config") {
   defines = []
-  if (is_debug) {
+  if (dart_debug) {
     defines += ["DEBUG"]
   } else {
     defines += ["NDEBUG"]
@@ -24,6 +35,16 @@
     "-fno-rtti",
     "-fno-exceptions",
   ]
+
+  if (dart_debug) {
+    cflags += [
+      "-O1",
+    ]
+  } else {
+    cflags += [
+      "-O3",
+    ]
+  }
 }
 
 
@@ -82,6 +103,7 @@
 
 
 executable("libdart_dependency_helper") {
+  configs += [":dart_config"]
   deps = [
     "vm:libdart_lib_withcore",
     "vm:libdart_lib",
diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart
index a741da9..f5eec7e 100644
--- a/runtime/bin/builtin.dart
+++ b/runtime/bin/builtin.dart
@@ -351,7 +351,7 @@
     // uri.
     libraryUri = uri;
   }
-  _asyncLoadErrorCallback(uri, libraryUri, new LoadError(error));
+  _asyncLoadErrorCallback(uri, libraryUri, new LoadError(error.toString()));
   _finishedOneLoadRequest(uri);
 }
 
diff --git a/runtime/bin/builtin_impl_sources.gypi b/runtime/bin/builtin_impl_sources.gypi
index e778ca7..8fcb007 100644
--- a/runtime/bin/builtin_impl_sources.gypi
+++ b/runtime/bin/builtin_impl_sources.gypi
@@ -33,6 +33,7 @@
     'directory_linux.cc',
     'directory_macos.cc',
     'directory_win.cc',
+    'eventhandler_test.cc',
     'extensions.h',
     'extensions.cc',
     'extensions_android.cc',
diff --git a/runtime/bin/eventhandler.cc b/runtime/bin/eventhandler.cc
index 52f2539..9e53d5f 100644
--- a/runtime/bin/eventhandler.cc
+++ b/runtime/bin/eventhandler.cc
@@ -61,6 +61,9 @@
 
 
 void EventHandler::Start() {
+  // Initialize global socket registry.
+  ListeningSocketRegistry::Initialize();
+
   ASSERT(event_handler == NULL);
   shutdown_monitor = new Monitor();
   event_handler = new EventHandler();
@@ -91,6 +94,9 @@
   event_handler = NULL;
   delete shutdown_monitor;
   shutdown_monitor = NULL;
+
+  // Destroy the global socket registry.
+  ListeningSocketRegistry::Cleanup();
 }
 
 
diff --git a/runtime/bin/eventhandler.h b/runtime/bin/eventhandler.h
index 70636bf..c110d34 100644
--- a/runtime/bin/eventhandler.h
+++ b/runtime/bin/eventhandler.h
@@ -6,8 +6,11 @@
 #define BIN_EVENTHANDLER_H_
 
 #include "bin/builtin.h"
+#include "bin/dartutils.h"
 #include "bin/isolate_data.h"
 
+#include "platform/hashmap.h"
+
 namespace dart {
 namespace bin {
 
@@ -44,6 +47,9 @@
     ((data & COMMAND_MASK) == (1 << command_bit))  // NOLINT
 #define IS_EVENT(data, event_bit) \
     ((data & EVENT_MASK) == (1 << event_bit))  // NOLINT
+#define IS_IO_EVENT(data) \
+    ((data & (1 << kInEvent | 1 << kOutEvent | 1 << kCloseEvent)) != 0 && \
+     (data & ~(1 << kInEvent | 1 << kOutEvent | 1 << kCloseEvent)) == 0)
 #define IS_LISTENING_SOCKET(data) \
     ((data & (1 << kListeningSocket)) != 0)  // NOLINT
 #define TOKEN_COUNT(data) (data & ((1 << kCloseCommand) - 1))
@@ -144,6 +150,8 @@
   }
 
   void RemoveHead() {
+    ASSERT(head_ != NULL);
+
     Entry* e = head_;
     if (e->next_ == e) {
       head_ = NULL;
@@ -155,19 +163,58 @@
     delete e;
   }
 
+  void Remove(T item) {
+    if (head_ == NULL) {
+      return;
+    } else if (head_ == head_->next_) {
+      if (head_->t == item) {
+        delete head_;
+        head_ = NULL;
+        return;
+      }
+    } else {
+      Entry *current = head_;
+      do {
+        if (current->t == item) {
+          Entry *next = current->next_;
+          Entry *prev = current->prev_;
+          prev->next_ = next;
+          next->prev_ = prev;
+
+          if (current == head_) {
+            head_ = head_->next_;
+          }
+
+          delete current;
+          return;
+        }
+        current = current->next_;
+      } while (current != head_);
+    }
+  }
+
+  void RemoveAll() {
+    while (HasHead()) {
+      RemoveHead();
+    }
+  }
+
   T head() const { return head_->t; }
 
-  bool HasHead() {
+  bool HasHead() const {
     return head_ != NULL;
   }
 
   void Rotate() {
-    head_ = head_->next_;
+    if (head_ != NULL) {
+      ASSERT(head_->next_ != NULL);
+      head_ = head_->next_;
+    }
   }
 
  private:
   struct Entry {
-    explicit Entry(const T& t) : t(t) {}
+    explicit Entry(const T& t) : t(t), next_(NULL), prev_(NULL) {}
     const T t;
     Entry* next_;
     Entry* prev_;
@@ -176,6 +223,372 @@
   Entry* head_;
 };
 
+
+class DescriptorInfoBase {
+ public:
+  explicit DescriptorInfoBase(intptr_t fd) : fd_(fd) {
+    ASSERT(fd_ != -1);
+  }
+
+  virtual ~DescriptorInfoBase() {}
+
+  // The OS descriptor.
+  intptr_t fd() { return fd_; }
+
+  // Whether this descriptor refers to an underlying listening OS socket.
+  virtual bool IsListeningSocket() const = 0;
+
+  // Inserts or updates a new Dart_Port which is interested in events specified
+  // in `mask`.
+  virtual void SetPortAndMask(Dart_Port port, intptr_t mask) = 0;
+
+  // Removes a port from the interested listeners.
+  virtual void RemovePort(Dart_Port port) = 0;
+
+  // Removes all ports from the interested listeners.
+  virtual void RemoveAllPorts() = 0;
+
+  // Returns a port to which `events_ready` can be sent to. It will also
+  // decrease the token count by 1 for this port.
+  virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) = 0;
+
+  // Will post `data` to all known Dart_Ports. It will also decrease the token
+  // count by 1 for all ports.
+  virtual void NotifyAllDartPorts(uintptr_t events) = 0;
+
+  // Returns `count` tokens for the given port.
+  virtual void ReturnTokens(Dart_Port port, int count) = 0;
+
+  // Returns the union of event masks of all ports. If a port has a non-positive
+  // token count it's mask is assumed to be 0.
+  virtual intptr_t Mask() = 0;
+
+  // Closes this descriptor.
+  virtual void Close() = 0;
+
+ protected:
+  intptr_t fd_;
+};
+
+
+// Describes a OS descriptor (e.g. file descriptor on linux or HANDLE on
+// windows) which is connected to a single Dart_Port.
+//
+// Subclasses of this class can be e.g. connected tcp sockets.
+template<typename DI>
+class DescriptorInfoSingleMixin : public DI {
+ private:
+  static const int kTokenCount = 16;
+
+ public:
+  explicit DescriptorInfoSingleMixin(intptr_t fd, bool disable_tokens)
+      : DI(fd), port_(0), tokens_(kTokenCount), mask_(0),
+        disable_tokens_(disable_tokens) {}
+
+  virtual ~DescriptorInfoSingleMixin() { }
+
+  virtual bool IsListeningSocket() const { return false; }
+
+  virtual void SetPortAndMask(Dart_Port port, intptr_t mask) {
+    ASSERT(port_ == 0 || port == port_);
+    port_ = port;
+    mask_ = mask;
+  }
+
+  virtual void RemovePort(Dart_Port port) {
+    // TODO(dart:io): Find out where we call RemovePort() with the invalid
+    // port. Afterwards remove the part in the ASSERT here.
+    ASSERT(port_ == 0 || port_ == port);
+    port_ = 0;
+    mask_ = 0;
+  }
+
+  virtual void RemoveAllPorts() {
+    port_ = 0;
+    mask_ = 0;
+  }
+
+  virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) {
+    ASSERT(IS_IO_EVENT(events_ready) ||
+           IS_EVENT(events_ready, kDestroyedEvent));
+    if (!disable_tokens_) {
+      tokens_--;
+    }
+    return port_;
+  }
+
+  virtual void NotifyAllDartPorts(uintptr_t events) {
+    // Unexpected close, asynchronous destroy or error events are the only
+    // ones we broadcast to all listeners.
+    ASSERT(IS_EVENT(events, kCloseEvent) ||
+           IS_EVENT(events, kErrorEvent) ||
+           IS_EVENT(events, kDestroyedEvent));
+
+    if (port_ != 0) {
+      DartUtils::PostInt32(port_, events);
+    }
+    if (!disable_tokens_) {
+      tokens_--;
+    }
+  }
+
+  virtual void ReturnTokens(Dart_Port port, int count) {
+    ASSERT(port_ == port);
+    if (!disable_tokens_) {
+      tokens_ += count;
+    }
+    ASSERT(tokens_ <= kTokenCount);
+  }
+
+  virtual intptr_t Mask() {
+    if (tokens_ <= 0) {
+      return 0;
+    }
+    return mask_;
+  }
+
+  virtual void Close() {
+    DI::Close();
+  }
+
+ private:
+  Dart_Port port_;
+  int tokens_;
+  intptr_t mask_;
+  bool disable_tokens_;
+};
+
+
+// Describes a OS descriptor (e.g. file descriptor on linux or HANDLE on
+// windows) which is connected to multiple Dart_Port's.
+//
+// Subclasses of this class can be e.g. a listening socket which multiple
+// isolates are listening on.
+template<typename DI>
+class DescriptorInfoMultipleMixin : public DI {
+ private:
+  static const int kTokenCount = 4;
+
+  static bool SamePortValue(void* key1, void* key2) {
+    return reinterpret_cast<Dart_Port>(key1) ==
+        reinterpret_cast<Dart_Port>(key2);
+  }
+
+  static uint32_t GetHashmapHashFromPort(Dart_Port port) {
+    return static_cast<uint32_t>(port & 0xFFFFFFFF);
+  }
+
+  static void* GetHashmapKeyFromPort(Dart_Port port) {
+    return reinterpret_cast<void*>(port);
+  }
+
+  static bool IsReadingMask(intptr_t mask) {
+    if (mask == (1 << kInEvent)) {
+      return true;
+    } else {
+      ASSERT(mask == 0);
+      return false;
+    }
+  }
+
+  struct PortEntry {
+    Dart_Port dart_port;
+    intptr_t is_reading;
+    intptr_t token_count;
+
+    bool IsReady() { return token_count > 0 && is_reading; }
+  };
+
+ public:
+  explicit DescriptorInfoMultipleMixin(intptr_t fd, bool disable_tokens)
+      : DI(fd), tokens_map_(&SamePortValue, kTokenCount),
+        disable_tokens_(disable_tokens) {}
+
+  virtual ~DescriptorInfoMultipleMixin() {}
+
+  virtual bool IsListeningSocket() const { return true; }
+
+  virtual void SetPortAndMask(Dart_Port port, intptr_t mask) {
+    HashMap::Entry* entry = tokens_map_.Lookup(
+        GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), true);
+    PortEntry* pentry;
+    if (entry->value == NULL) {
+      pentry = new PortEntry();
+      pentry->dart_port = port;
+      pentry->token_count = kTokenCount;
+      pentry->is_reading = IsReadingMask(mask);
+      entry->value = reinterpret_cast<void*>(pentry);
+
+      if (pentry->IsReady()) {
+        active_readers_.Add(pentry);
+      }
+    } else {
+      pentry = reinterpret_cast<PortEntry*>(entry->value);
+      bool was_ready = pentry->IsReady();
+      pentry->is_reading = IsReadingMask(mask);
+      bool is_ready = pentry->IsReady();
+
+      if (was_ready && !is_ready) {
+        active_readers_.Remove(pentry);
+      } else if (!was_ready && is_ready) {
+        active_readers_.Add(pentry);
+      }
+    }
+
+#ifdef DEBUG
+    // To ensure that all readers are ready.
+    int ready_count = 0;
+
+    if (active_readers_.HasHead()) {
+      PortEntry* root = reinterpret_cast<PortEntry*>(active_readers_.head());
+      PortEntry* current = root;
+      do {
+        ASSERT(current->IsReady());
+        ready_count++;
+        active_readers_.Rotate();
+        current = active_readers_.head();
+      } while (current != root);
+    }
+
+    for (HashMap::Entry *entry = tokens_map_.Start();
+         entry != NULL;
+         entry = tokens_map_.Next(entry)) {
+      PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
+      if (pentry->IsReady()) {
+        ready_count--;
+      }
+    }
+    // Ensure all ready items are in `active_readers_`.
+    ASSERT(ready_count == 0);
+#endif
+  }
+
+  virtual void RemovePort(Dart_Port port) {
+    HashMap::Entry* entry = tokens_map_.Lookup(
+        GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false);
+    if (entry != NULL) {
+      PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
+      if (pentry->IsReady()) {
+        active_readers_.Remove(pentry);
+      }
+      tokens_map_.Remove(
+          GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port));
+      delete pentry;
+    } else {
+      // NOTE: This is a listening socket which has been immediately closed.
+      //
+      // If a listening socket is not listened on, the event handler does not
+      // know about it beforehand. So the first time the event handler knows
+      // about it, is when it is supposed to be closed. We therefore do nothing
+      // here.
+      //
+      // But whether to close it, depends on whether other isolates have it open
+      // as well or not.
+    }
+  }
+
+  virtual void RemoveAllPorts() {
+    active_readers_.RemoveAll();
+    for (HashMap::Entry *entry = tokens_map_.Start();
+         entry != NULL;
+         entry = tokens_map_.Next(entry)) {
+      PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
+      delete pentry;
+    }
+    tokens_map_.Clear();
+  }
+
+  virtual Dart_Port NextNotifyDartPort(intptr_t events_ready) {
+    // We're only sending `kInEvents` if there are multiple listeners (which is
+    // listening socktes).
+    ASSERT(IS_EVENT(events_ready, kInEvent) ||
+           IS_EVENT(events_ready, kDestroyedEvent));
+
+    if (active_readers_.HasHead()) {
+      PortEntry* pentry = reinterpret_cast<PortEntry*>(active_readers_.head());
+
+      // Update token count.
+      if (!disable_tokens_) {
+        pentry->token_count--;
+      }
+      if (pentry->token_count <= 0) {
+        active_readers_.RemoveHead();
+      } else {
+        active_readers_.Rotate();
+      }
+
+      return pentry->dart_port;
+    }
+    return 0;
+  }
+
+  virtual void NotifyAllDartPorts(uintptr_t events) {
+    // Unexpected close, asynchronous destroy or error events are the only
+    // ones we broadcast to all listeners.
+    ASSERT(IS_EVENT(events, kCloseEvent) ||
+           IS_EVENT(events, kErrorEvent) ||
+           IS_EVENT(events, kDestroyedEvent));
+
+    for (HashMap::Entry *entry = tokens_map_.Start();
+         entry != NULL;
+         entry = tokens_map_.Next(entry)) {
+      PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
+      DartUtils::PostInt32(pentry->dart_port, events);
+
+      // Update token count.
+      bool was_ready = pentry->IsReady();
+      if (!disable_tokens_) {
+        pentry->token_count--;
+      }
+
+      if (was_ready && pentry->token_count <= 0) {
+        active_readers_.Remove(pentry);
+      }
+    }
+  }
+
+  virtual void ReturnTokens(Dart_Port port, int count) {
+    HashMap::Entry* entry = tokens_map_.Lookup(
+        GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false);
+    ASSERT(entry != NULL);
+
+    PortEntry* pentry = reinterpret_cast<PortEntry*>(entry->value);
+    bool was_ready = pentry->IsReady();
+    if (!disable_tokens_) {
+      pentry->token_count += count;
+    }
+    ASSERT(pentry->token_count <= kTokenCount);
+    bool is_ready = pentry->token_count > 0 && pentry->IsReady();
+    if (!was_ready && is_ready) {
+      active_readers_.Add(pentry);
+    }
+  }
+
+  virtual intptr_t Mask() {
+    if (active_readers_.HasHead()) {
+      return 1 << kInEvent;
+    }
+    return 0;
+  }
+
+  virtual void Close() {
+    DI::Close();
+  }
+
+ private:
+  // The [Dart_Port]s which are not paused (i.e. are interested in read events,
+  // i.e. `mask == (1 << kInEvent)`) and we have enough tokens to communicate
+  // with them.
+  CircularLinkedList<PortEntry *> active_readers_;
+
+  // A convenience mapping:
+  //   Dart_Port -> struct PortEntry { dart_port, mask, token_count }
+  HashMap tokens_map_;
+
+  bool disable_tokens_;
+};
+
+
 }  // namespace bin
 }  // namespace dart
 
diff --git a/runtime/bin/eventhandler_android.cc b/runtime/bin/eventhandler_android.cc
index 204bf8a..14cddc1 100644
--- a/runtime/bin/eventhandler_android.cc
+++ b/runtime/bin/eventhandler_android.cc
@@ -6,6 +6,7 @@
 #if defined(TARGET_OS_ANDROID)
 
 #include "bin/eventhandler.h"
+#include "bin/eventhandler_android.h"
 
 #include <errno.h>  // NOLINT
 #include <pthread.h>  // NOLINT
@@ -19,6 +20,8 @@
 #include "bin/dartutils.h"
 #include "bin/fdutils.h"
 #include "bin/log.h"
+#include "bin/lockers.h"
+#include "bin/socket.h"
 #include "bin/thread.h"
 #include "bin/utils.h"
 #include "platform/hashmap.h"
@@ -35,46 +38,50 @@
 namespace bin {
 
 
-intptr_t SocketData::GetPollEvents() {
+intptr_t DescriptorInfo::GetPollEvents() {
   // Do not ask for EPOLLERR and EPOLLHUP explicitly as they are
   // triggered anyway.
   intptr_t events = 0;
-  if ((mask_ & (1 << kInEvent)) != 0) {
+  if ((Mask() & (1 << kInEvent)) != 0) {
     events |= EPOLLIN;
   }
-  if ((mask_ & (1 << kOutEvent)) != 0) {
+  if ((Mask() & (1 << kOutEvent)) != 0) {
     events |= EPOLLOUT;
   }
   return events;
 }
 
 
-// Unregister the file descriptor for a SocketData structure with epoll.
-static void RemoveFromEpollInstance(intptr_t epoll_fd_, SocketData* sd) {
+// Unregister the file descriptor for a DescriptorInfo structure with
+// epoll.
+static void RemoveFromEpollInstance(intptr_t epoll_fd_,
+                                    DescriptorInfo* di) {
   VOID_NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_,
                                    EPOLL_CTL_DEL,
-                                   sd->fd(),
+                                   di->fd(),
                                    NULL));
 }
 
 
-static void AddToEpollInstance(intptr_t epoll_fd_, SocketData* sd) {
+static void AddToEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) {
   struct epoll_event event;
-  event.events = EPOLLRDHUP | sd->GetPollEvents();
-  if (!sd->IsListeningSocket()) {
+  event.events = EPOLLRDHUP | di->GetPollEvents();
+  if (!di->IsListeningSocket()) {
     event.events |= EPOLLET;
   }
-  event.data.ptr = sd;
+  event.data.ptr = di;
   int status = NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_,
                                            EPOLL_CTL_ADD,
-                                           sd->fd(),
+                                           di->fd(),
                                            &event));
   if (status == -1) {
+    // TODO(dart:io): Verify that the dart end is handling this correctly.
+
     // Epoll does not accept the file descriptor. It could be due to
     // already closed file descriptor, or unuspported devices, such
     // as /dev/null. In such case, mark the file descriptor as closed,
     // so dart will handle it accordingly.
-    DartUtils::PostInt32(sd->port(), 1 << kCloseEvent);
+    di->NotifyAllDartPorts(1 << kCloseEvent);
   }
 }
 
@@ -95,7 +102,7 @@
   static const int kEpollInitialSize = 64;
   epoll_fd_ = NO_RETRY_EXPECTED(epoll_create(kEpollInitialSize));
   if (epoll_fd_ == -1) {
-    FATAL("Failed creating epoll file descriptor");
+    FATAL1("Failed creating epoll file descriptor: %i", errno);
   }
   FDUtils::SetCloseOnExec(epoll_fd_);
   // Register the interrupt_fd with the epoll instance.
@@ -103,9 +110,9 @@
   event.events = EPOLLIN;
   event.data.ptr = NULL;
   int status = NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_,
-                                            EPOLL_CTL_ADD,
-                                            interrupt_fds_[0],
-                                            &event));
+                                           EPOLL_CTL_ADD,
+                                           interrupt_fds_[0],
+                                           &event));
   if (status == -1) {
     FATAL("Failed adding interrupt fd to epoll instance");
   }
@@ -119,21 +126,41 @@
 }
 
 
-SocketData* EventHandlerImplementation::GetSocketData(
-    intptr_t fd, bool listening_socket) {
+void EventHandlerImplementation::UpdateEpollInstance(intptr_t old_mask,
+                                                     DescriptorInfo *di) {
+  intptr_t new_mask = di->Mask();
+  if (old_mask != 0 && new_mask == 0) {
+    RemoveFromEpollInstance(epoll_fd_, di);
+  } else if (old_mask == 0 && new_mask != 0) {
+    AddToEpollInstance(epoll_fd_, di);
+  } else if (old_mask != 0 && new_mask != 0 && old_mask != new_mask) {
+    ASSERT(!di->IsListeningSocket());
+    RemoveFromEpollInstance(epoll_fd_, di);
+    AddToEpollInstance(epoll_fd_, di);
+  }
+}
+
+
+DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo(
+    intptr_t fd, bool is_listening) {
   ASSERT(fd >= 0);
   HashMap::Entry* entry = socket_map_.Lookup(
       GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true);
   ASSERT(entry != NULL);
-  SocketData* sd = reinterpret_cast<SocketData*>(entry->value);
-  if (sd == NULL) {
+  DescriptorInfo* di =
+      reinterpret_cast<DescriptorInfo*>(entry->value);
+  if (di == NULL) {
     // If there is no data in the hash map for this file descriptor a
-    // new SocketData for the file descriptor is inserted.
-    sd = new SocketData(fd, listening_socket);
-    entry->value = sd;
+    // new DescriptorInfo for the file descriptor is inserted.
+    if (is_listening) {
+      di = new DescriptorInfoMultiple(fd);
+    } else {
+      di = new DescriptorInfoSingle(fd);
+    }
+    entry->value = di;
   }
-  ASSERT(fd == sd->fd());
-  return sd;
+  ASSERT(fd == di->fd());
+  return di;
 }
 
 
@@ -154,7 +181,7 @@
     if (result == -1) {
       perror("Interrupt message failure:");
     }
-    FATAL1("Interrupt message failure. Wrote %d bytes.", result);
+    FATAL1("Interrupt message failure. Wrote %" Pd " bytes.", result);
   }
 }
 
@@ -172,39 +199,64 @@
     } else {
       ASSERT((msg[i].data & COMMAND_MASK) != 0);
 
-      SocketData* sd = GetSocketData(
+      DescriptorInfo* di = GetDescriptorInfo(
           msg[i].id, IS_LISTENING_SOCKET(msg[i].data));
       if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) {
+        ASSERT(!di->IsListeningSocket());
         // Close the socket for reading.
-        shutdown(sd->fd(), SHUT_RD);
+        VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD));
       } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) {
+        ASSERT(!di->IsListeningSocket());
         // Close the socket for writing.
-        shutdown(sd->fd(), SHUT_WR);
+        VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR));
       } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
-        // Close the socket and free system resources and move on to
-        // next message.
-        RemoveFromEpollInstance(epoll_fd_, sd);
-        intptr_t fd = sd->fd();
-        sd->Close();
-        socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
-        delete sd;
-        DartUtils::PostInt32(msg[i].dart_port, 1 << kDestroyedEvent);
+        // Close the socket and free system resources and move on to next
+        // message.
+        intptr_t old_mask = di->Mask();
+        Dart_Port port = msg[i].dart_port;
+        di->RemovePort(port);
+        intptr_t new_mask = di->Mask();
+        UpdateEpollInstance(old_mask, di);
+
+        intptr_t fd = di->fd();
+        if (di->IsListeningSocket()) {
+          // We only close the socket file descriptor from the operating
+          // system if there are no other dart socket objects which
+          // are listening on the same (address, port) combination.
+          ListeningSocketRegistry *registry =
+              ListeningSocketRegistry::Instance();
+
+          MutexLocker locker(registry->mutex());
+
+          if (registry->CloseSafe(fd)) {
+            ASSERT(new_mask == 0);
+            socket_map_.Remove(GetHashmapKeyFromFd(fd),
+                               GetHashmapHashFromFd(fd));
+            di->Close();
+            delete di;
+          }
+        } else {
+          ASSERT(new_mask == 0);
+          socket_map_.Remove(
+              GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
+          di->Close();
+          delete di;
+        }
+
+        DartUtils::PostInt32(port, 1 << kDestroyedEvent);
       } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) {
         int count = TOKEN_COUNT(msg[i].data);
-
-        for (int i = 0; i < count; i++) {
-          if (sd->ReturnToken()) {
-            AddToEpollInstance(epoll_fd_, sd);
-          }
-        }
+        intptr_t old_mask = di->Mask();
+        di->ReturnTokens(msg[i].dart_port, count);
+        UpdateEpollInstance(old_mask, di);
       } else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) {
         // `events` can only have kInEvent/kOutEvent flags set.
         intptr_t events = msg[i].data & EVENT_MASK;
         ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
 
-        // Setup events to wait for.
-        sd->SetPortAndMask(msg[i].dart_port, events);
-        AddToEpollInstance(epoll_fd_, sd);
+        intptr_t old_mask = di->Mask();
+        di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK);
+        UpdateEpollInstance(old_mask, di);
       } else {
         UNREACHABLE();
       }
@@ -233,9 +285,9 @@
 #endif
 
 intptr_t EventHandlerImplementation::GetPollEvents(intptr_t events,
-                                                   SocketData* sd) {
+                                                   DescriptorInfo* di) {
 #ifdef DEBUG_POLL
-  PrintEventMask(sd->fd(), events);
+  PrintEventMask(di->fd(), events);
 #endif
   if (events & EPOLLERR) {
     // Return error only if EPOLLIN is present.
@@ -256,15 +308,14 @@
     if (events[i].data.ptr == NULL) {
       interrupt_seen = true;
     } else {
-      SocketData* sd = reinterpret_cast<SocketData*>(events[i].data.ptr);
-      intptr_t event_mask = GetPollEvents(events[i].events, sd);
+      DescriptorInfo* di =
+          reinterpret_cast<DescriptorInfo*>(events[i].data.ptr);
+      intptr_t event_mask = GetPollEvents(events[i].events, di);
       if (event_mask != 0) {
-        if (sd->TakeToken()) {
-          // Took last token, remove from epoll.
-          RemoveFromEpollInstance(epoll_fd_, sd);
-        }
-        Dart_Port port = sd->port();
+        intptr_t old_mask = di->Mask();
+        Dart_Port port = di->NextNotifyDartPort(event_mask);
         ASSERT(port != 0);
+        UpdateEpollInstance(old_mask, di);
         DartUtils::PostInt32(port, event_mask);
       }
     }
@@ -329,7 +380,7 @@
 
 void EventHandlerImplementation::Start(EventHandler* handler) {
   int result = Thread::Start(&EventHandlerImplementation::Poll,
-                                   reinterpret_cast<uword>(handler));
+                             reinterpret_cast<uword>(handler));
   if (result != 0) {
     FATAL1("Failed to start event handler thread %d", result);
   }
@@ -343,7 +394,7 @@
 
 void EventHandlerImplementation::SendData(intptr_t id,
                                           Dart_Port dart_port,
-                                          intptr_t data) {
+                                          int64_t data) {
   WakeupHandler(id, dart_port, data);
 }
 
diff --git a/runtime/bin/eventhandler_android.h b/runtime/bin/eventhandler_android.h
index 2f9ab91..ee10016 100644
--- a/runtime/bin/eventhandler_android.h
+++ b/runtime/bin/eventhandler_android.h
@@ -22,54 +22,36 @@
 namespace dart {
 namespace bin {
 
-class SocketData {
+class DescriptorInfo : public DescriptorInfoBase {
  public:
-  explicit SocketData(intptr_t fd, bool listening_socket)
-      : fd_(fd), port_(0), mask_(0), tokens_(16),
-        listening_socket_(listening_socket) {
-    ASSERT(fd_ != -1);
-  }
+  explicit DescriptorInfo(intptr_t fd) : DescriptorInfoBase(fd) { }
+
+  virtual ~DescriptorInfo() { }
 
   intptr_t GetPollEvents();
 
-  void Close() {
-    port_ = 0;
-    mask_ = 0;
+  virtual void Close() {
     VOID_TEMP_FAILURE_RETRY(close(fd_));
     fd_ = -1;
   }
+};
 
-  void SetPortAndMask(Dart_Port port, intptr_t mask) {
-    ASSERT(fd_ != -1);
-    port_ = port;
-    mask_ = mask;
-  }
 
-  intptr_t fd() { return fd_; }
-  Dart_Port port() { return port_; }
+class DescriptorInfoSingle
+    : public DescriptorInfoSingleMixin<DescriptorInfo> {
+ public:
+  explicit DescriptorInfoSingle(intptr_t fd)
+      : DescriptorInfoSingleMixin(fd, false) {}
+  virtual ~DescriptorInfoSingle() {}
+};
 
-  bool IsListeningSocket() { return listening_socket_; }
 
-  // Returns true if the last token was taken.
-  bool TakeToken() {
-    ASSERT(tokens_ > 0);
-    tokens_--;
-    return tokens_ == 0;
-  }
-
-  // Returns true if the tokens was 0 before adding.
-  bool ReturnToken() {
-    ASSERT(tokens_ >= 0);
-    tokens_++;
-    return tokens_ == 1;
-  }
-
- private:
-  intptr_t fd_;
-  Dart_Port port_;
-  intptr_t mask_;
-  int tokens_;
-  bool listening_socket_;
+class DescriptorInfoMultiple
+    : public DescriptorInfoMultipleMixin<DescriptorInfo> {
+ public:
+  explicit DescriptorInfoMultiple(intptr_t fd)
+      : DescriptorInfoMultipleMixin(fd, false) {}
+  virtual ~DescriptorInfoMultiple() {}
 };
 
 
@@ -78,10 +60,12 @@
   EventHandlerImplementation();
   ~EventHandlerImplementation();
 
+  void UpdateEpollInstance(intptr_t old_mask, DescriptorInfo *di);
+
   // Gets the socket data structure for a given file
   // descriptor. Creates a new one if one is not found.
-  SocketData* GetSocketData(intptr_t fd, bool is_listening);
-  void SendData(intptr_t id, Dart_Port dart_port, intptr_t data);
+  DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening);
+  void SendData(intptr_t id, Dart_Port dart_port, int64_t data);
   void Start(EventHandler* handler);
   void Shutdown();
 
@@ -93,7 +77,7 @@
   void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data);
   void HandleInterruptFd();
   void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask);
-  intptr_t GetPollEvents(intptr_t events, SocketData* sd);
+  intptr_t GetPollEvents(intptr_t events, DescriptorInfo* di);
   static void* GetHashmapKeyFromFd(intptr_t fd);
   static uint32_t GetHashmapHashFromFd(intptr_t fd);
 
diff --git a/runtime/bin/eventhandler_linux.cc b/runtime/bin/eventhandler_linux.cc
index 475b39d..f28481f 100644
--- a/runtime/bin/eventhandler_linux.cc
+++ b/runtime/bin/eventhandler_linux.cc
@@ -6,6 +6,7 @@
 #if defined(TARGET_OS_LINUX)
 
 #include "bin/eventhandler.h"
+#include "bin/eventhandler_linux.h"
 
 #include <errno.h>  // NOLINT
 #include <pthread.h>  // NOLINT
@@ -20,6 +21,7 @@
 #include "bin/dartutils.h"
 #include "bin/fdutils.h"
 #include "bin/log.h"
+#include "bin/lockers.h"
 #include "bin/socket.h"
 #include "bin/thread.h"
 #include "platform/utils.h"
@@ -29,46 +31,50 @@
 namespace bin {
 
 
-intptr_t SocketData::GetPollEvents() {
+intptr_t DescriptorInfo::GetPollEvents() {
   // Do not ask for EPOLLERR and EPOLLHUP explicitly as they are
   // triggered anyway.
   intptr_t events = 0;
-  if ((mask_ & (1 << kInEvent)) != 0) {
+  if ((Mask() & (1 << kInEvent)) != 0) {
     events |= EPOLLIN;
   }
-  if ((mask_ & (1 << kOutEvent)) != 0) {
+  if ((Mask() & (1 << kOutEvent)) != 0) {
     events |= EPOLLOUT;
   }
   return events;
 }
 
 
-// Unregister the file descriptor for a SocketData structure with epoll.
-static void RemoveFromEpollInstance(intptr_t epoll_fd_, SocketData* sd) {
+// Unregister the file descriptor for a DescriptorInfo structure with
+// epoll.
+static void RemoveFromEpollInstance(intptr_t epoll_fd_,
+                                    DescriptorInfo* di) {
   VOID_NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_,
                                    EPOLL_CTL_DEL,
-                                   sd->fd(),
+                                   di->fd(),
                                    NULL));
 }
 
 
-static void AddToEpollInstance(intptr_t epoll_fd_, SocketData* sd) {
+static void AddToEpollInstance(intptr_t epoll_fd_, DescriptorInfo* di) {
   struct epoll_event event;
-  event.events = EPOLLRDHUP | sd->GetPollEvents();
-  if (!sd->IsListeningSocket()) {
+  event.events = EPOLLRDHUP | di->GetPollEvents();
+  if (!di->IsListeningSocket()) {
     event.events |= EPOLLET;
   }
-  event.data.ptr = sd;
+  event.data.ptr = di;
   int status = NO_RETRY_EXPECTED(epoll_ctl(epoll_fd_,
                                            EPOLL_CTL_ADD,
-                                           sd->fd(),
+                                           di->fd(),
                                            &event));
   if (status == -1) {
+    // TODO(dart:io): Verify that the dart end is handling this correctly.
+
     // Epoll does not accept the file descriptor. It could be due to
     // already closed file descriptor, or unuspported devices, such
     // as /dev/null. In such case, mark the file descriptor as closed,
     // so dart will handle it accordingly.
-    DartUtils::PostInt32(sd->port(), 1 << kCloseEvent);
+    di->NotifyAllDartPorts(1 << kCloseEvent);
   }
 }
 
@@ -129,25 +135,41 @@
 }
 
 
-SocketData* EventHandlerImplementation::GetSocketData(intptr_t fd,
-                                                      bool is_listening) {
+void EventHandlerImplementation::UpdateEpollInstance(intptr_t old_mask,
+                                                     DescriptorInfo *di) {
+  intptr_t new_mask = di->Mask();
+  if (old_mask != 0 && new_mask == 0) {
+    RemoveFromEpollInstance(epoll_fd_, di);
+  } else if (old_mask == 0 && new_mask != 0) {
+    AddToEpollInstance(epoll_fd_, di);
+  } else if (old_mask != 0 && new_mask != 0 && old_mask != new_mask) {
+    ASSERT(!di->IsListeningSocket());
+    RemoveFromEpollInstance(epoll_fd_, di);
+    AddToEpollInstance(epoll_fd_, di);
+  }
+}
+
+
+DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo(
+    intptr_t fd, bool is_listening) {
   ASSERT(fd >= 0);
   HashMap::Entry* entry = socket_map_.Lookup(
       GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true);
   ASSERT(entry != NULL);
-  SocketData* sd = reinterpret_cast<SocketData*>(entry->value);
-  if (sd == NULL) {
+  DescriptorInfo* di =
+      reinterpret_cast<DescriptorInfo*>(entry->value);
+  if (di == NULL) {
     // If there is no data in the hash map for this file descriptor a
-    // new SocketData for the file descriptor is inserted.
+    // new DescriptorInfo for the file descriptor is inserted.
     if (is_listening) {
-      sd = new ListeningSocketData(fd);
+      di = new DescriptorInfoMultiple(fd);
     } else {
-      sd = new SocketData(fd);
+      di = new DescriptorInfoSingle(fd);
     }
-    entry->value = sd;
+    entry->value = di;
   }
-  ASSERT(fd == sd->fd());
-  return sd;
+  ASSERT(fd == di->fd());
+  return di;
 }
 
 
@@ -195,42 +217,64 @@
     } else {
       ASSERT((msg[i].data & COMMAND_MASK) != 0);
 
-      SocketData* sd = GetSocketData(
+      DescriptorInfo* di = GetDescriptorInfo(
           msg[i].id, IS_LISTENING_SOCKET(msg[i].data));
       if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) {
-        ASSERT(!sd->IsListeningSocket());
+        ASSERT(!di->IsListeningSocket());
         // Close the socket for reading.
-        VOID_NO_RETRY_EXPECTED(shutdown(sd->fd(), SHUT_RD));
+        VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD));
       } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) {
-        ASSERT(!sd->IsListeningSocket());
+        ASSERT(!di->IsListeningSocket());
         // Close the socket for writing.
-        VOID_NO_RETRY_EXPECTED(shutdown(sd->fd(), SHUT_WR));
+        VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR));
       } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
         // Close the socket and free system resources and move on to next
         // message.
-        if (sd->RemovePort(msg[i].dart_port)) {
-          RemoveFromEpollInstance(epoll_fd_, sd);
-          intptr_t fd = sd->fd();
-          sd->Close();
-          socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
-          delete sd;
+        intptr_t old_mask = di->Mask();
+        Dart_Port port = msg[i].dart_port;
+        di->RemovePort(port);
+        intptr_t new_mask = di->Mask();
+        UpdateEpollInstance(old_mask, di);
+
+        intptr_t fd = di->fd();
+        if (di->IsListeningSocket()) {
+          // We only close the socket file descriptor from the operating
+          // system if there are no other dart socket objects which
+          // are listening on the same (address, port) combination.
+          ListeningSocketRegistry *registry =
+              ListeningSocketRegistry::Instance();
+
+          MutexLocker locker(registry->mutex());
+
+          if (registry->CloseSafe(fd)) {
+            ASSERT(new_mask == 0);
+            socket_map_.Remove(GetHashmapKeyFromFd(fd),
+                               GetHashmapHashFromFd(fd));
+            di->Close();
+            delete di;
+          }
+        } else {
+          ASSERT(new_mask == 0);
+          socket_map_.Remove(
+              GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
+          di->Close();
+          delete di;
         }
-        DartUtils::PostInt32(msg[i].dart_port, 1 << kDestroyedEvent);
+
+        DartUtils::PostInt32(port, 1 << kDestroyedEvent);
       } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) {
         int count = TOKEN_COUNT(msg[i].data);
-        if (sd->ReturnToken(msg[i].dart_port, count)) {
-          AddToEpollInstance(epoll_fd_, sd);
-        }
+        intptr_t old_mask = di->Mask();
+        di->ReturnTokens(msg[i].dart_port, count);
+        UpdateEpollInstance(old_mask, di);
       } else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) {
         // `events` can only have kInEvent/kOutEvent flags set.
         intptr_t events = msg[i].data & EVENT_MASK;
         ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
 
-        // Setup events to wait for.
-        if (sd->AddPort(msg[i].dart_port)) {
-          sd->SetMask(events);
-          AddToEpollInstance(epoll_fd_, sd);
-        }
+        intptr_t old_mask = di->Mask();
+        di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK);
+        UpdateEpollInstance(old_mask, di);
       } else {
         UNREACHABLE();
       }
@@ -259,9 +303,9 @@
 #endif
 
 intptr_t EventHandlerImplementation::GetPollEvents(intptr_t events,
-                                                   SocketData* sd) {
+                                                   DescriptorInfo* di) {
 #ifdef DEBUG_POLL
-  PrintEventMask(sd->fd(), events);
+  PrintEventMask(di->fd(), events);
 #endif
   if (events & EPOLLERR) {
     // Return error only if EPOLLIN is present.
@@ -290,15 +334,20 @@
         timeout_queue_.RemoveCurrent();
       }
     } else {
-      SocketData* sd = reinterpret_cast<SocketData*>(events[i].data.ptr);
-      intptr_t event_mask = GetPollEvents(events[i].events, sd);
+      DescriptorInfo* di =
+          reinterpret_cast<DescriptorInfo*>(events[i].data.ptr);
+      intptr_t event_mask = GetPollEvents(events[i].events, di);
+
+      if ((event_mask & (1 << kErrorEvent)) != 0) {
+        di->NotifyAllDartPorts(event_mask);
+      }
+      event_mask &= ~(1 << kErrorEvent);
+
       if (event_mask != 0) {
-        Dart_Port port = sd->port();
-        if (sd->TakeToken()) {
-          // Took last token, remove from epoll.
-          RemoveFromEpollInstance(epoll_fd_, sd);
-        }
+        intptr_t old_mask = di->Mask();
+        Dart_Port port = di->NextNotifyDartPort(event_mask);
         ASSERT(port != 0);
+        UpdateEpollInstance(old_mask, di);
         DartUtils::PostInt32(port, event_mask);
       }
     }
diff --git a/runtime/bin/eventhandler_linux.h b/runtime/bin/eventhandler_linux.h
index 0c5bd26..099bef5 100644
--- a/runtime/bin/eventhandler_linux.h
+++ b/runtime/bin/eventhandler_linux.h
@@ -21,183 +21,36 @@
 namespace dart {
 namespace bin {
 
-class ListeningSocketData;
-class SocketData {
+class DescriptorInfo : public DescriptorInfoBase {
  public:
-  explicit SocketData(intptr_t fd)
-      : fd_(fd), port_(0), mask_(0), tokens_(16) {
-    ASSERT(fd_ != -1);
-  }
+  explicit DescriptorInfo(intptr_t fd) : DescriptorInfoBase(fd) { }
 
-  virtual ~SocketData() {
-  }
+  virtual ~DescriptorInfo() { }
 
   intptr_t GetPollEvents();
 
-  void Close() {
-    port_ = 0;
-    mask_ = 0;
+  virtual void Close() {
     VOID_TEMP_FAILURE_RETRY(close(fd_));
     fd_ = -1;
   }
-
-  void SetMask(intptr_t mask) {
-    ASSERT(fd_ != -1);
-    mask_ = mask;
-  }
-
-  intptr_t fd() { return fd_; }
-  virtual Dart_Port port() { return port_; }
-
-  virtual bool IsListeningSocket() const { return false; }
-
-  virtual bool AddPort(Dart_Port port) {
-    ASSERT(port_ == 0);
-    port_ = port;
-    return true;
-  }
-
-  virtual bool RemovePort(Dart_Port port) {
-    ASSERT(port_ == 0 || port_ == port);
-    return true;
-  }
-
-  // Returns true if the last token was taken.
-  virtual bool TakeToken() {
-    ASSERT(tokens_ > 0);
-    tokens_--;
-    return tokens_ == 0;
-  }
-
-  // Returns true if the tokens was 0 before adding.
-  virtual bool ReturnToken(Dart_Port port, int count) {
-    ASSERT(port_ == port);
-    ASSERT(tokens_ >= 0);
-    bool was_empty = tokens_ == 0;
-    tokens_ += count;
-    return was_empty;
-  }
-
-  bool HasTokens() const { return tokens_ > 0; }
-
- protected:
-  intptr_t fd_;
-  Dart_Port port_;
-  intptr_t mask_;
-  int tokens_;
 };
 
 
-class ListeningSocketData : public SocketData {
- private:
-  static const int kTokenCount = 4;
-
-  static bool SamePortValue(void* key1, void* key2) {
-    return reinterpret_cast<Dart_Port>(key1) ==
-        reinterpret_cast<Dart_Port>(key2);
-  }
-
-  static uint32_t GetHashmapHashFromPort(Dart_Port port) {
-    return static_cast<uint32_t>(port & 0xFFFFFFFF);
-  }
-
-  static void* GetHashmapKeyFromPort(Dart_Port port) {
-    return reinterpret_cast<void*>(port);
-  }
-
+class DescriptorInfoSingle
+    : public DescriptorInfoSingleMixin<DescriptorInfo> {
  public:
-  explicit ListeningSocketData(intptr_t fd)
-      : SocketData(fd),
-        tokens_map_(&SamePortValue, 4) {}
+  explicit DescriptorInfoSingle(intptr_t fd)
+      : DescriptorInfoSingleMixin(fd, false) {}
+  virtual ~DescriptorInfoSingle() {}
+};
 
-  bool IsListeningSocket() const { return true; }
 
-  bool AddPort(Dart_Port port) {
-    HashMap::Entry* entry = tokens_map_.Lookup(
-        GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), true);
-    entry->value = reinterpret_cast<void*>(kTokenCount);
-    return live_ports_.Add(port);
-  }
-
-  virtual bool RemovePort(Dart_Port port) {
-    HashMap::Entry* entry = tokens_map_.Lookup(
-        GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false);
-    if (entry != NULL) {
-      intptr_t tokens = reinterpret_cast<intptr_t>(entry->value);
-      if (tokens == 0) {
-        while (idle_ports_.head() != port) {
-          idle_ports_.Rotate();
-        }
-        idle_ports_.RemoveHead();
-      } else {
-        while (live_ports_.head() != port) {
-          live_ports_.Rotate();
-        }
-        live_ports_.RemoveHead();
-      }
-      tokens_map_.Remove(
-          GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port));
-    } else {
-      // NOTE: This is a listening socket which has been immediately closed.
-      //
-      // If a listening socket is not listened on, the event handler does not
-      // know about it beforehand. So the first time the event handler knows
-      // about it, is when it is supposed to be closed. We therefore do nothing
-      // here.
-      //
-      // But whether to close it, depends on whether other isolates have it open
-      // as well or not.
-    }
-    return !live_ports_.HasHead();
-  }
-
-  bool TakeToken() {
-    ASSERT(live_ports_.HasHead());
-    Dart_Port port = live_ports_.head();
-    HashMap::Entry* entry = tokens_map_.Lookup(
-        GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false);
-    ASSERT(entry != NULL);
-    intptr_t tokens = reinterpret_cast<intptr_t>(entry->value);
-    tokens--;
-    entry->value = reinterpret_cast<void*>(tokens);
-    if (tokens == 0) {
-      live_ports_.RemoveHead();
-      idle_ports_.Add(port);
-      if (!live_ports_.HasHead()) {
-        return true;
-      }
-    } else {
-      live_ports_.Rotate();
-    }
-    return false;
-  }
-
-  Dart_Port port() { return live_ports_.head(); }
-
-  bool ReturnToken(Dart_Port port, int count) {
-    HashMap::Entry* entry = tokens_map_.Lookup(
-        GetHashmapKeyFromPort(port), GetHashmapHashFromPort(port), false);
-    ASSERT(entry != NULL);
-    intptr_t tokens = reinterpret_cast<intptr_t>(entry->value);
-    tokens += count;
-    entry->value = reinterpret_cast<void*>(tokens);
-    if (tokens == count) {
-      // Return to live_ports_.
-      while (idle_ports_.head() != port) {
-        idle_ports_.Rotate();
-      }
-      idle_ports_.RemoveHead();
-      bool was_empty = !live_ports_.HasHead();
-      live_ports_.Add(port);
-      return was_empty;
-    }
-    return false;
-  }
-
- private:
-  CircularLinkedList<Dart_Port> live_ports_;
-  CircularLinkedList<Dart_Port> idle_ports_;
-  HashMap tokens_map_;
+class DescriptorInfoMultiple
+    : public DescriptorInfoMultipleMixin<DescriptorInfo> {
+ public:
+  explicit DescriptorInfoMultiple(intptr_t fd)
+      : DescriptorInfoMultipleMixin(fd, false) {}
+  virtual ~DescriptorInfoMultiple() {}
 };
 
 
@@ -206,9 +59,11 @@
   EventHandlerImplementation();
   ~EventHandlerImplementation();
 
+  void UpdateEpollInstance(intptr_t old_mask, DescriptorInfo *di);
+
   // Gets the socket data structure for a given file
   // descriptor. Creates a new one if one is not found.
-  SocketData* GetSocketData(intptr_t fd, bool is_listening);
+  DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening);
   void SendData(intptr_t id, Dart_Port dart_port, int64_t data);
   void Start(EventHandler* handler);
   void Shutdown();
@@ -219,7 +74,7 @@
   void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data);
   void HandleInterruptFd();
   void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask);
-  intptr_t GetPollEvents(intptr_t events, SocketData* sd);
+  intptr_t GetPollEvents(intptr_t events, DescriptorInfo* di);
   static void* GetHashmapKeyFromFd(intptr_t fd);
   static uint32_t GetHashmapHashFromFd(intptr_t fd);
 
diff --git a/runtime/bin/eventhandler_macos.cc b/runtime/bin/eventhandler_macos.cc
index 911cbad..fa2dbf6 100644
--- a/runtime/bin/eventhandler_macos.cc
+++ b/runtime/bin/eventhandler_macos.cc
@@ -6,6 +6,7 @@
 #if defined(TARGET_OS_MACOS)
 
 #include "bin/eventhandler.h"
+#include "bin/eventhandler_macos.h"
 
 #include <errno.h>  // NOLINT
 #include <pthread.h>  // NOLINT
@@ -17,7 +18,9 @@
 
 #include "bin/dartutils.h"
 #include "bin/fdutils.h"
+#include "bin/lockers.h"
 #include "bin/log.h"
+#include "bin/socket.h"
 #include "bin/thread.h"
 #include "bin/utils.h"
 #include "platform/hashmap.h"
@@ -27,60 +30,64 @@
 namespace dart {
 namespace bin {
 
-bool SocketData::HasReadEvent() {
-  return (mask_ & (1 << kInEvent)) != 0;
+
+bool DescriptorInfo::HasReadEvent() {
+  return (Mask() & (1 << kInEvent)) != 0;
 }
 
 
-bool SocketData::HasWriteEvent() {
-  return (mask_ & (1 << kOutEvent)) != 0;
+bool DescriptorInfo::HasWriteEvent() {
+  return (Mask() & (1 << kOutEvent)) != 0;
 }
 
 
 // Unregister the file descriptor for a SocketData structure with kqueue.
-static void RemoveFromKqueue(intptr_t kqueue_fd_, SocketData* sd) {
-  if (!sd->tracked_by_kqueue()) return;
+static void RemoveFromKqueue(intptr_t kqueue_fd_, DescriptorInfo* di) {
+  if (!di->tracked_by_kqueue()) return;
   static const intptr_t kMaxChanges = 2;
   struct kevent events[kMaxChanges];
-  EV_SET(events, sd->fd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
+  EV_SET(events, di->fd(), EVFILT_READ, EV_DELETE, 0, 0, NULL);
   VOID_NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, 1, NULL, 0, NULL));
-  EV_SET(events, sd->fd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
+  EV_SET(events, di->fd(), EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
   VOID_NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, 1, NULL, 0, NULL));
-  sd->set_tracked_by_kqueue(false);
+  di->set_tracked_by_kqueue(false);
 }
 
 
 // Update the kqueue registration for SocketData structure to reflect
 // the events currently of interest.
-static void AddToKqueue(intptr_t kqueue_fd_, SocketData* sd) {
-  ASSERT(!sd->tracked_by_kqueue());
+static void AddToKqueue(intptr_t kqueue_fd_, DescriptorInfo* di) {
+  ASSERT(!di->tracked_by_kqueue());
   static const intptr_t kMaxChanges = 2;
   intptr_t changes = 0;
   struct kevent events[kMaxChanges];
   int flags = EV_ADD;
-  if (!sd->IsListeningSocket()) {
+  if (!di->IsListeningSocket()) {
     flags |= EV_CLEAR;
   }
+
+  ASSERT(di->HasReadEvent() || di->HasWriteEvent());
+
   // Register or unregister READ filter if needed.
-  if (sd->HasReadEvent()) {
+  if (di->HasReadEvent()) {
     EV_SET(events + changes,
-           sd->fd(),
+           di->fd(),
            EVFILT_READ,
            flags,
            0,
            0,
-           sd);
+           di);
     ++changes;
   }
   // Register or unregister WRITE filter if needed.
-  if (sd->HasWriteEvent()) {
+  if (di->HasWriteEvent()) {
     EV_SET(events + changes,
-           sd->fd(),
+           di->fd(),
            EVFILT_WRITE,
            flags,
            0,
            0,
-           sd);
+           di);
     ++changes;
   }
   ASSERT(changes > 0);
@@ -88,13 +95,15 @@
   int status =
       NO_RETRY_EXPECTED(kevent(kqueue_fd_, events, changes, NULL, 0, NULL));
   if (status == -1) {
+    // TODO(dart:io): Verify that the dart end is handling this correctly.
+
     // kQueue does not accept the file descriptor. It could be due to
     // already closed file descriptor, or unuspported devices, such
     // as /dev/null. In such case, mark the file descriptor as closed,
     // so dart will handle it accordingly.
-    DartUtils::PostInt32(sd->port(), 1 << kCloseEvent);
+    di->NotifyAllDartPorts(1 << kCloseEvent);
   } else {
-    sd->set_tracked_by_kqueue(true);
+    di->set_tracked_by_kqueue(true);
   }
 }
 
@@ -136,21 +145,41 @@
 }
 
 
-SocketData* EventHandlerImplementation::GetSocketData(intptr_t fd,
-                                                      bool is_listening) {
+void EventHandlerImplementation::UpdateKQueueInstance(intptr_t old_mask,
+                                                      DescriptorInfo *di) {
+  intptr_t new_mask = di->Mask();
+  if (old_mask != 0 && new_mask == 0) {
+    RemoveFromKqueue(kqueue_fd_, di);
+  } else if (old_mask == 0 && new_mask != 0) {
+    AddToKqueue(kqueue_fd_, di);
+  } else if (old_mask != 0 && new_mask != 0 && old_mask != new_mask) {
+    ASSERT(!di->IsListeningSocket());
+    RemoveFromKqueue(kqueue_fd_, di);
+    AddToKqueue(kqueue_fd_, di);
+  }
+}
+
+
+DescriptorInfo* EventHandlerImplementation::GetDescriptorInfo(
+    intptr_t fd, bool is_listening) {
   ASSERT(fd >= 0);
   HashMap::Entry* entry = socket_map_.Lookup(
       GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd), true);
   ASSERT(entry != NULL);
-  SocketData* sd = reinterpret_cast<SocketData*>(entry->value);
-  if (sd == NULL) {
+  DescriptorInfo* di =
+      reinterpret_cast<DescriptorInfo*>(entry->value);
+  if (di == NULL) {
     // If there is no data in the hash map for this file descriptor a
-    // new SocketData for the file descriptor is inserted.
-    sd = new SocketData(fd, is_listening);
-    entry->value = sd;
+    // new DescriptorInfo for the file descriptor is inserted.
+    if (is_listening) {
+      di = new DescriptorInfoMultiple(fd);
+    } else {
+      di = new DescriptorInfoSingle(fd);
+    }
+    entry->value = di;
   }
-  ASSERT(fd == sd->fd());
-  return sd;
+  ASSERT(fd == di->fd());
+  return di;
 }
 
 
@@ -188,38 +217,63 @@
     } else {
       ASSERT((msg[i].data & COMMAND_MASK) != 0);
 
-      SocketData* sd = GetSocketData(
+      DescriptorInfo* di = GetDescriptorInfo(
           msg[i].id, IS_LISTENING_SOCKET(msg[i].data));
       if (IS_COMMAND(msg[i].data, kShutdownReadCommand)) {
+        ASSERT(!di->IsListeningSocket());
         // Close the socket for reading.
-        shutdown(sd->fd(), SHUT_RD);
+        VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_RD));
       } else if (IS_COMMAND(msg[i].data, kShutdownWriteCommand)) {
+        ASSERT(!di->IsListeningSocket());
         // Close the socket for writing.
-        shutdown(sd->fd(), SHUT_WR);
+        VOID_NO_RETRY_EXPECTED(shutdown(di->fd(), SHUT_WR));
       } else if (IS_COMMAND(msg[i].data, kCloseCommand)) {
-        // Close the socket and free system resources.
-        RemoveFromKqueue(kqueue_fd_, sd);
-        intptr_t fd = sd->fd();
-        VOID_TEMP_FAILURE_RETRY(close(fd));
-        socket_map_.Remove(GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
-        delete sd;
-        DartUtils::PostInt32(msg[i].dart_port, 1 << kDestroyedEvent);
-      } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) {
-        int count = TOKEN_COUNT(msg[i].data);
-        for (int i = 0; i < count; i++) {
-          if (sd->ReturnToken()) {
-            AddToKqueue(kqueue_fd_, sd);
+        // Close the socket and free system resources and move on to next
+        // message.
+        intptr_t old_mask = di->Mask();
+        Dart_Port port = msg[i].dart_port;
+        di->RemovePort(port);
+        intptr_t new_mask = di->Mask();
+        UpdateKQueueInstance(old_mask, di);
+
+        intptr_t fd = di->fd();
+        if (di->IsListeningSocket()) {
+          // We only close the socket file descriptor from the operating
+          // system if there are no other dart socket objects which
+          // are listening on the same (address, port) combination.
+          ListeningSocketRegistry *registry =
+              ListeningSocketRegistry::Instance();
+
+          MutexLocker locker(registry->mutex());
+
+          if (registry->CloseSafe(fd)) {
+            ASSERT(new_mask == 0);
+            socket_map_.Remove(GetHashmapKeyFromFd(fd),
+                               GetHashmapHashFromFd(fd));
+            di->Close();
+            delete di;
           }
+        } else {
+          ASSERT(new_mask == 0);
+          socket_map_.Remove(
+              GetHashmapKeyFromFd(fd), GetHashmapHashFromFd(fd));
+          di->Close();
+          delete di;
         }
+
+        DartUtils::PostInt32(port, 1 << kDestroyedEvent);
+      } else if (IS_COMMAND(msg[i].data, kReturnTokenCommand)) {
+        intptr_t old_mask = di->Mask();
+        di->ReturnTokens(msg[i].dart_port, TOKEN_COUNT(msg[i].data));
+        UpdateKQueueInstance(old_mask, di);
       } else if (IS_COMMAND(msg[i].data, kSetEventMaskCommand)) {
         // `events` can only have kInEvent/kOutEvent flags set.
         intptr_t events = msg[i].data & EVENT_MASK;
         ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
 
-        // Setup events to wait for.
-        ASSERT(sd->port() == 0);
-        sd->SetPortAndMask(msg[i].dart_port, events);
-        AddToKqueue(kqueue_fd_, sd);
+        intptr_t old_mask = di->Mask();
+        di->SetPortAndMask(msg[i].dart_port, msg[i].data & EVENT_MASK);
+        UpdateKQueueInstance(old_mask, di);
       } else {
         UNREACHABLE();
       }
@@ -249,12 +303,12 @@
 
 
 intptr_t EventHandlerImplementation::GetEvents(struct kevent* event,
-                                               SocketData* sd) {
+                                               DescriptorInfo* di) {
 #ifdef DEBUG_KQUEUE
-  PrintEventMask(sd->fd(), event);
+  PrintEventMask(di->fd(), event);
 #endif
   intptr_t event_mask = 0;
-  if (sd->IsListeningSocket()) {
+  if (di->IsListeningSocket()) {
     // On a listening socket the READ event means that there are
     // connections ready to be accepted.
     if (event->filter == EVFILT_READ) {
@@ -310,15 +364,19 @@
     if (events[i].udata == NULL) {
       interrupt_seen = true;
     } else {
-      SocketData* sd = reinterpret_cast<SocketData*>(events[i].udata);
-      intptr_t event_mask = GetEvents(events + i, sd);
+      DescriptorInfo* di =
+          reinterpret_cast<DescriptorInfo*>(events[i].udata);
+      intptr_t event_mask = GetEvents(events + i, di);
+      if ((event_mask & (1 << kErrorEvent)) != 0) {
+        di->NotifyAllDartPorts(event_mask);
+      }
+      event_mask &= ~(1 << kErrorEvent);
+
       if (event_mask != 0) {
-        if (sd->TakeToken()) {
-          // Took last token, remove from epoll.
-          RemoveFromKqueue(kqueue_fd_, sd);
-        }
-        Dart_Port port = sd->port();
+        intptr_t old_mask = di->Mask();
+        Dart_Port port = di->NextNotifyDartPort(event_mask);
         ASSERT(port != 0);
+        UpdateKQueueInstance(old_mask, di);
         DartUtils::PostInt32(port, event_mask);
       }
     }
diff --git a/runtime/bin/eventhandler_macos.h b/runtime/bin/eventhandler_macos.h
index ebfe251..48b0b96 100644
--- a/runtime/bin/eventhandler_macos.h
+++ b/runtime/bin/eventhandler_macos.h
@@ -21,56 +21,50 @@
 namespace dart {
 namespace bin {
 
-class SocketData {
+class DescriptorInfo : public DescriptorInfoBase {
  public:
-  explicit SocketData(intptr_t fd, bool is_listening)
-      : fd_(fd),
-        port_(0),
-        mask_(0),
-        tracked_by_kqueue_(false),
-        tokens_(16),
-        is_listening_(is_listening) {
-    ASSERT(fd_ != -1);
+  explicit DescriptorInfo(intptr_t fd)
+      : DescriptorInfoBase(fd), tracked_by_kqueue_(false) { }
+
+  virtual ~DescriptorInfo() { }
+
+  intptr_t GetPollEvents();
+
+  virtual void Close() {
+    VOID_TEMP_FAILURE_RETRY(close(fd_));
+    fd_ = -1;
   }
 
-  bool HasReadEvent();
-  bool HasWriteEvent();
-
-  bool IsListeningSocket() { return is_listening_; }
-
-  void SetPortAndMask(Dart_Port port, intptr_t mask) {
-    ASSERT(fd_ != -1);
-    port_ = port;
-    mask_ = mask;
-  }
-
-  intptr_t fd() { return fd_; }
-  Dart_Port port() { return port_; }
-  intptr_t mask() { return mask_; }
-  bool tracked_by_kqueue() { return tracked_by_kqueue_; }
   void set_tracked_by_kqueue(bool value) {
     tracked_by_kqueue_ = value;
   }
 
-  // Returns true if the last token was taken.
-  bool TakeToken() {
-    tokens_--;
-    return tokens_ == 0;
-  }
+  bool tracked_by_kqueue() { return tracked_by_kqueue_; }
 
-  // Returns true if the tokens was 0 before adding.
-  bool ReturnToken() {
-    tokens_++;
-    return tokens_ == 1;
-  }
+  bool HasReadEvent();
 
- private:
-  intptr_t fd_;
-  Dart_Port port_;
-  intptr_t mask_;
+  bool HasWriteEvent();
+
+ protected:
   bool tracked_by_kqueue_;
-  int tokens_;
-  bool is_listening_;
+};
+
+
+class DescriptorInfoSingle
+    : public DescriptorInfoSingleMixin<DescriptorInfo> {
+ public:
+  explicit DescriptorInfoSingle(intptr_t fd)
+      : DescriptorInfoSingleMixin(fd, false) {}
+  virtual ~DescriptorInfoSingle() {}
+};
+
+
+class DescriptorInfoMultiple
+    : public DescriptorInfoMultipleMixin<DescriptorInfo> {
+ public:
+  explicit DescriptorInfoMultiple(intptr_t fd)
+      : DescriptorInfoMultipleMixin(fd, false) {}
+  virtual ~DescriptorInfoMultiple() {}
 };
 
 
@@ -79,9 +73,11 @@
   EventHandlerImplementation();
   ~EventHandlerImplementation();
 
+  void UpdateKQueueInstance(intptr_t old_mask, DescriptorInfo *di);
+
   // Gets the socket data structure for a given file
   // descriptor. Creates a new one if one is not found.
-  SocketData* GetSocketData(intptr_t fd, bool is_listening);
+  DescriptorInfo* GetDescriptorInfo(intptr_t fd, bool is_listening);
   void SendData(intptr_t id, Dart_Port dart_port, int64_t data);
   void Start(EventHandler* handler);
   void Shutdown();
@@ -94,7 +90,7 @@
   void WakeupHandler(intptr_t id, Dart_Port dart_port, int64_t data);
   void HandleInterruptFd();
   void SetPort(intptr_t fd, Dart_Port dart_port, intptr_t mask);
-  intptr_t GetEvents(struct kevent* event, SocketData* sd);
+  intptr_t GetEvents(struct kevent* event, DescriptorInfo* di);
   static void* GetHashmapKeyFromFd(intptr_t fd);
   static uint32_t GetHashmapHashFromFd(intptr_t fd);
 
diff --git a/runtime/bin/eventhandler_test.cc b/runtime/bin/eventhandler_test.cc
new file mode 100644
index 0000000..098113d
--- /dev/null
+++ b/runtime/bin/eventhandler_test.cc
@@ -0,0 +1,91 @@
+// 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.
+
+#include "bin/eventhandler.h"
+#include "platform/assert.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+namespace bin {
+
+UNIT_TEST_CASE(CircularLinkedList) {
+  CircularLinkedList<int> list;
+
+  EXPECT(!list.HasHead());
+
+  list.Add(1);
+  EXPECT(list.HasHead());
+  EXPECT(list.head() == 1);
+
+
+  // Test: Inserts don't move head.
+  for (int i = 2; i <= 100; i++) {
+    list.Add(i);
+    EXPECT(list.head() == 1);
+  }
+
+
+  // Test: Rotate cycle through all elements in insertion order.
+  for (int i = 1; i <= 100; i++) {
+    EXPECT(list.HasHead());
+    EXPECT(list.head() == i);
+    list.Rotate();
+  }
+
+
+  // Test: Removing head results in next element to be head.
+  for (int i = 1; i <= 100; i++) {
+    list.RemoveHead();
+    for (int j = i + 1; j <= 100; j++) {
+      EXPECT(list.HasHead());
+      EXPECT(list.head() == j);
+      list.Rotate();
+    }
+  }
+
+  // Test: Removing all items individually make list empty.
+  EXPECT(!list.HasHead());
+
+
+  // Test: Removing all items at once makes list empty.
+  for (int i = 1; i <= 100; i++) {
+    list.Add(i);
+  }
+  list.RemoveAll();
+  EXPECT(!list.HasHead());
+
+
+  // Test: Remove individual items just deletes them without modifying head.
+  for (int i = 1; i <= 10; i++) {
+    list.Add(i);
+  }
+  for (int i = 2; i <= 9; i++) {
+    list.Remove(i);
+  }
+  EXPECT(list.head() == 1);
+  list.Rotate();
+  EXPECT(list.head() == 10);
+  list.Rotate();
+  EXPECT(list.head() == 1);
+
+
+  // Test: Remove non-existent element leaves list un-changed.
+  list.Remove(4242);
+  EXPECT(list.head() == 1);
+
+
+  // Test: Remove head element individually moves head to next element.
+  list.Remove(1);
+  EXPECT(list.HasHead());
+  EXPECT(list.head() == 10);
+  list.Remove(10);
+  EXPECT(!list.HasHead());
+
+
+  // Test: Remove non-existent element from empty list works.
+  list.Remove(4242);
+}
+
+}  // namespace bin
+}  // namespace dart
diff --git a/runtime/bin/eventhandler_win.cc b/runtime/bin/eventhandler_win.cc
index 61b6f44..73c738d 100644
--- a/runtime/bin/eventhandler_win.cc
+++ b/runtime/bin/eventhandler_win.cc
@@ -6,6 +6,7 @@
 #if defined(TARGET_OS_WINDOWS)
 
 #include "bin/eventhandler.h"
+#include "bin/eventhandler_win.h"
 
 #include <winsock2.h>  // NOLINT
 #include <ws2tcpip.h>  // NOLINT
@@ -111,26 +112,9 @@
   return data_length_ - index_;
 }
 
-
-Handle::Handle(HANDLE handle)
-    : handle_(reinterpret_cast<HANDLE>(handle)),
-      port_(0),
-      mask_(0),
-      completion_port_(INVALID_HANDLE_VALUE),
-      event_handler_(NULL),
-      data_ready_(NULL),
-      pending_read_(NULL),
-      pending_write_(NULL),
-      last_error_(NOERROR),
-      flags_(0) {
-  InitializeCriticalSection(&cs_);
-}
-
-
-Handle::Handle(HANDLE handle, Dart_Port port)
-    : handle_(reinterpret_cast<HANDLE>(handle)),
-      port_(port),
-      mask_(0),
+Handle::Handle(intptr_t handle)
+    : DescriptorInfoBase(handle),
+      handle_(reinterpret_cast<HANDLE>(handle)),
       completion_port_(INVALID_HANDLE_VALUE),
       event_handler_(NULL),
       data_ready_(NULL),
@@ -289,7 +273,7 @@
     // Completing asynchronously through thread.
     pending_read_ = buffer;
     int result = Thread::Start(ReadFileThread,
-                                     reinterpret_cast<uword>(this));
+                               reinterpret_cast<uword>(this));
     if (result != 0) {
       FATAL1("Failed to start read file thread %d", result);
     }
@@ -335,7 +319,7 @@
 static void HandleClosed(Handle* handle) {
   if (!handle->IsClosing()) {
     int event_mask = 1 << kCloseEvent;
-    DartUtils::PostInt32(handle->port(), event_mask);
+    handle->NotifyAllDartPorts(event_mask);
   }
 }
 
@@ -344,10 +328,7 @@
   handle->set_last_error(WSAGetLastError());
   handle->MarkError();
   if (!handle->IsClosing()) {
-    Dart_Port port = handle->port();
-    if (port != ILLEGAL_PORT) {
-      DartUtils::PostInt32(port, 1 << kErrorEvent);
-    }
+    handle->NotifyAllDartPorts(1 << kErrorEvent);
   }
 }
 
@@ -463,6 +444,7 @@
 
 bool ListenSocket::IssueAccept() {
   ScopedLock lock(this);
+
   // For AcceptEx there needs to be buffer storage for address
   // information for two addresses (local and remote address). The
   // AcceptEx documentation says: "This value must be at least 16
@@ -511,7 +493,7 @@
                         reinterpret_cast<char*>(&s), sizeof(s));
     if (rc == NO_ERROR) {
       // Insert the accepted socket into the list.
-      ClientSocket* client_socket = new ClientSocket(buffer->client(), 0);
+      ClientSocket* client_socket = new ClientSocket(buffer->client());
       client_socket->mark_connected();
       client_socket->CreateCompletionPort(completion_port);
       if (accepted_head_ == NULL) {
@@ -522,6 +504,7 @@
         accepted_tail_->set_next(client_socket);
         accepted_tail_ = client_socket;
       }
+      accepted_count_++;
     } else {
       closesocket(buffer->client());
     }
@@ -537,11 +520,9 @@
 
 static void DeleteIfClosed(Handle* handle) {
   if (handle->IsClosed()) {
-    Dart_Port port = handle->port();
+    handle->NotifyAllDartPorts(1 << kDestroyedEvent);
+    handle->RemoveAllPorts();
     delete handle;
-    if (port != ILLEGAL_PORT) {
-      DartUtils::PostInt32(port, 1 << kDestroyedEvent);
-    }
   }
 }
 
@@ -570,16 +551,23 @@
 
 ClientSocket* ListenSocket::Accept() {
   ScopedLock lock(this);
-  if (accepted_head_ == NULL) return NULL;
-  ClientSocket* result = accepted_head_;
-  accepted_head_ = accepted_head_->next();
-  if (accepted_head_ == NULL) accepted_tail_ = NULL;
-  result->set_next(NULL);
+
+  ClientSocket *result = NULL;
+
+  if (accepted_head_ != NULL) {
+    result = accepted_head_;
+    accepted_head_ = accepted_head_->next();
+    if (accepted_head_ == NULL) accepted_tail_ = NULL;
+    result->set_next(NULL);
+    accepted_count_--;
+  }
+
   if (!IsClosing()) {
     if (!IssueAccept()) {
       HandleError(this);
     }
   }
+
   return result;
 }
 
@@ -876,9 +864,8 @@
   if (ok || WSAGetLastError() != WSA_IO_PENDING) {
     DisconnectComplete(buffer);
   }
-  Dart_Port p = port();
-  if (p != ILLEGAL_PORT) DartUtils::PostInt32(p, 1 << kDestroyedEvent);
-  port_ = ILLEGAL_PORT;
+  NotifyAllDartPorts(1 << kDestroyedEvent);
+  RemoveAllPorts();
 }
 
 
@@ -888,7 +875,7 @@
   if (data_ready_ != NULL) {
     OverlappedBuffer::DisposeBuffer(data_ready_);
   }
-  closed_ = true;
+  mark_closed();
 }
 
 
@@ -896,16 +883,14 @@
   OverlappedBuffer::DisposeBuffer(buffer);
   // Update socket to support full socket API, after ConnectEx completed.
   setsockopt(socket(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0);
-  Dart_Port p = port();
-  if (p != ILLEGAL_PORT) {
-    // If the port is set, we already listen for this socket in Dart.
-    // Handle the cases here.
-    if (!IsClosedRead()) {
-      IssueRead();
-    }
-    if (!IsClosedWrite()) {
-      DartUtils::PostInt32(p, 1 << kOutEvent);
-    }
+  // If the port is set, we already listen for this socket in Dart.
+  // Handle the cases here.
+  if (!IsClosedRead() && ((Mask() & (1 << kInEvent)) != 0)) {
+    IssueRead();
+  }
+  if (!IsClosedWrite() && ((Mask() & (1 << kOutEvent)) != 0)) {
+    Dart_Port port = NextNotifyDartPort(1 << kOutEvent);
+    DartUtils::PostInt32(port, 1 << kOutEvent);
   }
 }
 
@@ -1016,6 +1001,7 @@
   } else {
     Handle* handle = reinterpret_cast<Handle*>(msg->id);
     ASSERT(handle != NULL);
+
     if (handle->is_listen_socket()) {
       ListenSocket* listen_socket =
           reinterpret_cast<ListenSocket*>(handle);
@@ -1024,26 +1010,29 @@
       Handle::ScopedLock lock(listen_socket);
 
       if (IS_COMMAND(msg->data, kReturnTokenCommand)) {
-        // No tokens to return on Windows.
+        listen_socket->ReturnTokens(msg->dart_port, TOKEN_COUNT(msg->data));
       } else if (IS_COMMAND(msg->data, kSetEventMaskCommand)) {
         // `events` can only have kInEvent/kOutEvent flags set.
         intptr_t events = msg->data & EVENT_MASK;
         ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
-
-        listen_socket->SetPortAndMask(msg->dart_port, msg->data);
-
-        // If incoming connections are requested make sure to post already
-        // accepted connections.
-        if ((events & (1 << kInEvent)) != 0) {
-          if (listen_socket->CanAccept()) {
-            int event_mask = (1 << kInEvent);
-            handle->set_mask(handle->mask() & ~event_mask);
-            DartUtils::PostInt32(handle->port(), event_mask);
-          }
-        }
+        listen_socket->SetPortAndMask(msg->dart_port, events);
+        TryDispatchingPendingAccepts(listen_socket);
       } else if (IS_COMMAND(msg->data, kCloseCommand)) {
-        handle->SetPortAndMask(msg->dart_port, msg->data);
-        handle->Close();
+        listen_socket->RemovePort(msg->dart_port);
+
+        // We only close the socket file descriptor from the operating
+        // system if there are no other dart socket objects which
+        // are listening on the same (address, port) combination.
+        ListeningSocketRegistry *registry =
+            ListeningSocketRegistry::Instance();
+        MutexLocker locker(registry->mutex());
+        if (registry->CloseSafe(reinterpret_cast<intptr_t>(listen_socket))) {
+          ASSERT(listen_socket->Mask() == 0);
+          listen_socket->Close();
+          DeleteIfClosed(handle);
+        }
+
+        DartUtils::PostInt32(msg->dart_port, 1 << kDestroyedEvent);
       } else {
         UNREACHABLE();
       }
@@ -1053,16 +1042,16 @@
       Handle::ScopedLock lock(handle);
 
       if (IS_COMMAND(msg->data, kReturnTokenCommand)) {
-        // No tokens to return on Windows.
+        handle->ReturnTokens(msg->dart_port, TOKEN_COUNT(msg->data));
       } else if (IS_COMMAND(msg->data, kSetEventMaskCommand)) {
         // `events` can only have kInEvent/kOutEvent flags set.
         intptr_t events = msg->data & EVENT_MASK;
         ASSERT(0 == (events & ~(1 << kInEvent | 1 << kOutEvent)));
 
-        handle->SetPortAndMask(msg->dart_port, msg->data);
+        handle->SetPortAndMask(msg->dart_port, events);
 
         // Issue a read.
-        if ((msg->data & (1 << kInEvent)) != 0) {
+        if ((handle->Mask() & (1 << kInEvent)) != 0) {
           if (handle->is_datagram_socket()) {
             handle->IssueRecvFrom();
           } else if (handle->is_client_socket()) {
@@ -1077,14 +1066,22 @@
         // If out events (can write events) have been requested, and there
         // are no pending writes, meaning any writes are already complete,
         // post an out event immediately.
-        if ((msg->data & (1 << kOutEvent)) != 0) {
+        if ((events & (1 << kOutEvent)) != 0) {
           if (!handle->HasPendingWrite()) {
             if (handle->is_client_socket()) {
               if (reinterpret_cast<ClientSocket*>(handle)->is_connected()) {
-                DartUtils::PostInt32(handle->port(), 1 << kOutEvent);
+                intptr_t event_mask = 1 << kOutEvent;
+                if ((handle->Mask() & event_mask) != 0) {
+                  Dart_Port port = handle->NextNotifyDartPort(event_mask);
+                  DartUtils::PostInt32(port, event_mask);
+                }
               }
             } else {
-              DartUtils::PostInt32(handle->port(), 1 << kOutEvent);
+              intptr_t event_mask = 1 << kOutEvent;
+              if ((handle->Mask() & event_mask) != 0) {
+                Dart_Port port = handle->NextNotifyDartPort(event_mask);
+                DartUtils::PostInt32(port, event_mask);
+              }
             }
           }
         }
@@ -1092,25 +1089,20 @@
         ASSERT(handle->is_client_socket());
 
         ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(handle);
-        if ((msg->data & (1 << kShutdownReadCommand)) != 0) {
-          client_socket->Shutdown(SD_RECEIVE);
-        }
+        client_socket->Shutdown(SD_RECEIVE);
       } else if (IS_COMMAND(msg->data, kShutdownWriteCommand)) {
         ASSERT(handle->is_client_socket());
 
         ClientSocket* client_socket = reinterpret_cast<ClientSocket*>(handle);
-        if ((msg->data & (1 << kShutdownWriteCommand)) != 0) {
-          client_socket->Shutdown(SD_SEND);
-        }
+        client_socket->Shutdown(SD_SEND);
       } else if (IS_COMMAND(msg->data, kCloseCommand)) {
-        handle->SetPortAndMask(msg->dart_port, msg->data);
+        handle->SetPortAndMask(msg->dart_port, 0);
         handle->Close();
       } else {
         UNREACHABLE();
       }
+      DeleteIfClosed(handle);
     }
-
-    DeleteIfClosed(handle);
   }
 }
 
@@ -1119,17 +1111,30 @@
                                               OverlappedBuffer* buffer) {
   listen_socket->AcceptComplete(buffer, completion_port_);
 
-  if (!listen_socket->IsClosing()) {
-    int event_mask = 1 << kInEvent;
-    if ((listen_socket->mask() & event_mask) != 0) {
-      DartUtils::PostInt32(listen_socket->port(), event_mask);
-    }
+  {
+    Handle::ScopedLock lock(listen_socket);
+    TryDispatchingPendingAccepts(listen_socket);
   }
 
   DeleteIfClosed(listen_socket);
 }
 
 
+void EventHandlerImplementation::TryDispatchingPendingAccepts(
+    ListenSocket *listen_socket) {
+  if (!listen_socket->IsClosing() && listen_socket->CanAccept()) {
+    intptr_t event_mask = 1 << kInEvent;
+    for (int i = 0;
+         i < listen_socket->accepted_count() &&
+         listen_socket->Mask() == event_mask;
+         i++) {
+      Dart_Port port = listen_socket->NextNotifyDartPort(event_mask);
+      DartUtils::PostInt32(port, event_mask);
+    }
+  }
+}
+
+
 void EventHandlerImplementation::HandleRead(Handle* handle,
                                             int bytes,
                                             OverlappedBuffer* buffer) {
@@ -1138,8 +1143,9 @@
   if (bytes > 0) {
     if (!handle->IsClosing()) {
       int event_mask = 1 << kInEvent;
-      if ((handle->mask() & event_mask) != 0) {
-        DartUtils::PostInt32(handle->port(), event_mask);
+      if ((handle->Mask() & event_mask) != 0) {
+        Dart_Port port = handle->NextNotifyDartPort(event_mask);
+        DartUtils::PostInt32(port, event_mask);
       }
     }
   } else {
@@ -1163,8 +1169,9 @@
   handle->ReadComplete(buffer);
   if (!handle->IsClosing()) {
     int event_mask = 1 << kInEvent;
-    if ((handle->mask() & event_mask) != 0) {
-      DartUtils::PostInt32(handle->port(), event_mask);
+    if ((handle->Mask() & event_mask) != 0) {
+        Dart_Port port = handle->NextNotifyDartPort(event_mask);
+      DartUtils::PostInt32(port, event_mask);
     }
   }
 
@@ -1182,8 +1189,9 @@
       int event_mask = 1 << kOutEvent;
       ASSERT(!handle->is_client_socket() ||
              reinterpret_cast<ClientSocket*>(handle)->is_connected());
-      if ((handle->mask() & event_mask) != 0) {
-        DartUtils::PostInt32(handle->port(), event_mask);
+      if ((handle->Mask() & event_mask) != 0) {
+        Dart_Port port = handle->NextNotifyDartPort(event_mask);
+        DartUtils::PostInt32(port, event_mask);
       }
     }
   } else {
diff --git a/runtime/bin/eventhandler_win.h b/runtime/bin/eventhandler_win.h
index 3a9a1f4..bfd02f9 100644
--- a/runtime/bin/eventhandler_win.h
+++ b/runtime/bin/eventhandler_win.h
@@ -154,7 +154,7 @@
 
 // Abstract super class for holding information on listen and connected
 // sockets.
-class Handle {
+class Handle : public DescriptorInfoBase {
  public:
   enum Type {
     kFile,
@@ -218,7 +218,6 @@
     EventHandlerImplementation* event_handler) = 0;
 
   HANDLE handle() { return handle_; }
-  Dart_Port port() { return port_; }
 
   void Lock();
   void Unlock();
@@ -231,10 +230,6 @@
 
   bool IsHandleClosed() const { return handle_ == INVALID_HANDLE_VALUE; }
 
-  void SetPortAndMask(Dart_Port port, intptr_t mask) {
-    port_ = port;
-    mask_ = mask;
-  }
   Type type() { return type_; }
   bool is_file() { return type_ == kFile; }
   bool is_socket() { return type_ == kListenSocket ||
@@ -243,8 +238,6 @@
   bool is_listen_socket() { return type_ == kListenSocket; }
   bool is_client_socket() { return type_ == kClientSocket; }
   bool is_datagram_socket() { return type_ == kDatagramSocket; }
-  void set_mask(intptr_t mask) { mask_ = mask; }
-  intptr_t mask() { return mask_; }
 
   void MarkDoesNotSupportOverlappedIO() {
     flags_ |= (1 << kDoesNotSupportOverlappedIO);
@@ -267,15 +260,12 @@
     kError = 4
   };
 
-  explicit Handle(HANDLE handle);
-  Handle(HANDLE handle, Dart_Port port);
+  explicit Handle(intptr_t handle);
 
   virtual void HandleIssueError();
 
   Type type_;
   HANDLE handle_;
-  Dart_Port port_;  // Dart port to communicate events for this socket.
-  intptr_t mask_;  // Mask of events to report through the port.
   HANDLE completion_port_;
   EventHandlerImplementation* event_handler_;
 
@@ -291,12 +281,12 @@
 };
 
 
-class FileHandle : public Handle {
+class FileHandle : public DescriptorInfoSingleMixin<Handle> {
  public:
   explicit FileHandle(HANDLE handle)
-      : Handle(handle) { type_ = kFile; }
-  FileHandle(HANDLE handle, Dart_Port port)
-      : Handle(handle, port) { type_ = kFile; }
+      : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(handle), true) {
+    type_ = kFile;
+  }
 
   virtual void EnsureInitialized(EventHandlerImplementation* event_handler);
   virtual bool IsClosed();
@@ -332,10 +322,10 @@
 };
 
 
-class DirectoryWatchHandle : public Handle {
+class DirectoryWatchHandle : public DescriptorInfoSingleMixin<Handle> {
  public:
   DirectoryWatchHandle(HANDLE handle, int events, bool recursive)
-      : Handle(handle),
+      : DescriptorInfoSingleMixin(reinterpret_cast<intptr_t>(handle), true),
         events_(events),
         recursive_(recursive) {
     type_ = kDirectoryWatch;
@@ -359,11 +349,8 @@
   SOCKET socket() const { return socket_; }
 
  protected:
-  explicit SocketHandle(SOCKET s)
-      : Handle(reinterpret_cast<HANDLE>(s)),
-        socket_(s) {}
-  SocketHandle(SOCKET s, Dart_Port port)
-      : Handle(reinterpret_cast<HANDLE>(s), port),
+  explicit SocketHandle(intptr_t s)
+      : Handle(s),
         socket_(s) {}
 
   virtual void HandleIssueError();
@@ -374,13 +361,14 @@
 
 
 // Information on listen sockets.
-class ListenSocket : public SocketHandle {
+class ListenSocket : public DescriptorInfoMultipleMixin<SocketHandle> {
  public:
-  explicit ListenSocket(SOCKET s) : SocketHandle(s),
-                                    AcceptEx_(NULL),
-                                    pending_accept_count_(0),
-                                    accepted_head_(NULL),
-                                    accepted_tail_(NULL) {
+  explicit ListenSocket(intptr_t s) : DescriptorInfoMultipleMixin(s, true),
+                                      AcceptEx_(NULL),
+                                      pending_accept_count_(0),
+                                      accepted_head_(NULL),
+                                      accepted_tail_(NULL),
+                                      accepted_count_(0) {
     type_ = kListenSocket;
   }
   virtual ~ListenSocket() {
@@ -405,35 +393,36 @@
 
   int pending_accept_count() { return pending_accept_count_; }
 
+  int accepted_count() { return accepted_count_; }
+
  private:
   bool LoadAcceptEx();
 
   LPFN_ACCEPTEX AcceptEx_;
+
+  // The number of asynchronous `IssueAccept` operations which haven't completed
+  // yet.
   int pending_accept_count_;
+
   // Linked list of accepted connections provided by completion code. Ready to
   // be handed over through accept.
   ClientSocket* accepted_head_;
   ClientSocket* accepted_tail_;
+
+  // The number of accepted connections which are waiting to be removed from
+  // this queue and processed by dart isolates.
+  int accepted_count_;
 };
 
 
 // Information on connected sockets.
-class ClientSocket : public SocketHandle {
+class ClientSocket : public DescriptorInfoSingleMixin<SocketHandle> {
  public:
-  explicit ClientSocket(SOCKET s) : SocketHandle(s),
-                                    DisconnectEx_(NULL),
-                                    next_(NULL),
-                                    connected_(false),
-                                    closed_(false) {
-    LoadDisconnectEx();
-    type_ = kClientSocket;
-  }
-
-  ClientSocket(SOCKET s, Dart_Port port) : SocketHandle(s, port),
-                                           DisconnectEx_(NULL),
-                                           next_(NULL),
-                                           connected_(false),
-                                           closed_(false) {
+  explicit ClientSocket(intptr_t s) : DescriptorInfoSingleMixin(s, true),
+                                      DisconnectEx_(NULL),
+                                      next_(NULL),
+                                      connected_(false),
+                                      closed_(false) {
     LoadDisconnectEx();
     type_ = kClientSocket;
   }
@@ -469,6 +458,10 @@
   }
   bool is_connected() const { return connected_; }
 
+  void mark_closed() {
+    closed_ = true;
+  }
+
  private:
   bool LoadDisconnectEx();
 
@@ -479,9 +472,9 @@
 };
 
 
-class DatagramSocket : public SocketHandle {
+class DatagramSocket : public DescriptorInfoSingleMixin<SocketHandle> {
  public:
-  explicit DatagramSocket(SOCKET s) : SocketHandle(s) {
+  explicit DatagramSocket(intptr_t s) : DescriptorInfoSingleMixin(s, true) {
     type_ = kDatagramSocket;
   }
 
@@ -516,6 +509,7 @@
   void HandleInterrupt(InterruptMessage* msg);
   void HandleTimeout();
   void HandleAccept(ListenSocket* listen_socket, OverlappedBuffer* buffer);
+  void TryDispatchingPendingAccepts(ListenSocket *listen_socket);
   void HandleRead(Handle* handle, int bytes, OverlappedBuffer* buffer);
   void HandleRecvFrom(Handle* handle, int bytes, OverlappedBuffer* buffer);
   void HandleWrite(Handle* handle, int bytes, OverlappedBuffer* buffer);
diff --git a/runtime/bin/file_system_watcher_macos.cc b/runtime/bin/file_system_watcher_macos.cc
index 93177f2..2e24122 100644
--- a/runtime/bin/file_system_watcher_macos.cc
+++ b/runtime/bin/file_system_watcher_macos.cc
@@ -264,8 +264,8 @@
   Node* AddPath(const char* path, int events, bool recursive) {
     int fds[2];
     VOID_NO_RETRY_EXPECTED(pipe(fds));
-    Socket::SetNonBlocking(fds[0]);
-    Socket::SetBlocking(fds[1]);
+    FDUtils::SetNonBlocking(fds[0]);
+    FDUtils::SetBlocking(fds[1]);
 
     char base_path[PATH_MAX];
     realpath(path, base_path);
@@ -393,5 +393,3 @@
 }  // namespace dart
 
 #endif  // defined(TARGET_OS_MACOS)
-
-
diff --git a/runtime/bin/io_natives.cc b/runtime/bin/io_natives.cc
index 9756284..cbe0c83 100644
--- a/runtime/bin/io_natives.cc
+++ b/runtime/bin/io_natives.cc
@@ -66,8 +66,9 @@
   V(SecureSocket_RegisterHandshakeCompleteCallback, 2)                         \
   V(SecureSocket_Renegotiate, 4)                                               \
   V(ServerSocket_Accept, 2)                                                    \
-  V(ServerSocket_CreateBindListen, 5)                                          \
+  V(ServerSocket_CreateBindListen, 6)                                          \
   V(Socket_CreateConnect, 3)                                                   \
+  V(Socket_CreateBindConnect, 4)                                               \
   V(Socket_CreateBindDatagram, 4)                                              \
   V(Socket_Available, 1)                                                       \
   V(Socket_Read, 2)                                                            \
@@ -83,6 +84,7 @@
   V(Socket_SetOption, 4)                                                       \
   V(Socket_JoinMulticast, 4)                                                   \
   V(Socket_LeaveMulticast, 4)                                                  \
+  V(Socket_MarkSocketAsSharedHack, 1)                                          \
   V(Socket_GetSocketId, 1)                                                     \
   V(Socket_SetSocketId, 2)                                                     \
   V(Stdin_ReadByte, 1)                                                         \
diff --git a/runtime/bin/socket.cc b/runtime/bin/socket.cc
index dcacf6a..b8e8766 100644
--- a/runtime/bin/socket.cc
+++ b/runtime/bin/socket.cc
@@ -7,6 +7,7 @@
 #include "bin/dartutils.h"
 #include "bin/socket.h"
 #include "bin/thread.h"
+#include "bin/lockers.h"
 #include "bin/utils.h"
 
 #include "platform/globals.h"
@@ -19,6 +20,170 @@
 
 static const int kSocketIdNativeField = 0;
 
+
+ListeningSocketRegistry *globalTcpListeningSocketRegistry = NULL;
+
+
+void ListeningSocketRegistry::Initialize() {
+  ASSERT(globalTcpListeningSocketRegistry == NULL);
+  globalTcpListeningSocketRegistry = new ListeningSocketRegistry();
+}
+
+
+ListeningSocketRegistry *ListeningSocketRegistry::Instance() {
+  return globalTcpListeningSocketRegistry;
+}
+
+
+void ListeningSocketRegistry::Cleanup() {
+  delete globalTcpListeningSocketRegistry;
+  globalTcpListeningSocketRegistry = NULL;
+}
+
+
+Dart_Handle ListeningSocketRegistry::CreateBindListen(Dart_Handle socket_object,
+                                                      RawAddr addr,
+                                                      intptr_t port,
+                                                      intptr_t backlog,
+                                                      bool v6_only,
+                                                      bool shared) {
+  MutexLocker ml(ListeningSocketRegistry::mutex_);
+
+  SocketsIterator it = sockets_by_port_.find(port);
+  OSSocket *first_os_socket = NULL;
+  if (it != sockets_by_port_.end()) {
+    first_os_socket = it->second;
+  }
+
+  if (first_os_socket != NULL) {
+    // There is already a socket listening on this port. We need to ensure
+    // that if there is one also listening on the same address, it was created
+    // with `shared = true`, ...
+
+    OSSocket *os_socket = it->second;
+    OSSocket *os_socket_same_addr = findOSSocketWithAddress(os_socket, addr);
+
+    if (os_socket_same_addr != NULL) {
+      if (!os_socket_same_addr->shared || !shared) {
+        OSError os_error(-1,
+                         "The shared flag to bind() needs to be `true` if "
+                         "binding multiple times on the same (address, port) "
+                         "combination.",
+                        OSError::kUnknown);
+        return DartUtils::NewDartOSError(&os_error);
+      }
+      if (os_socket_same_addr->v6_only != v6_only) {
+        OSError os_error(-1,
+                         "The v6Only flag to bind() needs to be the same if "
+                         "binding multiple times on the same (address, port) "
+                         "combination.",
+                        OSError::kUnknown);
+        return DartUtils::NewDartOSError(&os_error);
+      }
+
+      // This socket creation is the exact same as the one which originally
+      // created the socket. We therefore increment the refcount and reuse
+      // the file descriptor.
+      os_socket->ref_count++;
+
+      // We set as a side-effect the file descriptor on the dart socket_object.
+      Socket::SetSocketIdNativeField(socket_object, os_socket->socketfd);
+
+      return Dart_True();
+    }
+  }
+
+  // There is no socket listening on that (address, port), so we create new one.
+  intptr_t socketfd = ServerSocket::CreateBindListen(
+      addr, port, backlog, v6_only);
+  if (socketfd == -5) {
+    OSError os_error(-1, "Invalid host", OSError::kUnknown);
+    return DartUtils::NewDartOSError(&os_error);
+  }
+  if (socketfd < 0) {
+    OSError error;
+    return DartUtils::NewDartOSError(&error);
+  }
+  if (!ServerSocket::StartAccept(socketfd)) {
+    OSError os_error(-1, "Failed to start accept", OSError::kUnknown);
+    return DartUtils::NewDartOSError(&os_error);
+  }
+  intptr_t allocated_port = Socket::GetPort(socketfd);
+  ASSERT(allocated_port > 0);
+
+  OSSocket *os_socket =
+      new OSSocket(addr, allocated_port, v6_only, shared, socketfd);
+  os_socket->ref_count = 1;
+  os_socket->next = first_os_socket;
+  sockets_by_port_[allocated_port] = os_socket;
+  sockets_by_fd_[socketfd] = os_socket;
+
+  // We set as a side-effect the port on the dart socket_object.
+  Socket::SetSocketIdNativeField(socket_object, socketfd);
+
+  return Dart_True();
+}
+
+
+bool ListeningSocketRegistry::CloseSafe(intptr_t socketfd) {
+  ASSERT(!mutex_->TryLock());
+
+  SocketsIterator it = sockets_by_fd_.find(socketfd);
+  if (it != sockets_by_fd_.end()) {
+    OSSocket *os_socket = it->second;
+
+    ASSERT(os_socket->ref_count > 0);
+    os_socket->ref_count--;
+    if (os_socket->ref_count == 0) {
+      // We free the OS socket by removing it from two datastructures.
+      sockets_by_fd_.erase(socketfd);
+
+      OSSocket *prev = NULL;
+      OSSocket *current = sockets_by_port_[os_socket->port];
+      while (current != os_socket) {
+        ASSERT(current != NULL);
+        prev = current;
+        current = current->next;
+      }
+
+      if (prev == NULL && current->next == NULL) {
+        // Remove last element from the list.
+        sockets_by_port_.erase(os_socket->port);
+      } else if (prev == NULL) {
+        // Remove first element of the list.
+        sockets_by_port_[os_socket->port] = current->next;
+      } else {
+        // Remove element from the list which is not the first one.
+        prev->next = os_socket->next;
+      }
+
+      delete os_socket;
+      return true;
+    }
+    return false;
+  } else {
+    // It should be impossible for the event handler to close something that
+    // hasn't been created before.
+    UNREACHABLE();
+    return false;
+  }
+}
+
+
+Dart_Handle ListeningSocketRegistry::MarkSocketFdAsSharableHack(
+    intptr_t socketfd) {
+  MutexLocker ml(ListeningSocketRegistry::mutex_);
+
+  SocketsIterator it = sockets_by_fd_.find(socketfd);
+  if (it != sockets_by_fd_.end()) {
+    it->second->shared = true;
+    return Dart_True();
+  } else {
+    return Dart_False();
+  }
+}
+
+
 void FUNCTION_NAME(InternetAddress_Parse)(Dart_NativeArguments args) {
   const char* address =
       DartUtils::GetStringValue(Dart_GetNativeArgument(args, 0));
@@ -57,6 +222,25 @@
 }
 
 
+void FUNCTION_NAME(Socket_CreateBindConnect)(Dart_NativeArguments args) {
+  RawAddr addr;
+  SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
+  Dart_Handle port_arg = Dart_GetNativeArgument(args, 2);
+  int64_t port = DartUtils::GetInt64ValueCheckRange(port_arg, 0, 65535);
+  RawAddr sourceAddr;
+  SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 3), &sourceAddr);
+  intptr_t socket = Socket::CreateBindConnect(
+      addr, static_cast<intptr_t>(port), sourceAddr);
+  OSError error;
+  if (socket >= 0) {
+    Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket);
+    Dart_SetReturnValue(args, Dart_True());
+  } else {
+    Dart_SetReturnValue(args, DartUtils::NewDartOSError(&error));
+  }
+}
+
+
 void FUNCTION_NAME(Socket_CreateBindDatagram)(Dart_NativeArguments args) {
   RawAddr addr;
   SocketAddress::GetSockAddr(Dart_GetNativeArgument(args, 1), &addr);
@@ -376,20 +560,12 @@
       0,
       65535);
   bool v6_only = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 4));
-  intptr_t socket = ServerSocket::CreateBindListen(
-      addr, port, backlog, v6_only);
-  OSError error;
-  if (socket >= 0 && ServerSocket::StartAccept(socket)) {
-    Socket::SetSocketIdNativeField(Dart_GetNativeArgument(args, 0), socket);
-    Dart_SetReturnValue(args, Dart_True());
-  } else {
-    if (socket == -5) {
-      OSError os_error(-1, "Invalid host", OSError::kUnknown);
-      Dart_SetReturnValue(args, DartUtils::NewDartOSError(&os_error));
-    } else {
-      Dart_SetReturnValue(args, DartUtils::NewDartOSError(&error));
-    }
-  }
+  bool shared = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 5));
+
+  Dart_Handle socket_object = Dart_GetNativeArgument(args, 0);
+  Dart_Handle result = ListeningSocketRegistry::Instance()->CreateBindListen(
+      socket_object, addr, port, backlog, v6_only, shared);
+  Dart_SetReturnValue(args, result);
 }
 
 
@@ -678,6 +854,15 @@
 }
 
 
+void FUNCTION_NAME(Socket_MarkSocketAsSharedHack)(Dart_NativeArguments args) {
+  intptr_t socketfd =
+      Socket::GetSocketIdNativeField(Dart_GetNativeArgument(args, 0));
+
+  ListeningSocketRegistry *registry = ListeningSocketRegistry::Instance();
+  Dart_SetReturnValue(args, registry->MarkSocketFdAsSharableHack(socketfd));
+}
+
+
 void Socket::SetSocketIdNativeField(Dart_Handle socket, intptr_t id) {
   Dart_Handle err =
       Dart_SetNativeInstanceField(socket, kSocketIdNativeField, id);
diff --git a/runtime/bin/socket.h b/runtime/bin/socket.h
index ff5444d..fc27903 100644
--- a/runtime/bin/socket.h
+++ b/runtime/bin/socket.h
@@ -5,6 +5,8 @@
 #ifndef BIN_SOCKET_H_
 #define BIN_SOCKET_H_
 
+#include <map>
+
 #include "platform/globals.h"
 
 #include "bin/builtin.h"
@@ -76,6 +78,21 @@
         sizeof(struct in6_addr) : sizeof(struct in_addr);
   }
 
+  static bool AreAddressesEqual(const RawAddr& a, const RawAddr& b) {
+    if (a.ss.ss_family == AF_INET) {
+      if (b.ss.ss_family != AF_INET) return false;
+      return memcmp(&a.in.sin_addr, &b.in.sin_addr, sizeof(a.in.sin_addr)) == 0;
+    } else if (a.ss.ss_family == AF_INET6) {
+      if (b.ss.ss_family != AF_INET6) return false;
+      return memcmp(&a.in6.sin6_addr,
+                    &b.in6.sin6_addr,
+                    sizeof(a.in6.sin6_addr)) == 0;
+    } else {
+      UNREACHABLE();
+      return false;
+    }
+  }
+
   static void GetSockAddr(Dart_Handle obj, RawAddr* addr) {
     Dart_TypedData_Type data_type;
     uint8_t* data = NULL;
@@ -227,10 +244,11 @@
       intptr_t fd, const void* buffer, intptr_t num_bytes, RawAddr addr);
   static intptr_t RecvFrom(
       intptr_t fd, void* buffer, intptr_t num_bytes, RawAddr* addr);
-  static intptr_t Create(RawAddr addr);
-  static intptr_t Connect(intptr_t fd, RawAddr addr, const intptr_t port);
-  static intptr_t CreateConnect(RawAddr addr,
+  static intptr_t CreateConnect(const RawAddr& addr,
                                 const intptr_t port);
+  static intptr_t CreateBindConnect(const RawAddr& addr,
+                                    const intptr_t port,
+                                    const RawAddr& source_addr);
   static intptr_t CreateBindDatagram(
       RawAddr* addr, intptr_t port, bool reuseAddress);
   static intptr_t GetPort(intptr_t fd);
@@ -239,8 +257,6 @@
   static int GetType(intptr_t fd);
   static intptr_t GetStdioHandle(intptr_t num);
   static void Close(intptr_t fd);
-  static bool SetNonBlocking(intptr_t fd);
-  static bool SetBlocking(intptr_t fd);
   static bool GetNoDelay(intptr_t fd, bool* enabled);
   static bool SetNoDelay(intptr_t fd, bool enabled);
   static bool GetMulticastLoop(intptr_t fd, intptr_t protocol, bool* enabled);
@@ -312,6 +328,91 @@
   DISALLOW_IMPLICIT_CONSTRUCTORS(ServerSocket);
 };
 
+class ListeningSocketRegistry {
+ private:
+  struct OSSocket {
+    RawAddr address;
+    int port;
+    bool v6_only;
+    bool shared;
+    int ref_count;
+    intptr_t socketfd;
+
+    // Singly linked lists of OSSocket instances which listen on the same port
+    // but on different addresses.
+    OSSocket *next;
+
+    OSSocket(RawAddr address, int port, bool v6_only, bool shared,
+             intptr_t socketfd)
+        : address(address), port(port), v6_only(v6_only), shared(shared),
+          ref_count(0), socketfd(socketfd), next(NULL) {}
+  };
+
+ public:
+  static void Initialize();
+
+  static ListeningSocketRegistry *Instance();
+
+  static void Cleanup();
+
+
+  ListeningSocketRegistry() : mutex_(new Mutex()) {}
+
+  ~ListeningSocketRegistry() {
+    delete mutex_;
+    mutex_ = NULL;
+  }
+
+  // This function should be called from a dart runtime call in order to create
+  // a new (potentially shared) socket.
+  Dart_Handle CreateBindListen(Dart_Handle socket_object,
+                               RawAddr addr,
+                               intptr_t port,
+                               intptr_t backlog,
+                               bool v6_only,
+                               bool shared);
+
+  // This should be called from the event handler for every kCloseEvent it gets
+  // on listening sockets.
+  //
+  // Returns `true` if the last reference has been dropped and the underlying
+  // socket can be closed.
+  //
+  // The caller is responsible for obtaining the mutex first, before calling
+  // this function.
+  bool CloseSafe(intptr_t socketfd);
+
+  // Mark an existing socket as sharable if it is not already marked as
+  // sharable.
+  //
+  // NOTE: This is a temporary measure until ServerSocketReference's are
+  // removed.
+  Dart_Handle MarkSocketFdAsSharableHack(intptr_t socketfd);
+
+  Mutex *mutex() { return mutex_; }
+
+ private:
+  OSSocket *findOSSocketWithAddress(OSSocket *current, const RawAddr& addr) {
+    while (current != NULL) {
+      if (SocketAddress::AreAddressesEqual(current->address, addr)) {
+        return current;
+      }
+      current = current->next;
+    }
+    return NULL;
+  }
+
+  std::map<intptr_t, OSSocket*> sockets_by_port_;
+  std::map<intptr_t, OSSocket*> sockets_by_fd_;
+  Mutex *mutex_;
+
+  typedef std::map<intptr_t, OSSocket*>::iterator SocketsIterator;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ListeningSocketRegistry);
+};
+
+
 }  // namespace bin
 }  // namespace dart
 
diff --git a/runtime/bin/socket_android.cc b/runtime/bin/socket_android.cc
index ab9c085..e965057 100644
--- a/runtime/bin/socket_android.cc
+++ b/runtime/bin/socket_android.cc
@@ -55,7 +55,7 @@
 }
 
 
-intptr_t Socket::Create(RawAddr addr) {
+static intptr_t Create(RawAddr addr) {
   intptr_t fd;
   fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0));
   if (fd < 0) {
@@ -66,7 +66,7 @@
 }
 
 
-intptr_t Socket::Connect(intptr_t fd, RawAddr addr, const intptr_t port) {
+static intptr_t Connect(intptr_t fd, RawAddr addr, const intptr_t port) {
   SocketAddress::SetAddrPort(&addr, port);
   intptr_t result = TEMP_FAILURE_RETRY(
       connect(fd, &addr.addr, SocketAddress::GetAddrLength(&addr)));
@@ -78,15 +78,34 @@
 }
 
 
-intptr_t Socket::CreateConnect(RawAddr addr, const intptr_t port) {
-  intptr_t fd = Socket::Create(addr);
+intptr_t Socket::CreateConnect(const RawAddr& addr, const intptr_t port) {
+  intptr_t fd = Create(addr);
   if (fd < 0) {
     return fd;
   }
 
-  Socket::SetNonBlocking(fd);
+  FDUtils::SetNonBlocking(fd);
 
-  return Socket::Connect(fd, addr, port);
+  return Connect(fd, addr, port);
+}
+
+
+intptr_t Socket::CreateBindConnect(const RawAddr& addr,
+                                   const intptr_t port,
+                                   const RawAddr& source_addr) {
+  intptr_t fd = Create(addr);
+  if (fd < 0) {
+    return fd;
+  }
+
+  intptr_t result = TEMP_FAILURE_RETRY(
+      bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(&source_addr)));
+  if (result != 0 && errno != EINPROGRESS) {
+    VOID_TEMP_FAILURE_RETRY(close(fd));
+    return -1;
+  }
+
+  return Connect(fd, addr, port);
 }
 
 
@@ -305,7 +324,7 @@
     return -1;
   }
 
-  Socket::SetNonBlocking(fd);
+  FDUtils::SetNonBlocking(fd);
   return fd;
 }
 
@@ -365,7 +384,7 @@
     return -1;
   }
 
-  Socket::SetNonBlocking(fd);
+  FDUtils::SetNonBlocking(fd);
   return fd;
 }
 
@@ -400,7 +419,8 @@
       socket = kTemporaryFailure;
     }
   } else {
-    Socket::SetNonBlocking(socket);
+    FDUtils::SetCloseOnExec(socket);
+    FDUtils::SetNonBlocking(socket);
   }
   return socket;
 }
@@ -412,16 +432,6 @@
 }
 
 
-bool Socket::SetNonBlocking(intptr_t fd) {
-  return FDUtils::SetNonBlocking(fd);
-}
-
-
-bool Socket::SetBlocking(intptr_t fd) {
-  return FDUtils::SetBlocking(fd);
-}
-
-
 bool Socket::GetNoDelay(intptr_t fd, bool* enabled) {
   int on;
   socklen_t len = sizeof(on);
diff --git a/runtime/bin/socket_linux.cc b/runtime/bin/socket_linux.cc
index c6e1fe1..151ac5b 100644
--- a/runtime/bin/socket_linux.cc
+++ b/runtime/bin/socket_linux.cc
@@ -52,7 +52,7 @@
 }
 
 
-intptr_t Socket::Create(RawAddr addr) {
+static intptr_t Create(RawAddr addr) {
   intptr_t fd;
   fd = NO_RETRY_EXPECTED(
       socket(addr.ss.ss_family, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0));
@@ -63,7 +63,7 @@
 }
 
 
-intptr_t Socket::Connect(intptr_t fd, RawAddr addr, const intptr_t port) {
+static intptr_t Connect(intptr_t fd, RawAddr addr, const intptr_t port) {
   SocketAddress::SetAddrPort(&addr, port);
   intptr_t result = TEMP_FAILURE_RETRY(
       connect(fd, &addr.addr, SocketAddress::GetAddrLength(&addr)));
@@ -75,12 +75,31 @@
 }
 
 
-intptr_t Socket::CreateConnect(RawAddr addr, const intptr_t port) {
-  intptr_t fd = Socket::Create(addr);
+intptr_t Socket::CreateConnect(const RawAddr& addr, const intptr_t port) {
+  intptr_t fd = Create(addr);
   if (fd < 0) {
     return fd;
   }
-  return Socket::Connect(fd, addr, port);
+  return Connect(fd, addr, port);
+}
+
+
+intptr_t Socket::CreateBindConnect(const RawAddr& addr,
+                                   const intptr_t port,
+                                   const RawAddr& source_addr) {
+  intptr_t fd = Create(addr);
+  if (fd < 0) {
+    return fd;
+  }
+
+  intptr_t result = TEMP_FAILURE_RETRY(
+      bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(&source_addr)));
+  if (result != 0 && errno != EINPROGRESS) {
+    VOID_TEMP_FAILURE_RETRY(close(fd));
+    return -1;
+  }
+
+  return Connect(fd, addr, port);
 }
 
 
@@ -427,8 +446,8 @@
       socket = kTemporaryFailure;
     }
   } else {
-    FDUtils::SetNonBlocking(socket);
     FDUtils::SetCloseOnExec(socket);
+    FDUtils::SetNonBlocking(socket);
   }
   return socket;
 }
diff --git a/runtime/bin/socket_macos.cc b/runtime/bin/socket_macos.cc
index ffe0aee..239bb45 100644
--- a/runtime/bin/socket_macos.cc
+++ b/runtime/bin/socket_macos.cc
@@ -57,7 +57,7 @@
 }
 
 
-intptr_t Socket::Create(RawAddr addr) {
+static intptr_t Create(RawAddr addr) {
   intptr_t fd;
   fd = NO_RETRY_EXPECTED(socket(addr.ss.ss_family, SOCK_STREAM, 0));
   if (fd < 0) {
@@ -68,7 +68,7 @@
 }
 
 
-intptr_t Socket::Connect(intptr_t fd, RawAddr addr, const intptr_t port) {
+static intptr_t Connect(intptr_t fd, RawAddr addr, const intptr_t port) {
   SocketAddress::SetAddrPort(&addr, port);
   intptr_t result = TEMP_FAILURE_RETRY(
       connect(fd, &addr.addr, SocketAddress::GetAddrLength(&addr)));
@@ -80,15 +80,34 @@
 }
 
 
-intptr_t Socket::CreateConnect(RawAddr addr, const intptr_t port) {
-  intptr_t fd = Socket::Create(addr);
+intptr_t Socket::CreateConnect(const RawAddr& addr, const intptr_t port) {
+  intptr_t fd = Create(addr);
   if (fd < 0) {
     return fd;
   }
 
-  Socket::SetNonBlocking(fd);
+  FDUtils::SetNonBlocking(fd);
 
-  return Socket::Connect(fd, addr, port);
+  return Connect(fd, addr, port);
+}
+
+
+intptr_t Socket::CreateBindConnect(const RawAddr& addr,
+                                   const intptr_t port,
+                                   const RawAddr& source_addr) {
+  intptr_t fd = Create(addr);
+  if (fd < 0) {
+    return fd;
+  }
+
+  intptr_t result = TEMP_FAILURE_RETRY(
+      bind(fd, &source_addr.addr, SocketAddress::GetAddrLength(&source_addr)));
+  if (result != 0 && errno != EINPROGRESS) {
+    VOID_TEMP_FAILURE_RETRY(close(fd));
+    return -1;
+  }
+
+  return Connect(fd, addr, port);
 }
 
 
@@ -298,7 +317,7 @@
     return -1;
   }
 
-  Socket::SetNonBlocking(fd);
+  FDUtils::SetNonBlocking(fd);
   return fd;
 }
 
@@ -398,7 +417,7 @@
     return -1;
   }
 
-  Socket::SetNonBlocking(fd);
+  FDUtils::SetNonBlocking(fd);
   return fd;
 }
 
@@ -423,7 +442,8 @@
       socket = kTemporaryFailure;
     }
   } else {
-    Socket::SetNonBlocking(socket);
+    FDUtils::SetCloseOnExec(socket);
+    FDUtils::SetNonBlocking(socket);
   }
   return socket;
 }
@@ -435,16 +455,6 @@
 }
 
 
-bool Socket::SetNonBlocking(intptr_t fd) {
-  return FDUtils::SetNonBlocking(fd);
-}
-
-
-bool Socket::SetBlocking(intptr_t fd) {
-  return FDUtils::SetBlocking(fd);
-}
-
-
 bool Socket::GetNoDelay(intptr_t fd, bool* enabled) {
   int on;
   socklen_t len = sizeof(on);
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart
index 3983f24..c866977 100644
--- a/runtime/bin/socket_patch.dart
+++ b/runtime/bin/socket_patch.dart
@@ -6,15 +6,17 @@
   /* patch */ static Future<RawServerSocket> bind(address,
                                                   int port,
                                                   {int backlog: 0,
-                                                   bool v6Only: false}) {
-    return _RawServerSocket.bind(address, port, backlog, v6Only);
+                                                   bool v6Only: false,
+                                                   bool shared: false}) {
+    return _RawServerSocket.bind(address, port, backlog, v6Only, shared);
   }
 }
 
 
 patch class RawSocket {
-  /* patch */ static Future<RawSocket> connect(host, int port) {
-    return _RawSocket.connect(host, port);
+  /* patch */ static Future<RawSocket> connect(
+      host, int port, {sourceAddress}) {
+    return _RawSocket.connect(host, port, sourceAddress);
   }
 }
 
@@ -381,7 +383,12 @@
         });
   }
 
-  static Future<_NativeSocket> connect(host, int port) {
+  static Future<_NativeSocket> connect(host, int port, sourceAddress) {
+    if (sourceAddress != null && sourceAddress is! _InternetAddress) {
+      if (sourceAddress is String) {
+        sourceAddress = new InternetAddress(sourceAddress);
+      }
+    }
     return new Future.value(host)
         .then((host) {
           if (host is _InternetAddress) return [host];
@@ -410,7 +417,14 @@
             var address = it.current;
             var socket = new _NativeSocket.normal();
             socket.address = address;
-            var result = socket.nativeCreateConnect(address._in_addr, port);
+            var result;
+            if (sourceAddress == null) {
+              result = socket.nativeCreateConnect(address._in_addr, port);
+            } else {
+              assert(sourceAddress is _InternetAddress);
+              result = socket.nativeCreateBindConnect(
+                  address._in_addr, port, sourceAddress._in_addr);
+            }
             if (result is OSError) {
               // Keep first error, if present.
               if (error == null) {
@@ -460,7 +474,8 @@
   static Future<_NativeSocket> bind(host,
                                     int port,
                                     int backlog,
-                                    bool v6Only) {
+                                    bool v6Only,
+                                    bool shared) {
     return new Future.value(host)
         .then((host) {
           if (host is _InternetAddress) return host;
@@ -475,10 +490,12 @@
         .then((address) {
           var socket = new _NativeSocket.listen();
           socket.address = address;
+
           var result = socket.nativeCreateBindListen(address._in_addr,
                                                      port,
                                                      backlog,
-                                                     v6Only);
+                                                     v6Only,
+                                                     shared);
           if (result is OSError) {
             throw new SocketException("Failed to create server socket",
                                       osError: result,
@@ -1116,7 +1133,11 @@
       native "Socket_SendTo";
   nativeCreateConnect(List<int> addr,
                       int port) native "Socket_CreateConnect";
-  nativeCreateBindListen(List<int> addr, int port, int backlog, bool v6Only)
+  nativeCreateBindConnect(
+      List<int> addr, int port, List<int> sourceAddr)
+      native "Socket_CreateBindConnect";
+  nativeCreateBindListen(List<int> addr, int port, int backlog, bool v6Only,
+                         bool shared)
       native "ServerSocket_CreateBindListen";
   nativeCreateBindDatagram(List<int> addr, int port, bool reuseAddress)
       native "Socket_CreateBindDatagram";
@@ -1134,6 +1155,8 @@
   bool nativeLeaveMulticast(
       List<int> addr, List<int> interfaceAddr, int interfaceIndex)
       native "Socket_LeaveMulticast";
+  bool _nativeMarkSocketAsSharedHack()
+      native "Socket_MarkSocketAsSharedHack";
 }
 
 
@@ -1142,19 +1165,21 @@
   final _NativeSocket _socket;
   StreamController<RawSocket> _controller;
   ReceivePort _referencePort;
+  bool _v6Only;
 
   static Future<_RawServerSocket> bind(address,
                                        int port,
                                        int backlog,
-                                       bool v6Only) {
+                                       bool v6Only,
+                                       bool shared) {
     if (port < 0 || port > 0xFFFF)
       throw new ArgumentError("Invalid port $port");
     if (backlog < 0) throw new ArgumentError("Invalid backlog $backlog");
-    return _NativeSocket.bind(address, port, backlog, v6Only)
-        .then((socket) => new _RawServerSocket(socket));
+    return _NativeSocket.bind(address, port, backlog, v6Only, shared)
+        .then((socket) => new _RawServerSocket(socket, v6Only));
   }
 
-  _RawServerSocket(this._socket);
+  _RawServerSocket(this._socket, this._v6Only);
 
   StreamSubscription<RawSocket> listen(void onData(RawSocket event),
                                        {Function onError,
@@ -1236,18 +1261,20 @@
 
   RawServerSocketReference get reference {
     if (_referencePort == null) {
+      bool successfull = _socket._nativeMarkSocketAsSharedHack();
       _referencePort = new ReceivePort();
       _referencePort.listen((sendPort) {
         sendPort.send(
-          [_socket.nativeGetSocketId(),
-           _socket.address,
-           _socket.localPort]);
+          [_socket.address,
+           _socket.port,
+           _v6Only]);
       });
     }
     return new _RawServerSocketReference(_referencePort.sendPort);
   }
 
   Map _toJSON(bool ref) => _socket._toJSON(ref);
+
   void set _owner(owner) { _socket.owner = owner; }
 }
 
@@ -1260,20 +1287,21 @@
   Future<RawServerSocket> create() {
     var port = new ReceivePort();
     _sendPort.send(port.sendPort);
-    return port.first.then((args) {
+    return port.first.then((List args) {
       port.close();
-      var native = new _NativeSocket.listen();
-      native.nativeSetSocketId(args[0]);
-      native.address = args[1];
-      native.localPort = args[2];
-      return new _RawServerSocket(native);
+
+      InternetAddress address = args[0];
+      int tcpPort = args[1];
+      bool v6Only = args[2];
+      return
+          RawServerSocket.bind(address, tcpPort, v6Only: v6Only, shared: true);
     });
   }
 
   int get hashCode => _sendPort.hashCode;
 
   bool operator==(Object other)
-    => other is _RawServerSocketReference && _sendPort == other._sendPort;
+      => other is _RawServerSocketReference && _sendPort == other._sendPort;
 }
 
 
@@ -1287,8 +1315,8 @@
   // Flag to handle Ctrl-D closing of stdio on Mac OS.
   bool _isMacOSTerminalInput = false;
 
-  static Future<RawSocket> connect(host, int port) {
-    return _NativeSocket.connect(host, port)
+  static Future<RawSocket> connect(host, int port, sourceAddress) {
+    return _NativeSocket.connect(host, port, sourceAddress)
         .then((socket) => new _RawSocket(socket));
   }
 
@@ -1432,8 +1460,9 @@
   /* patch */ static Future<ServerSocket> bind(address,
                                                int port,
                                                {int backlog: 0,
-                                                bool v6Only: false}) {
-    return _ServerSocket.bind(address, port, backlog, v6Only);
+                                                bool v6Only: false,
+                                                bool shared: false}) {
+    return _ServerSocket.bind(address, port, backlog, v6Only, shared);
   }
 }
 
@@ -1456,8 +1485,9 @@
   static Future<_ServerSocket> bind(address,
                                     int port,
                                     int backlog,
-                                    bool v6Only) {
-    return _RawServerSocket.bind(address, port, backlog, v6Only)
+                                    bool v6Only,
+                                    bool shared) {
+    return _RawServerSocket.bind(address, port, backlog, v6Only, shared)
         .then((socket) => new _ServerSocket(socket));
   }
 
@@ -1485,13 +1515,14 @@
   }
 
   Map _toJSON(bool ref) => _socket._toJSON(ref);
+
   void set _owner(owner) { _socket._owner = owner; }
 }
 
 
 patch class Socket {
-  /* patch */ static Future<Socket> connect(host, int port) {
-    return RawSocket.connect(host, port).then(
+  /* patch */ static Future<Socket> connect(host, int port, {sourceAddress}) {
+    return RawSocket.connect(host, port, sourceAddress: sourceAddress).then(
         (socket) => new _Socket(socket));
   }
 }
diff --git a/runtime/bin/socket_win.cc b/runtime/bin/socket_win.cc
index 346c7b1..e366430 100644
--- a/runtime/bin/socket_win.cc
+++ b/runtime/bin/socket_win.cc
@@ -129,7 +129,7 @@
 }
 
 
-intptr_t Socket::Create(RawAddr addr) {
+static intptr_t Create(RawAddr addr) {
   SOCKET s = socket(addr.ss.ss_family, SOCK_STREAM, 0);
   if (s == INVALID_SOCKET) {
     return -1;
@@ -152,23 +152,17 @@
 }
 
 
-intptr_t Socket::Connect(intptr_t fd, RawAddr addr, const intptr_t port) {
+static intptr_t Connect(
+    intptr_t fd, RawAddr addr, const intptr_t port, RawAddr bind_addr) {
   ASSERT(reinterpret_cast<Handle*>(fd)->is_client_socket());
   ClientSocket* handle = reinterpret_cast<ClientSocket*>(fd);
   SOCKET s = handle->socket();
 
-  RawAddr bind_addr;
-  memset(&bind_addr, 0, sizeof(bind_addr));
-  bind_addr.ss.ss_family = addr.ss.ss_family;
-  if (addr.ss.ss_family == AF_INET) {
-    bind_addr.in.sin_addr.s_addr = INADDR_ANY;
-  } else {
-    bind_addr.in6.sin6_addr = in6addr_any;
-  }
   int status = bind(
       s, &bind_addr.addr, SocketAddress::GetAddrLength(&bind_addr));
   if (status != NO_ERROR) {
     int rc = WSAGetLastError();
+    handle->mark_closed();  // Destructor asserts that socket is marked closed.
     delete handle;
     closesocket(s);
     SetLastError(rc);
@@ -223,13 +217,34 @@
 }
 
 
-intptr_t Socket::CreateConnect(RawAddr addr, const intptr_t port) {
-  intptr_t fd = Socket::Create(addr);
+intptr_t Socket::CreateConnect(const RawAddr& addr, const intptr_t port) {
+  intptr_t fd = Create(addr);
   if (fd < 0) {
     return fd;
   }
 
-  return Socket::Connect(fd, addr, port);
+  RawAddr bind_addr;
+  memset(&bind_addr, 0, sizeof(bind_addr));
+  bind_addr.ss.ss_family = addr.ss.ss_family;
+  if (addr.ss.ss_family == AF_INET) {
+    bind_addr.in.sin_addr.s_addr = INADDR_ANY;
+  } else {
+    bind_addr.in6.sin6_addr = in6addr_any;
+  }
+
+  return Connect(fd, addr, port, bind_addr);
+}
+
+
+intptr_t Socket::CreateBindConnect(const RawAddr& addr,
+                                   const intptr_t port,
+                                   const RawAddr& source_addr) {
+  intptr_t fd = Create(addr);
+  if (fd < 0) {
+    return fd;
+  }
+
+  return Connect(fd, addr, port, source_addr);
 }
 
 
@@ -550,27 +565,6 @@
 }
 
 
-static bool SetBlockingHelper(intptr_t fd, bool blocking) {
-  SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
-  u_long iMode = blocking ? 0 : 1;
-  int status = ioctlsocket(handle->socket(), FIONBIO, &iMode);
-  if (status != NO_ERROR) {
-    return false;
-  }
-  return true;
-}
-
-
-bool Socket::SetNonBlocking(intptr_t fd) {
-  return SetBlockingHelper(fd, false);
-}
-
-
-bool Socket::SetBlocking(intptr_t fd) {
-  return SetBlockingHelper(fd, true);
-}
-
-
 bool Socket::GetNoDelay(intptr_t fd, bool* enabled) {
   SocketHandle* handle = reinterpret_cast<SocketHandle*>(fd);
   int on;
diff --git a/runtime/include/dart_api.h b/runtime/include/dart_api.h
index f7fbfe3..acea977 100755
--- a/runtime/include/dart_api.h
+++ b/runtime/include/dart_api.h
@@ -1933,9 +1933,15 @@
  * \param data The internal data address is returned here.
  * \param len Size of the typed array is returned here.
  *
- * Note: When the internal address of the object is acquired any calls to a
- *       Dart API function that could potentially allocate an object or run
- *       any Dart code will return an error.
+ * Notes:
+ *   When the internal address of the object is acquired any calls to a
+ *   Dart API function that could potentially allocate an object or run
+ *   any Dart code will return an error.
+ *
+ *   Any Dart API functions for accessing the data should not be called
+ *   before the corresponding release. In particular, the object should
+ *   not be acquired again before its release. This leads to undefined
+ *   behavior.
  *
  * \return Success if the internal data address is acquired successfully.
  *   Otherwise, returns an error handle.
diff --git a/runtime/lib/bigint.dart b/runtime/lib/bigint.dart
index 07c6d80..7519ac1 100644
--- a/runtime/lib/bigint.dart
+++ b/runtime/lib/bigint.dart
@@ -1167,7 +1167,7 @@
   // This method must support smi._toBigint()._shrFromInt(int).
   int _shrFromInt(int other) {
     if (_used == 0) return other;  // Shift amount is zero.
-    if (_neg) throw "negative shift amount";  // TODO(regis): What exception?
+    if (_neg) throw new RangeError(this);
     assert(_DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
     var shift;
     if ((_used > 2) || ((_used == 2) && (_digits[1] > 0x10000000))) {
@@ -1186,7 +1186,7 @@
   // An out of memory exception is thrown if the result cannot be allocated.
   int _shlFromInt(int other) {
     if (_used == 0) return other;  // Shift amount is zero.
-    if (_neg) throw "negative shift amount";  // TODO(regis): What exception?
+    if (_neg) throw new RangeError(this);
     assert(_DIGIT_BITS == 32);  // Otherwise this code needs to be revised.
     var shift;
     if (_used > 2 || (_used == 2 && _digits[1] > 0x10000000)) {
@@ -1338,19 +1338,23 @@
     return other._toBigint()._compare(this) == 0;
   }
 
-  // Return pow(this, e) % m, with e >= 0, m > 0.
+  // Returns pow(this, e) % m, with e >= 0, m > 0.
   int modPow(int e, int m) {
-    if (e is! int || e < 0) throw new ArgumentError(e);
-    if (m is! int || m <= 0) throw new ArgumentError(m);
+    if (e is! int) throw new ArgumentError(e);
+    if (m is! int) throw new ArgumentError(m);
+    if (e < 0) throw new RangeError(e);
+    if (m <= 0) throw new RangeError(m);
+    if (e == 0) return 1;
+    e = e._toBigint();
+    m = m._toBigint();
     final m_used = m._used;
-    final m_used2p2 = 2*m_used + 1 + 1;  // +1 for leading zero.
+    final m_used2p2 = 2*m_used + 2;
     final e_bitlen = e.bitLength;
     if (e_bitlen <= 0) return 1;
     if ((e is! _Bigint) || m.isEven) {
       _Reduction z = (e_bitlen < 8 || m.isEven) ?
           new _Classic(m) : new _Montgomery(m);
       // TODO(regis): Should we use Barrett reduction for an even modulus?
-      var m_used = m._used;
       var r_digits = new Uint32List(m_used2p2);
       var r2_digits = new Uint32List(m_used2p2);
       var g_digits = new Uint32List(m_used + (m_used & 1));
@@ -1627,7 +1631,8 @@
     while (x_used > 0 && x_digits[x_used - 1] == 0) {
       --x_used;
     }
-    x_used = _Bigint._drShiftDigits(x_digits, x_used, m_used, x_digits);
+    // Shift right by m_used digits or, if processing pairs, by i (even) digits.
+    x_used = _Bigint._drShiftDigits(x_digits, x_used, i, x_digits);
     if (_Bigint._compareDigits(x_digits, x_used, m_digits, m_used) >= 0) {
       _Bigint._absSub(x_digits, x_used, m_digits, m_used, x_digits);
     }
@@ -1697,7 +1702,7 @@
     var digits;
     var used;
     if (x._neg || x._compare(_m) >= 0) {
-      var r = x.rem(_m);
+      var r = x._rem(_m);
       if (x._neg && !r._neg && r._used > 0) {
         r = _m._sub(r);
       }
@@ -1708,7 +1713,7 @@
       used = x._used;
       digits = x._digits;
     }
-    var i = used + (used + 1);  // Copy leading zero if any.
+    var i = used + (used & 1);  // Copy leading zero if any.
     while (--i >= 0) {
       r_digits[i] = digits[i];
     }
diff --git a/runtime/lib/core_patch.dart b/runtime/lib/core_patch.dart
index a4217b9..5f1f828 100644
--- a/runtime/lib/core_patch.dart
+++ b/runtime/lib/core_patch.dart
@@ -64,8 +64,10 @@
         current = null;
         return false;
       }
-      if (isYieldEach && (current is Iterable)) {
-        yieldEachIterator = current.iterator;
+      if (isYieldEach) {
+        // Spec mandates: it is a dynamic error if the class of [the object
+        // returned by yield*] does not implement Iterable.
+        yieldEachIterator = (current as Iterable).iterator;
         continue;
       }
       return true;    
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index 26a996d..857a415 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -265,25 +265,28 @@
 
   _leftShiftWithMask32(count, mask)  native "Integer_leftShiftWithMask32";
 
-  // Return pow(this, e) % m.
+  // Returns pow(this, e) % m.
   int modPow(int e, int m) {
-    if (e is! int || e < 0) throw new ArgumentError(e);
-    if (m is! int || m <= 0) throw new ArgumentError(m);
+    if (e is! int) throw new ArgumentError(e);
+    if (m is! int) throw new ArgumentError(m);
+    if (e < 0) throw new RangeError(e);
+    if (m <= 0) throw new RangeError(m);
+    if (e == 0) return 1;
     if (e is _Bigint || m is _Bigint) {
       return _toBigint().modPow(e, m);
     }
     if (e < 1) return 1;
     int b = this;
     if (b < 0 || b > m) {
-      b = b % m;
+      b %= m;
     }
     int r = 1;
     while (e > 0) {
-     if ((e & 1) != 0) {
-       r = (r * b) % m;
-     }
-     e >>= 1;
-     b = (b * b) % m;
+      if (e.isOdd) {
+        r = (r * b) % m;
+      }
+      e >>= 1;
+      b = (b * b) % m;
     }
     return r;
   }
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index c05335a..3a5ba02 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -71,7 +71,7 @@
 
 static void EnsureConstructorsAreCompiled(const Function& func) {
   // Only generative constructors can have initializing formals.
-  if (!func.IsConstructor()) return;
+  if (!func.IsGenerativeConstructor()) return;
 
   Isolate* isolate = Isolate::Current();
   const Class& cls = Class::Handle(isolate, func.Owner());
@@ -256,7 +256,7 @@
   bool isConstructor = (func.kind() == RawFunction::kConstructor);
   args.SetAt(7, Bool::Get(isConstructor));
   args.SetAt(8, Bool::Get(isConstructor && func.is_const()));
-  args.SetAt(9, Bool::Get(isConstructor && func.IsConstructor()));
+  args.SetAt(9, Bool::Get(isConstructor && func.IsGenerativeConstructor()));
   args.SetAt(10, Bool::Get(isConstructor && func.is_redirecting()));
   args.SetAt(11, Bool::Get(isConstructor && func.IsFactory()));
 
@@ -1540,7 +1540,7 @@
       klass.LookupFunction(internal_constructor_name));
 
   if (lookup_constructor.IsNull() ||
-      !(lookup_constructor.IsConstructor() || lookup_constructor.IsFactory()) ||
+      (lookup_constructor.kind() != RawFunction::kConstructor) ||
       !lookup_constructor.is_reflectable()) {
     // Pretend we didn't find the constructor at all when the arity is wrong
     // so as to produce the same NoSuchMethodError as the non-reflective case.
@@ -1602,7 +1602,7 @@
 
   const intptr_t num_explicit_args = explicit_args.Length();
   const intptr_t num_implicit_args =
-      redirected_constructor.IsConstructor() ? 2 : 1;
+      redirected_constructor.IsGenerativeConstructor() ? 2 : 1;
   const Array& args =
       Array::Handle(Array::New(num_implicit_args + num_explicit_args));
 
@@ -1633,7 +1633,7 @@
   }
 
   Instance& new_object = Instance::Handle();
-  if (redirected_constructor.IsConstructor()) {
+  if (redirected_constructor.IsGenerativeConstructor()) {
     // Constructors get the uninitialized object and a constructor phase. Note
     // we have delayed allocation until after the function type and argument
     // matching checks.
@@ -1664,7 +1664,7 @@
   // Factories may return null.
   ASSERT(result.IsInstance() || result.IsNull());
 
-  if (redirected_constructor.IsConstructor()) {
+  if (redirected_constructor.IsGenerativeConstructor()) {
     return new_object.raw();
   } else {
     return result.raw();
@@ -1840,7 +1840,7 @@
   const Function& func = Function::Handle(ref.GetFunctionReferent());
   GET_NATIVE_ARGUMENT(AbstractType, instantiator, arguments->NativeArgAt(1));
   // We handle constructors in Dart code.
-  ASSERT(!func.IsConstructor());
+  ASSERT(!func.IsGenerativeConstructor());
   const AbstractType& type = AbstractType::Handle(func.result_type());
   return InstantiateType(type, instantiator);
 }
diff --git a/runtime/lib/typed_data.dart b/runtime/lib/typed_data.dart
index 6935e2f..61065ed 100644
--- a/runtime/lib/typed_data.dart
+++ b/runtime/lib/typed_data.dart
@@ -3588,12 +3588,16 @@
 
 
 int _toInt64(int value) {
-  return _toInt(value, 0xFFFFFFFFFFFFFFFF);  // TODO(regis): Avoid bigint mask.
+  // Avoid bigint mask when possible.
+  return (ClassID.getID(value) == ClassID.cidBigint) ?
+      _toInt(value, 0xFFFFFFFFFFFFFFFF) : value;
 }
 
 
 int _toUint64(int value) {
-  return value & 0xFFFFFFFFFFFFFFFF;  // TODO(regis): Avoid bigint mask.
+  // Avoid bigint mask when possible.
+  return (ClassID.getID(value) == ClassID.cidBigint) ?
+      _toInt(value, 0xFFFFFFFFFFFFFFFF) : value;
 }
 
 
diff --git a/runtime/observatory/lib/src/app/application.dart b/runtime/observatory/lib/src/app/application.dart
index 447e8db..c5413c9 100644
--- a/runtime/observatory/lib/src/app/application.dart
+++ b/runtime/observatory/lib/src/app/application.dart
@@ -65,7 +65,11 @@
   void _onEvent(ServiceEvent event) {
     switch(event.eventType) {
       case 'IsolateCreated':
-        // vm.reload();
+        // Ignore for now.
+        break;
+
+      case 'IsolateResumed':
+        event.isolate.pauseEvent = null;
         break;
 
       case 'IsolateShutdown':
@@ -73,7 +77,11 @@
         // What if there are hundreds of them?  Coalesce multiple
         // shutdown events into one notification?
         removePauseEvents(event.isolate);
-        // vm.reload();
+
+        // TODO(turnidge): Reload the isolate for now in case it is
+        // paused.  We may need to distinguish an IsolateShutdown
+        // event from a "paused at isolate shutdown" event.
+        event.isolate.reload();
         break;
 
       case 'BreakpointResolved':
@@ -83,6 +91,7 @@
       case 'BreakpointReached':
       case 'IsolateInterrupted':
       case 'ExceptionThrown':
+        event.isolate.pauseEvent = event;
         removePauseEvents(event.isolate);
         notifications.add(event);
         break;
diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart
index f17bacb..9c18f94 100644
--- a/runtime/observatory/lib/src/elements/debugger.dart
+++ b/runtime/observatory/lib/src/elements/debugger.dart
@@ -4,15 +4,431 @@
 
 library debugger_page_element;
 
+import 'dart:async';
 import 'dart:html';
 import 'observatory_element.dart';
+import 'package:observatory/cli.dart';
 import 'package:observatory/service.dart';
 import 'package:polymer/polymer.dart';
 
+// TODO(turnidge): Move Debugger, DebuggerCommand to their own lib.
+abstract class DebuggerCommand extends Command {
+  Debugger debugger;
+
+  DebuggerCommand(this.debugger, name, children)
+      : super(name, children);
+}
+
+class HelpCommand extends DebuggerCommand {
+  HelpCommand(Debugger debugger) : super(debugger, 'help', []);
+
+  Future run(List<String> args) {
+    var con = debugger.console;
+    con.printLine('List of commands:');
+    con.newline();
+
+    // TODO(turnidge): Build a real help system.
+    List completions = debugger.cmd.completeCommand('');
+    completions = completions.map((s )=> s.trimRight()).toList();
+    completions.sort();
+    con.printLine(completions.toString());
+    con.newline();
+    con.printLine("Command prefixes are accepted (e.g. 'h' for 'help')");
+    con.printLine("Hit [TAB] to complete a command (try 'i[TAB][TAB]')");
+    con.printLine("Hit [ENTER] to repeat the last command");
+
+    return new Future.value(null);
+  }
+}
+
+class PauseCommand extends DebuggerCommand {
+  PauseCommand(Debugger debugger) : super(debugger, 'pause', []);
+
+  Future run(List<String> args) {
+    if (!debugger.isolatePaused()) {
+      return debugger.isolate.pause();
+    } else {
+      debugger.console.printLine('The program is already paused');
+      return new Future.value(null);
+    }
+  }
+}
+
+class ContinueCommand extends DebuggerCommand {
+  ContinueCommand(Debugger debugger) : super(debugger, 'continue', []);
+
+  Future run(List<String> args) {
+    if (debugger.isolatePaused()) {
+      return debugger.isolate.resume().then((_) {
+          debugger.warnOutOfDate();
+        });
+    } else {
+      debugger.console.printLine('The program must be paused');
+      return new Future.value(null);
+    }
+  }
+}
+
+class NextCommand extends DebuggerCommand {
+  NextCommand(Debugger debugger) : super(debugger, 'next', []);
+
+  Future run(List<String> args) {
+    if (debugger.isolatePaused()) {
+      var event = debugger.isolate.pauseEvent;
+      if (event.eventType == 'IsolateCreated') {
+        debugger.console.printLine("Type 'continue' to start the isolate");
+        return new Future.value(null);
+      }
+      if (event.eventType == 'IsolateShutdown') {
+        debugger.console.printLine("Type 'continue' to exit the isolate");
+        return new Future.value(null);
+      }
+      return debugger.isolate.stepOver();
+    } else {
+      debugger.console.printLine('The program is already running');
+      return new Future.value(null);
+    }
+  }
+}
+
+class StepCommand extends DebuggerCommand {
+  StepCommand(Debugger debugger) : super(debugger, 'step', []);
+
+  Future run(List<String> args) {
+    if (debugger.isolatePaused()) {
+      var event = debugger.isolate.pauseEvent;
+      if (event.eventType == 'IsolateCreated') {
+        debugger.console.printLine("Type 'continue' to start the isolate");
+        return new Future.value(null);
+      }
+      if (event.eventType == 'IsolateShutdown') {
+        debugger.console.printLine("Type 'continue' to exit the isolate");
+        return new Future.value(null);
+      }
+      return debugger.isolate.stepInto();
+    } else {
+      debugger.console.printLine('The program is already running');
+      return new Future.value(null);
+    }
+  }
+}
+
+class FinishCommand extends DebuggerCommand {
+  FinishCommand(Debugger debugger) : super(debugger, 'finish', []);
+
+  Future run(List<String> args) {
+    if (debugger.isolatePaused()) {
+      return debugger.isolate.stepOut();
+    } else {
+      debugger.console.printLine('The program is already running');
+      return new Future.value(null);
+    }
+  }
+}
+
+// TODO(turnidge): Add argument completion.
+class DeleteCommand extends DebuggerCommand {
+  DeleteCommand(Debugger debugger) : super(debugger, 'delete', []);
+
+  Future run(List<String> args) {
+    if (args.length < 1) {
+      debugger.console.printLine('delete expects one or more arguments');
+      return new Future.value(null);
+    }
+    List toDelete = [];
+    for (var arg in args) {
+      int id = int.parse(arg);
+      var bpt = null;
+      for (var candidate in debugger.isolate.breakpoints) {
+        if (candidate['breakpointNumber'] == id) {
+          bpt = candidate;
+          break;
+        }
+      }
+      if (bpt == null) {
+        debugger.console.printLine("Invalid breakpoint id '${id}'");
+        return new Future.value(null);
+      }
+      toDelete.add(bpt);
+    }
+    List pending = [];
+    for (var bpt in toDelete) {
+      pending.add(debugger.isolate.removeBreakpoint(bpt).then((_) {
+            var id = bpt['breakpointNumber'];
+            debugger.console.printLine("Removed breakpoint $id");
+          }));
+    }
+    return Future.wait(pending);
+  }
+}
+
+class InfoBreakpointsCommand extends DebuggerCommand {
+  InfoBreakpointsCommand(Debugger debugger)
+      : super(debugger, 'breakpoints', []);
+
+  Future run(List<String> args) {
+    return debugger.isolate.reloadBreakpoints().then((_) {
+      if (debugger.isolate.breakpoints.isEmpty) {
+        debugger.console.printLine('No breakpoints');
+      }
+      for (var bpt in debugger.isolate.breakpoints) {
+        var bpId = bpt['breakpointNumber'];
+        var script = bpt['location']['script'];
+        var tokenPos = bpt['location']['tokenPos'];
+        var line = script.tokenToLine(tokenPos);
+        var col = script.tokenToCol(tokenPos);
+        debugger.console.printLine(
+            'Breakpoint ${bpId} at ${script.name}:${line}:${col}');
+      }
+    });
+  }
+}
+
+class InfoIsolatesCommand extends DebuggerCommand {
+  InfoIsolatesCommand(Debugger debugger) : super(debugger, 'isolates', []);
+
+  Future run(List<String> args) {
+    for (var isolate in debugger.isolate.vm.isolates) {
+      debugger.console.printLine(
+          "Isolate ${isolate.id} '${isolate.name}'");
+    }
+    return new Future.value(null);
+  }
+}
+
+class InfoCommand extends DebuggerCommand {
+  InfoCommand(Debugger debugger) : super(debugger, 'info', [
+      new InfoBreakpointsCommand(debugger),
+      new InfoIsolatesCommand(debugger),
+  ]);
+
+  Future run(List<String> args) {
+    debugger.console.printLine("Invalid info command");
+    return new Future.value(null);
+  }
+}
+
+class RefreshCoverageCommand extends DebuggerCommand {
+  RefreshCoverageCommand(Debugger debugger) : super(debugger, 'coverage', []);
+
+  Future run(List<String> args) {
+    Set<Script> scripts = debugger.stackElement.activeScripts();
+    List pending = [];
+    for (var script in scripts) {
+      pending.add(script.refreshCoverage().then((_) {
+          debugger.console.printLine('Refreshed coverage for ${script.name}');
+        }));
+    }
+    return Future.wait(pending);
+  }
+}
+
+class RefreshCommand extends DebuggerCommand {
+  RefreshCommand(Debugger debugger) : super(debugger, 'refresh', [
+      new RefreshCoverageCommand(debugger),
+  ]);
+
+  Future run(List<String> args) {
+    return debugger.refreshStack();
+  }
+}
+
+// Tracks the state for an isolate debugging session.
+class Debugger {
+  RootCommand cmd;
+  DebuggerConsoleElement console;
+  DebuggerStackElement stackElement;
+  ServiceMap stack;
+
+  Debugger() {
+    cmd = new RootCommand([
+        new HelpCommand(this),
+        new PauseCommand(this),
+        new ContinueCommand(this),
+        new NextCommand(this),
+        new StepCommand(this),
+        new FinishCommand(this),
+        new DeleteCommand(this),
+        new InfoCommand(this),
+        new RefreshCommand(this),
+    ]);
+  }
+
+  void set isolate(Isolate iso) {
+    _isolate = iso;
+    if (_isolate != null) {
+      _isolate.reload().then((_) {
+        _isolate.vm.events.stream.listen(_onEvent);
+        _refreshStack(isolate.pauseEvent).then((_) {
+          reportStatus();
+        });
+      });
+    }
+  }
+  Isolate get isolate => _isolate;
+  Isolate _isolate;
+
+  void init() {
+    console.newline();
+    console.printBold("Type 'h' for help");
+  }
+
+  Future refreshStack() {
+    return _refreshStack(isolate.pauseEvent).then((_) {
+        reportStatus();
+      });
+  }
+
+  bool isolatePaused() {
+    // TODO(turnidge): Stop relying on the isolate to track the last
+    // pause event.  Since we listen to events directly in the
+    // debugger, this could introduce a race.
+    return isolate.pauseEvent != null;
+  }
+
+  void warnOutOfDate() {
+    // Wait a bit, then tell the user that the stack may be out of date.
+    new Timer(const Duration(seconds:2), () {
+      if (!isolatePaused()) {
+        stackElement.isSampled = true;
+      }
+    });
+  }
+
+  Future<ServiceMap> _refreshStack(ServiceEvent pauseEvent) {
+    return isolate.getStack().then((result) {
+      stack = result;
+      // TODO(turnidge): Replace only the changed part of the stack to
+      // reduce flicker.
+      // stackElement.stack = stack;
+      stackElement.updateStack(stack, pauseEvent);
+    });
+  }
+
+  void reportStatus() {
+    if (_isolate.idle) {
+      console.printLine('Isolate is idle');
+    } else if (_isolate.running) {
+      console.printLine("Isolate is running (type 'pause' to interrupt)");
+    } else if (_isolate.pauseEvent != null) {
+      _reportPause(_isolate.pauseEvent);
+    } else {
+      console.printLine('Isolate is in unknown state');
+    }
+  }
+
+  void _reportPause(ServiceEvent event) {
+    if (event.eventType == 'IsolateCreated') {
+      console.printLine(
+          "Paused at isolate start (type 'continue' to start the isolate')");
+    } else if (event.eventType == 'IsolateShutdown') {
+      console.printLine(
+          "Paused at isolate exit (type 'continue' to exit the isolate')");
+    }
+    if (stack['frames'].length > 0) {
+      var frame = stack['frames'][0];
+      var script = frame['script'];
+      script.load().then((_) {
+        var line = script.tokenToLine(frame['tokenPos']);
+        var col = script.tokenToCol(frame['tokenPos']);
+        if (event.breakpoint != null) {
+          var bpId = event.breakpoint['breakpointNumber'];
+          console.printLine('Breakpoint ${bpId} at ${script.name}:${line}:${col}');
+        } else if (event.exception != null) {
+          // TODO(turnidge): Test this.
+          console.printLine(
+              'Exception ${event.exception} at ${script.name}:${line}:${col}');
+        } else {
+          console.printLine('Paused at ${script.name}:${line}:${col}');
+        }
+      });
+    }
+  }
+
+  void _onEvent(ServiceEvent event) {
+    if (event.owner != isolate) {
+      return;
+    }
+    switch(event.eventType) {
+      case 'IsolateShutdown':
+        console.printLine('Isolate shutdown');
+        isolate = null;
+        break;
+
+      case 'BreakpointReached':
+      case 'IsolateInterrupted':
+      case 'ExceptionThrown':
+        _refreshStack(event).then((_) {
+          _reportPause(event);
+        });
+        break;
+
+      case 'IsolateResumed':
+        console.printLine('Continuing...');
+        break;
+
+      case '_Graph':
+      case 'BreakpointResolved':
+      case 'IsolateCreated':
+      case 'GC':
+        // Ignore these events for now.
+        break;
+
+      default:
+        console.printLine('Unrecognized event: $event');
+        break;
+    }
+  }
+
+  String complete(String line) {
+    List<String> completions = cmd.completeCommand(line);
+    if (completions.length == 0) {
+      // No completions.  Leave the line alone.
+      return line;
+    } else if (completions.length == 1) {
+      // Unambiguous completion.
+      return completions[0];
+    } else {
+      // Ambigous completion.
+      completions = completions.map((s )=> s.trimRight()).toList();
+      completions.sort();
+      console.printBold(completions.toString());
+
+      // TODO(turnidge): Complete to common prefix of all completions.
+      return line;
+    }
+  }
+
+  // TODO(turnidge): Implement real command line history.
+  String lastCommand;
+  bool busy = false;
+
+  Future run(String command) {
+    assert(!busy);
+    busy = true;
+    if (command == '') {
+      command = lastCommand;
+    }
+    lastCommand = command;
+    console.printBold('\$ $command');
+    return cmd.runCommand(command).then((_) {
+      busy = false;
+    }).catchError((e) {
+      console.printLine('ERROR $e');
+    });
+  }
+}
+
 @CustomTag('debugger-page')
 class DebuggerPageElement extends ObservatoryElement {
   @published Isolate isolate;
-  @published bool showConsole = false;
+
+  isolateChanged(oldValue) {
+    if (isolate != null) {
+      debugger.isolate = isolate;
+    }
+  }
+  Debugger debugger = new Debugger();
 
   DebuggerPageElement.created() : super.created();
 
@@ -20,34 +436,127 @@
   void attached() {
     super.attached();
 
-    // TODO(turnidge): Get these values from the DOM.
-    // TODO(turnidge): splitterHeight is 0 until I implement it.
-    const int navbarHeight = 56;
-    const int splitterHeight = 0;
-    const int cmdHeight = 22;
+    var navbarDiv = $['navbarDiv'];
+    var stackDiv = $['stackDiv'];
+    var splitterDiv = $['splitterDiv'];
+    var cmdDiv = $['commandDiv'];
+    var consoleDiv = $['consoleDiv'];
 
-    var stack = $['stack'];
+    int navbarHeight = navbarDiv.clientHeight;
+    int splitterHeight = splitterDiv.clientHeight;
+    int cmdHeight = cmdDiv.clientHeight;
+
     int windowHeight = window.innerHeight;
-    int available = windowHeight - (navbarHeight + splitterHeight);
-    int stackHeight = available ~/ 1.3;
-    if (showConsole) {
-      stack.style.setProperty('height', '${stackHeight}px');
-    } else {
-      stack.style.setProperty('height', '${available}px');
-    }
+    int fixedHeight = navbarHeight + splitterHeight + cmdHeight;
+    int available = windowHeight - fixedHeight;
+    int stackHeight = available ~/ 1.6;
+    stackDiv.style.setProperty('height', '${stackHeight}px');
+
+    // Wire the debugger object to the stack, console, and command line.
+    var stackElement = $['stackElement'];
+    debugger.stackElement = stackElement;
+    stackElement.debugger = debugger;
+    debugger.console = $['console'];
+    $['commandline'].debugger = debugger;
+    debugger.init();
   }
+
 }
 
 @CustomTag('debugger-stack')
 class DebuggerStackElement extends ObservatoryElement {
   @published Isolate isolate;
-  @published ServiceMap stack;
-  @published int activeFrame = 0;
+  @observable bool hasStack = false;
+  @observable bool isSampled = false;
+  Debugger debugger = null;
 
-  isolateChanged(oldValue) {
-    isolate.getStack().then((result) {
-        stack = result;
-    });
+  _addFrame(List frameList, ObservableMap frameInfo, bool expand) {
+    DebuggerFrameElement frameElement = new Element.tag('debugger-frame');
+    frameElement.expand = expand;
+    frameElement.frame = frameInfo;
+
+    var li = new LIElement();
+    li.classes.add('list-group-item');
+    li.children.insert(0, frameElement);
+
+    frameList.insert(0, li);
+  }
+
+  void updateStack(ServiceMap newStack, ServiceEvent pauseEvent) {
+    List frameElements = $['frameList'].children;
+    List newFrames = newStack['frames'];
+
+    // Remove any frames whose functions don't match, starting from
+    // bottom of stack.
+    int oldPos = frameElements.length - 1;
+    int newPos = newFrames.length - 1;
+    while (oldPos >= 0 && newPos >= 0) {
+      if (!frameElements[oldPos].children[0].matchFrame(newFrames[newPos])) {
+        // The rest of the frame elements no longer match.  Remove them.
+        for (int i = 0; i <= oldPos; i++) {
+          // NOTE(turnidge): removeRange is missing, sadly.
+          frameElements.removeAt(0);
+        }
+        break;
+      }
+      oldPos--;
+      newPos--;
+    }
+
+    // Remove any extra frames.
+    if (frameElements.length > newFrames.length) {
+      // Remove old frames from the top of stack.
+      int removeCount = frameElements.length - newFrames.length;
+      for (int i = 0; i < removeCount; i++) {
+        frameElements.removeAt(0);
+      }
+    }
+
+    // Add any new frames.
+    int newCount = 0;
+    if (frameElements.length < newFrames.length) {
+      // Add new frames to the top of stack.
+      newCount = newFrames.length - frameElements.length;
+      for (int i = newCount-1; i >= 0; i--) {
+        _addFrame(frameElements, newFrames[i], i == 0);
+      }
+    }
+    assert(frameElements.length == newFrames.length);
+
+    if (frameElements.isNotEmpty) {
+      frameElements[0].children[0].expand = true;
+      for (int i = newCount; i < frameElements.length; i++) {
+        frameElements[i].children[0].updateFrame(newFrames[i]);
+      }
+    }
+
+    isSampled = pauseEvent == null;
+    hasStack = frameElements.isNotEmpty;
+  }
+
+  Set<Script> activeScripts() {
+    var s = new Set<Script>();
+    List frameElements = $['frameList'].children;
+    for (var frameElement in frameElements) {
+      s.add(frameElement.children[0].script);
+    }
+    return s;
+  }
+
+  doPauseIsolate(_) {
+    if (debugger != null) {
+      return debugger.isolate.pause();
+    } else {
+      return new Future.value(null);
+    }
+  }
+
+  doRefreshStack(_) {
+    if (debugger != null) {
+      return debugger.refreshStack();
+    } else {
+      return new Future.value(null);
+    }
   }
 
   DebuggerStackElement.created() : super.created();
@@ -66,6 +575,19 @@
 
   DebuggerFrameElement.created() : super.created();
 
+  bool matchFrame(ObservableMap newFrame) {
+    return newFrame['function'].id == frame['function'].id;
+  }
+
+  void updateFrame(ObservableMap newFrame) {
+    assert(matchFrame(newFrame));
+    frame['depth'] = newFrame['depth'];
+    frame['tokenPos'] = newFrame['tokenPos'];
+    frame['vars'] = newFrame['vars'];
+  }
+
+  Script get script => frame['script'];
+
   @override
   void attached() {
     super.attached();
@@ -102,12 +624,35 @@
   @published Isolate isolate;
 
   DebuggerConsoleElement.created() : super.created();
+
+  void printLine(String line) {
+    var div = new DivElement();
+    div.classes.add('normal');
+    div.appendText(line);
+    $['consoleText'].children.add(div);
+    div.scrollIntoView();
+  }
+
+  void printBold(String line) {
+    var div = new DivElement();
+    div.classes.add('bold');
+    div.appendText(line);
+    $['consoleText'].children.add(div);
+    div.scrollIntoView();
+  }
+
+  void newline() {
+    var br = new BRElement();
+    $['consoleText'].children.add(br);
+    br.scrollIntoView();
+  }
 }
 
 @CustomTag('debugger-input')
 class DebuggerInputElement extends ObservatoryElement {
   @published Isolate isolate;
   @published String text = '';
+  @observable Debugger debugger;
 
   @override
   void ready() {
@@ -118,13 +663,17 @@
 	switch (e.keyCode) {
           case KeyCode.TAB:
             e.preventDefault();
-            textBox.setRangeText('TAB');
-            textBox.setSelectionRange(textBox.selectionStart + 3,
-                                      textBox.selectionStart + 3);
+            int cursorPos = textBox.selectionStart;
+            var completion = debugger.complete(text.substring(0, cursorPos));
+            text = completion + text.substring(cursorPos);
+            // TODO(turnidge): Move the cursor to the end of the
+            // completion, rather than the end of the string.
             break;
           case KeyCode.ENTER:
-            print('Debugger command (not implemented): $text');
-            text = '';
+            if (!debugger.busy) {
+              debugger.run(text);
+              text = '';
+            }
             break;
 	}
       });
diff --git a/runtime/observatory/lib/src/elements/debugger.html b/runtime/observatory/lib/src/elements/debugger.html
index 6b15957..997ff77 100644
--- a/runtime/observatory/lib/src/elements/debugger.html
+++ b/runtime/observatory/lib/src/elements/debugger.html
@@ -40,6 +40,12 @@
         flex: 0 0 auto;
         overflow-y: auto;
       }
+      .splitter {
+        height: 0px;
+        margin: 0px;
+        font-size: 1px;
+        border-bottom: 1px solid #888;
+      }
       core-splitter {
         flex: 0 0 auto;
       }
@@ -53,26 +59,26 @@
     </style>
 
     <div class="container">
-      <nav-bar>
+      <nav-bar id="navbarDiv" showNotify="{{ false }}">
         <top-nav-menu></top-nav-menu>
         <isolate-nav-menu isolate="{{ isolate }}">
 	</isolate-nav-menu>
         <nav-menu link="{{ makeLink('/debugger', isolate) }}" anchor="debugger" last="{{ true }}"></nav-menu>
 	<nav-control></nav-control>
       </nav-bar>
-
-      <div id="stack" class="stack">
-	<debugger-stack isolate="{{ isolate }}"></debugger-stack>
+      <div id="stackDiv" class="stack">
+        <debugger-stack id="stackElement" isolate="{{ isolate }}"></debugger-stack>
       </div>
       <!--
       <core-splitter direction="up" allowOverflow=true></core-splitter>
-      <div class="console">
-	<debugger-console isolate="{{ isolate }}"></debugger-console>
-      </div>
-      <div class="commandline">
-	<debugger-input isolate="{{ isolate }}"></debugger-input>
-      </div>
       -->
+      <div id="splitterDiv"><hr class="splitter"></div>
+      <div id="consoleDiv" class="console">
+        <debugger-console id="console" isolate="{{ isolate }}"></debugger-console>
+      </div>
+      <div id="commandDiv" class="commandline">
+        <debugger-input id="commandline" isolate="{{ isolate }}"></debugger-input>
+      </div>
     </div>
   </template>
 </polymer-element>
@@ -80,20 +86,43 @@
 <polymer-element name="debugger-stack" extends="observatory-element">
   <template>
     <link rel="stylesheet" href="css/shared.css">
-    <template if="{{ stack == null }}">
-      Loading stack frames
+    <style>
+      .sampledMessage {
+        margin: 0px 20px 10px 20px;
+        font: 400 14px 'Montserrat', sans-serif;
+        line-height: 125%;
+      }
+      .splitter {
+        height: 0px;
+        margin: 0px;
+        font-size: 1px;
+        border-bottom: 1px dashed #888;
+      }
+      .noStack {
+        margin: 0px 20px 10px 20px;
+        font: normal 14px consolas, courier, monospace;
+        line-height: 125%;
+      }
+    </style>
+    <template if="{{ isSampled }}">
+      <div class="sampledMessage">
+        The program is not paused.  The stack trace below may be out of date.<br>
+        <br>
+        <action-link label="Pause Isolate" callback="{{ doPauseIsolate }}">
+        </action-link>
+        <action-link label="Refresh Stack" callback="{{ doRefreshStack }}">
+        </action-link>
+        <br>
+        <br>
+        <hr class="splitter">
+      </div>
     </template>
-    <template if="{{ stack != null }}">
-      <ul class="list-group">
-        <template repeat="{{ frame in stack['frames'] }}">
-          <li class="list-group-item">
-            <debugger-frame frame="{{ frame }}"
-                            expand="{{ frame['depth'] == activeFrame }}">
-            </debugger-frame>
-          </li>
-        </template>
-      </ul>
+    <template if="{{ !hasStack }}">
+      <div class="noStack">No stack</div>
     </template>
+    <ul id="frameList" class="list-group">
+      <!-- debugger-frame elements are added programmatically -->
+    </ul>
   </template>
 </polymer-element>
 
@@ -199,14 +228,24 @@
   <template>
     <link rel="stylesheet" href="css/shared.css">
     <style>
-      .textBox {
-        position: absolute;
-        bottom: 0px;
-        width: 100%;
+      .console {
+        margin: 0px 20px 10px 20px;
+      }
+      .normal {
+        font: normal 14px consolas, courier, monospace;
+        line-height: 125%;
+      }
+      .bold {
+        font: bold 14px consolas, courier, monospace;
+        line-height: 125%;
+      }
+      .spacer {
+        height: 20px;
       }
     </style>
-    <div>
-      Debugging console is not yet implemented.<br>
+    <div id="consoleText" class="console">
+      <!-- Console output is added programmatically here using the 'normal'
+           and 'bold' styles. -->
     </div>
   </template>
 </polymer-element>
@@ -216,11 +255,12 @@
     <link rel="stylesheet" href="css/shared.css">
     <style>
       .textBox {
-        font: 400 16px 'Montserrat', sans-serif;
-        width: 100%;
+        margin: 20px;
+        font: 400 16px consolas, courier, monospace;
+        width: 95%;
       }
     </style>
-    <input id="textBox" class="textBox" type="text" value="{{ text }}">
+    <input id="textBox" class="textBox" type="text" value="{{ text }}" autofocus>
   </template>
 </polymer-element>
 
diff --git a/runtime/observatory/lib/src/elements/isolate_summary.html b/runtime/observatory/lib/src/elements/isolate_summary.html
index 5331b1e..cd29e17 100644
--- a/runtime/observatory/lib/src/elements/isolate_summary.html
+++ b/runtime/observatory/lib/src/elements/isolate_summary.html
@@ -15,11 +15,12 @@
       <div class="flex-item-10-percent">
         <isolate-ref ref="{{ isolate }}"></isolate-ref>
       </div>
-      <div class="flex-item-30-percent">
+      <div class="flex-item-10-percent">
         <isolate-run-state isolate="{{ isolate }}"></isolate-run-state>
       </div>
-      <div class="flex-item-40-percent">
+      <div class="flex-item-60-percent">
         <isolate-location isolate="{{ isolate }}"></isolate-location>
+        [<a on-click="{{ goto }}" _href="{{ gotoLink('/debugger', isolate) }}">debug</a>]
       </div>
       <div class="flex-item-10-percent">
       </div>
@@ -42,21 +43,14 @@
   <template>
     <template if="{{ isolate.pauseEvent != null }}">
       <strong>paused</strong>
-      <action-link callback="{{ resume }}" label="resume"></action-link>
-
-      <action-link callback="{{ stepInto }}" label="step"></action-link>
-      <action-link callback="{{ stepOver }}" label="step&nbsp;over"></action-link>
-      <action-link callback="{{ stepOut }}" label="step&nbsp;out"></action-link>
     </template>
 
     <template if="{{ isolate.running }}">
       <strong>running</strong>
-      <action-link callback="{{ pause }}" label="pause"></action-link>
     </template>
 
     <template if="{{ isolate.idle }}">
       <strong>idle</strong>
-      <action-link callback="{{ pause }}" label="pause"></action-link>
     </template>
 
     <template if="{{ isolate.loading }}">
diff --git a/runtime/observatory/lib/src/elements/nav_bar.dart b/runtime/observatory/lib/src/elements/nav_bar.dart
index 75e7d66..0820eb8 100644
--- a/runtime/observatory/lib/src/elements/nav_bar.dart
+++ b/runtime/observatory/lib/src/elements/nav_bar.dart
@@ -13,6 +13,7 @@
 
 @CustomTag('nav-bar')
 class NavBarElement extends ObservatoryElement {
+  @published bool showNotify = true;
   @published bool pad = true;
 
   NavBarElement.created() : super.created();
diff --git a/runtime/observatory/lib/src/elements/nav_bar.html b/runtime/observatory/lib/src/elements/nav_bar.html
index bb6c045..97c300a 100644
--- a/runtime/observatory/lib/src/elements/nav_bar.html
+++ b/runtime/observatory/lib/src/elements/nav_bar.html
@@ -33,7 +33,9 @@
     </style>
     <nav>
       <ul>
-        <nav-notify events="{{ app.notifications }}"></nav-notify>
+        <template if="{{ showNotify }}">
+          <nav-notify events="{{ app.notifications }}"></nav-notify>
+        </template>
         <content></content>
       </ul>
     </nav>
@@ -274,7 +276,7 @@
         margin-top: 10px;
         margin-right: 10px;
         padding-right: 25px;
-        width: 200px;
+        width: 225px;
         color: #ddd;
         background: rgba(0,0,0,.6);
         border: solid 2px white;
@@ -323,21 +325,16 @@
            _href="{{ gotoLink('/inspect', event.isolate) }}">{{ event.isolate.name }}</a>
         is paused
         <template if="{{ event.breakpoint != null }}">
-          at breakpoint
+          at breakpoint {{ event.breakpoint['breakpointNumber'] }}
         </template>
         <template if="{{ event.eventType == 'ExceptionThrown' }}">
           at exception
         </template>
 
         <br><br>
-        <action-link callback="{{ resume }}" label="resume" color="white">
-        </action-link>
-        <action-link callback="{{ stepInto }}" label="step" color="white">
-        </action-link>
-        <action-link callback="{{ stepOver }}" label="step&nbsp;over"
-                     color="white"></action-link>
-        <action-link callback="{{ stepOut }}" label="step&nbsp;out"
-                     color="white"></action-link>
+        [<a class="link" on-click="{{ goto }}"
+            _href="{{ gotoLink('/debugger', event.isolate) }}">debug</a>]
+
         <a class="boxclose" on-click="{{ closeItem }}">&times;</a>
       </div>
     </template>
diff --git a/runtime/observatory/lib/src/elements/script_inset.dart b/runtime/observatory/lib/src/elements/script_inset.dart
index f129f71..aef3fe3 100644
--- a/runtime/observatory/lib/src/elements/script_inset.dart
+++ b/runtime/observatory/lib/src/elements/script_inset.dart
@@ -23,6 +23,7 @@
   @published int endPos;
 
   @observable int currentLine;
+  @observable int currentCol;
   @observable int startLine;
   @observable int endLine;
   @observable bool linesReady = false;
@@ -34,6 +35,16 @@
     return 'line-$line';
   }
 
+  String clip(String line, int start, [int limit]) {
+    try {
+      return line.substring(start, limit);
+    } catch (_) {
+      // NOTE(turnidge): Sometimes polymer updates give us garbage
+      // starts and limits during page updates.
+      return "OOB";
+    }
+  }
+
   MutationObserver _observer;
 
   void _scrollToCurrentPos() {
@@ -108,6 +119,9 @@
     currentLine = (currentPos != null
                    ? script.tokenToLine(currentPos)
                    : null);
+    currentCol = (currentPos != null
+                  ? (script.tokenToCol(currentPos) - 1)  // make this 0-based.
+                  : null);
     endLine = (endPos != null
                ? script.tokenToLine(endPos)
                : script.lines.length);
diff --git a/runtime/observatory/lib/src/elements/script_inset.html b/runtime/observatory/lib/src/elements/script_inset.html
index fb8c2dc..e01110a 100644
--- a/runtime/observatory/lib/src/elements/script_inset.html
+++ b/runtime/observatory/lib/src/elements/script_inset.html
@@ -25,7 +25,10 @@
         line-height: 125%;
         white-space: pre;
       }
-      .sourceItemCurrent {
+      .currentLine {
+        background-color: #fff;
+      }
+      .currentCol {
         background-color: #6cf;
       }
       .hitsNone, .hitsNotExecuted, .hitsExecuted {
@@ -69,7 +72,13 @@
                   <div class="sourceItem">&nbsp;</div>
 
                   <template if="{{ line.line == currentLine }}">
-                    <div class="sourceItemCurrent">{{line.text}}</div>
+                    <div class="sourceItem"><span class="currentLine">{{
+                          clip(line.text,0,currentCol
+                        )}}</span><span class="currentCol">{{
+                          clip(line.text,currentCol,currentCol+1)
+                        }}</span><span class="currentLine">{{
+                          clip(line.text,currentCol+1)
+                        }}</span></div>
                   </template>
                   <template if="{{ line.line != currentLine }}">
                     <div class="sourceItem">{{line.text}}</div>
diff --git a/runtime/observatory/lib/src/elements/service_ref.dart b/runtime/observatory/lib/src/elements/service_ref.dart
index f8ef42e..e238141 100644
--- a/runtime/observatory/lib/src/elements/service_ref.dart
+++ b/runtime/observatory/lib/src/elements/service_ref.dart
@@ -130,7 +130,6 @@
       return;
     }
     children.add(element);
-    Logger.root.info('Viewing object of \'${type}\'');
   }
 }
 
diff --git a/runtime/observatory/lib/src/elements/service_view.dart b/runtime/observatory/lib/src/elements/service_view.dart
index ad12afd..eacd996 100644
--- a/runtime/observatory/lib/src/elements/service_view.dart
+++ b/runtime/observatory/lib/src/elements/service_view.dart
@@ -176,7 +176,6 @@
       return;
     }
     children.add(element);
-    Logger.root.info('Viewing object of \'${type}\'');
   }
 }
 
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index f4ec057..47e45be 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -1021,8 +1021,7 @@
     return node;
   }
 
-  // TODO(turnidge): Make this an ObservableList instead.
-  ServiceMap breakpoints;
+  ObservableList breakpoints = new ObservableList();
 
   void _removeBreakpoint(ServiceMap bpt) {
     var script = bpt['location']['script'];
@@ -1058,7 +1057,7 @@
   void _updateBreakpoints(ServiceMap newBreakpoints) {
     // Remove all of the old breakpoints from the Script lines.
     if (breakpoints != null) {
-      for (var bpt in breakpoints['breakpoints']) {
+      for (var bpt in breakpoints) {
         _removeBreakpoint(bpt);
       }
     }
@@ -1066,7 +1065,11 @@
     for (var bpt in newBreakpoints['breakpoints']) {
       _addBreakpoint(bpt);
     }
-    breakpoints = newBreakpoints;
+    breakpoints.clear();
+    breakpoints.addAll(newBreakpoints['breakpoints']);
+
+    // Sort the breakpoints by breakpointNumber.
+    breakpoints.sort((a, b) => (a['breakpointNumber'] - b['breakpointNumber']));
   }
 
   Future<ServiceObject> _inProgressReloadBpts;
@@ -1122,6 +1125,9 @@
       });
   }
 
+  // TODO(turnidge): If the user invokes pause (or other rpcs) twice,
+  // they could get a race.  Consider returning an "in progress"
+  // future to avoid this.
   Future pause() {
     return invokeRpc('pause', {}).then((result) {
         if (result is DartError) {
@@ -1938,7 +1944,7 @@
     possibleBpt = !_isTrivialLine(text);
 
     // TODO(turnidge): This is not so efficient.  Consider improving.
-    for (var bpt in this.script.isolate.breakpoints['breakpoints']) {
+    for (var bpt in this.script.isolate.breakpoints) {
       var bptScript = bpt['location']['script'];
       var bptTokenPos = bpt['location']['tokenPos'];
       if (bptScript == this.script &&
diff --git a/runtime/observatory/test/debugging_test.dart b/runtime/observatory/test/debugging_test.dart
index 7fa5891..ac6b48c 100644
--- a/runtime/observatory/test/debugging_test.dart
+++ b/runtime/observatory/test/debugging_test.dart
@@ -46,12 +46,12 @@
       isolate.vm.events.stream.listen((ServiceEvent event) {
         if (event.eventType.startsWith('Breakpoint')) {
           events.add(event);
-        }
-        if (events.length == 2) {
-          expect(events[0].eventType, equals('BreakpointResolved'));
-          expect(events[1].eventType, equals('BreakpointReached'));
-          print('Breakpoint reached');
-          completer.complete();
+          if (events.length == 2) {
+            expect(events[0].eventType, equals('BreakpointResolved'));
+            expect(events[1].eventType, equals('BreakpointReached'));
+            print('Breakpoint reached');
+            completer.complete();
+          }
         }
       });
 
@@ -63,8 +63,7 @@
           expect(m.type, equals('Breakpoint'));
           expect(m['location']['script'].id, equals(script.id));
           expect(m['location']['tokenPos'], equals(51));
-          expect(isolate.breakpoints.type, equals('BreakpointList'));
-          expect(isolate.breakpoints['breakpoints'].length, equals(1));
+          expect(isolate.breakpoints.length, equals(1));
           return completer.future;  // Wait for breakpoint events.
       });
     });
@@ -87,9 +86,9 @@
   isolate.vm.events.stream.listen((ServiceEvent event) {
     if (event.eventType.startsWith('Breakpoint')) {
       expect(event.eventType, equals('BreakpointReached'));
+      print('Breakpoint reached');
+      completer.complete();
     }
-    print('Breakpoint reached');
-    completer.complete();
   });
   
   return isolate.stepInto().then((isolate) {
@@ -108,10 +107,10 @@
 
 // Remove breakpoint
 (Isolate isolate) {
-  expect(isolate.breakpoints['breakpoints'].length, equals(1));
-  var bpt = isolate.breakpoints['breakpoints'][0];
+  expect(isolate.breakpoints.length, equals(1));
+  var bpt = isolate.breakpoints[0];
   return isolate.removeBreakpoint(bpt).then((_) {
-      expect(isolate.breakpoints['breakpoints'].length, equals(0));
+      expect(isolate.breakpoints.length, equals(0));
   });
 },
 
diff --git a/runtime/tests/vm/dart/random_walk_fuzzer.dart b/runtime/tests/vm/dart/random_walk_fuzzer.dart
new file mode 100644
index 0000000..71c0513
--- /dev/null
+++ b/runtime/tests/vm/dart/random_walk_fuzzer.dart
@@ -0,0 +1,350 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Random-walk fuzzer for the Dart VM.
+//
+// Start with all the classes and libraries and various interesting values.
+// Repeatedly choose one as a receiver, construct a message it is likely to
+// understand, send the message, and add the result.
+//
+// Intentionally not run on the build bots because through dart:io it could
+// trash their setups.
+
+library fuzzer;
+
+import 'dart:io';
+import 'dart:math';
+import 'dart:mirrors';
+import 'dart:typed_data';
+
+var blacklist = [
+  'dart.io.exit',
+  'dart.io.exitCode',
+  'dart.io.sleep',
+  'dart.io.Process.killPid',
+];
+
+final bool trace = false;
+
+void main(List<String> args) {
+  int seed;
+  if (args.length == 1) {
+    seed = int.parse(args[0]);
+  } else {
+    // Dart's built-in random number generator doesn't provide access to the
+    // seed when it is choosen by the implementation. We need to be able to
+    // report this seed to make runs of the fuzzer reproducible, so we create
+    // the seed ourselves.
+
+    // When running on many machines in parallel, the current time alone
+    // is a poor choice of seed.
+    seed = 0;
+    try {
+      var f = new File("/dev/urandom").openSync();
+      seed = (seed << 8) | f.readByteSync();
+      seed = (seed << 8) | f.readByteSync();
+      seed = (seed << 8) | f.readByteSync();
+      seed = (seed << 8) | f.readByteSync();
+      f.close();
+    } catch(e) {
+      print("Failed to read from /dev/urandom: $e");
+    }
+
+    seed ^= new DateTime.now().millisecondsSinceEpoch;
+    seed &= 0xFFFFFFFF;
+  }
+  random = new Random(seed);
+
+  // Information needed to reproduce this run.
+  print("Dart VM fuzzer");
+  print("Executable: ${Platform.executable}");
+  print("Arguments: ${Platform.executableArguments}");
+  print("Version: ${Platform.version}");
+  print("Seed: ${seed}");
+  print("------------------------------------------");
+
+  setupInterestingValues();
+  setupClasses();
+
+  // Bound the number of steps in our random walk so that if any issue is found
+  // it can be reproduced without having to wait too long.
+  for (int i = 0; i < 100000; i++) {
+    fuzz(randomElementOf(candidateReceivers));
+    if (maybe(0.01)) garbageCollect();
+  }
+}
+
+Random random;
+
+bool maybe(probability) => random.nextDouble() < probability;
+
+randomElementOf(list) {
+  return list.length == 0 ? null : list[random.nextInt(list.length)];
+}
+
+class Candidate<T> {
+  Candidate origin;
+  String message;
+  T mirror;
+  trace() {
+    if (origin == null) {
+      print("");
+    } else {
+      origin.trace();
+      print(" $message");
+    }
+    print(mirror);
+  }
+}
+
+List<Candidate<ObjectMirror>> candidateReceivers =
+    new List<Candidate<ObjectMirror>>();
+List<Candidate<InstanceMirror>> candidateArguments =
+    new List<Candidate<InstanceMirror>>();
+
+void addInstance(var instance) {
+  addInstanceMirror(reflect(instance));
+}
+
+void addInstanceMirror(InstanceMirror mirror,
+                       [Candidate origin, String message]) {
+  var c = new Candidate<InstanceMirror>();
+  c.mirror = mirror;
+  c.origin = origin;
+  c.message = message;
+
+  candidateReceivers.add(c);
+  candidateArguments.add(c);
+}
+
+void addObjectMirror(ObjectMirror mirror) {
+  var c = new Candidate<ObjectMirror>();
+  c.mirror = mirror;
+  c.origin = null;
+  c.message = null;
+
+  candidateReceivers.add(c);
+}
+
+void setupInterestingValues() {
+  addInstance(null);
+  addInstance(true);
+  addInstance(false);
+
+  addInstance([]);
+  addInstance(const []);
+  addInstance({});
+  addInstance(const {});
+
+  addInstance(() => null);
+
+  addInstance(-1);
+  addInstance(0);
+  addInstance(1);
+  addInstance(2);
+
+  addInstance(1 << 31);
+  addInstance(1 << 31 + 1);
+  addInstance(1 << 31 - 1);
+
+  addInstance(1 << 32);
+  addInstance(1 << 32 + 1);
+  addInstance(1 << 32 - 1);
+
+  addInstance(1 << 63);
+  addInstance(1 << 63 + 1);
+  addInstance(1 << 63 - 1);
+
+  addInstance(1 << 64);
+  addInstance(1 << 64 + 1);
+  addInstance(1 << 64 - 1);
+
+  addInstance(-1.0);
+  addInstance(0.0);
+  addInstance(1.0);
+  addInstance(2.0);
+  addInstance(double.NAN);
+  addInstance(double.INFINITY);
+  addInstance(double.NEGATIVE_INFINITY);
+  addInstance(double.MIN_POSITIVE);
+  addInstance(double.MAX_FINITE);
+
+  addInstance("foo");  // ASCII string
+  addInstance("blåbærgrød");  // Latin1 string
+  addInstance("Îñţérñåţîöñåļîžåţîờñ");  // Unicode string
+  addInstance("𝄞");  // Surrogate pairs
+  addInstance("𝄞"[0]);  // Surrogate pairs
+  addInstance("𝄞"[1]);  // Surrogate pairs
+  addInstance("\u{0}");  // Non-printing charater
+  addInstance("\u{1}");  // Non-printing charater
+  addInstance("f\u{0}oo");  // Internal NUL
+  addInstance("blåbæ\u{0}rgrød");  // Internal NUL
+  addInstance("Îñţérñåţîö\u{0}ñåļîžåţîờñ");  // Internal NUL
+  addInstance("\u{0}𝄞");  // Internal NUL
+
+  for (int len = 0; len < 8; len++) {
+    addInstance(fillInt(new Int8List(len)));
+    addInstance(fillInt(new Int16List(len)));
+    addInstance(fillInt(new Int32List(len)));
+    addInstance(fillInt(new Int64List(len)));
+    addInstance(fillInt(new Uint8List(len)));
+    addInstance(fillInt(new Uint16List(len)));
+    addInstance(fillInt(new Uint32List(len)));
+    addInstance(fillInt(new Uint64List(len)));
+    addInstance(fillFloat(new Float32List(len)));
+    addInstance(fillFloat(new Float64List(len)));
+  }
+
+  randomInstance(ignore) {
+    return randomElementOf(candidateArguments).mirror.reflectee;
+  }
+  for (int len = 0; len < 8; len++) {
+    addInstance(new List.generate(len, randomInstance));
+  }
+}
+
+void fillInt(TypedData d) {
+  for (var i = 0; i < d.length; i++) {
+    d[i] = random.nextInt(0xFFFFFFFF);
+  }
+}
+
+void fillFloat(TypedData d) {
+  for (var i = 0; i < d.length; i++) {
+    d[i] = random.nextDouble();
+  }
+}
+
+void setupClasses() {
+  currentMirrorSystem().libraries.values.forEach((lib) {
+    if (lib.simpleName == #fuzzer) return;  // Don't recurse.
+    addObjectMirror(lib);
+    lib.declarations.values.forEach((decl) {
+      if (decl is ClassMirror) {
+        addObjectMirror(decl);
+      }
+    });
+  });
+}
+
+MethodMirror randomMethodOf(receiver) {
+  if (receiver is ClassMirror) {
+    return randomElementOf(receiver.declarations.values.where(
+        (d) => d is MethodMirror && d.isStatic).toList());
+  } else if (receiver is LibraryMirror) {
+    return randomElementOf(receiver.declarations.values.where(
+        (d) => d is MethodMirror).toList());
+  } else if (receiver is InstanceMirror) {
+    var methods = [];
+    var cls = receiver.type;
+    while (cls != reflectClass(Object)) {
+      cls.declarations.values.forEach((d) {
+        if (d is MethodMirror && !d.isStatic) methods.add(d);
+      });
+      cls = cls.superclass;
+    }
+    return randomElementOf(methods);
+  }
+  throw new Error("UNREACHABLE");
+}
+
+String prettyMessageName(receiver, method) {
+  var r = "?", m = "?";
+  if (receiver is InstanceMirror) {
+    r = MirrorSystem.getName(receiver.type.simpleName);
+  } else if (receiver is ClassMirror) {
+    r = MirrorSystem.getName(receiver.simpleName);
+    r = "$r class";
+  } else if (receiver is LibraryMirror) {
+    r = MirrorSystem.getName(receiver.simpleName);
+    r = "$r lib";
+  }
+  m = MirrorSystem.getName(method.simpleName);
+  return "$r>>#$m";
+}
+
+void fuzz(Candidate c) {
+  ObjectMirror receiver = c.mirror;
+  MethodMirror method = randomMethodOf(receiver);
+  if (method == null) return;
+  if (blacklist.contains(MirrorSystem.getName(method.qualifiedName))) return;
+
+  List positional = randomPositionalArgumentsFor(method);
+  Map named = randomNamedArgumentsFor(method);
+  InstanceMirror result;
+
+  String message = prettyMessageName(receiver, method);
+  if (trace) {
+    c.trace();
+    print(message);
+  }
+
+  if (method.isConstructor) {
+    try {
+      result = receiver.newInstance(method.simpleName, positional, named);
+    } catch(e) {}
+  } else if (method.isRegularMethod) {
+    try {
+      result = receiver.invoke(method.simpleName, positional, named);
+    } catch(e) {}
+  } else if (method.isGetter) {
+    try {
+      result = receiver.getField(method.simpleName);
+    } catch(e) {}
+  } else if (method.isSetter) {
+    try {
+      result = receiver.setField(method.simpleName, positional[0]);
+    } catch(e) {}
+  }
+
+  if (result != null) {
+    addInstanceMirror(result, c, message);
+  }
+}
+
+InstanceMirror randomArgumentWithBias(TypeMirror bias) {
+  if (maybe(0.75)) {
+    for (var candidate in candidateArguments) {
+      if (candidate.mirror.type.isAssignableTo(bias)) {
+        return candidate.mirror;
+      }
+    }
+  }
+  return randomElementOf(candidateArguments).mirror;
+}
+
+List randomPositionalArgumentsFor(MethodMirror method) {
+  var result = [];
+  for (int i = 0; i < method.parameters.length; i++) {
+    ParameterMirror p = method.parameters[i];
+    if (!p.isNamed && (!p.isOptional || maybe(0.5))) {
+      result.add(randomArgumentWithBias(p.type));
+    }
+  }
+  return result;
+}
+
+Map randomNamedArgumentsFor(MethodMirror method) {
+  var result = {};
+  for (int i = 0; i < method.parameters.length; i++) {
+    ParameterMirror p = method.parameters[i];
+    if (p.isNamed && maybe(0.5)) {
+      result[p.simpleName] = randomArgumentWithBias(p.type);
+    }
+  }
+
+  return result;
+}
+
+void garbageCollect() {
+  // Chain a bunch of moderately sized arrays, then let go of them. Using a
+  // moderate size avoids our allocations going directly to a large object 
+  // page in old space.
+  var n;
+  for (int i = 0; i < 2048; i++) {
+    var m = new List(512);
+    m[0] = n;
+    n = m;
+  }
+}
diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status
index dfe5f584..7b40c03 100644
--- a/runtime/tests/vm/vm.status
+++ b/runtime/tests/vm/vm.status
@@ -88,3 +88,6 @@
 
 [ $runtime != vm ]
 dart/snapshot_version_test: SkipByDesign  # Spawns processes
+
+[ $runtime == vm && $mode == debug && $builder_tag == asan ]
+cc/Dart2JSCompileAll: Skip  # Timeout.
diff --git a/runtime/vm/assembler.cc b/runtime/vm/assembler.cc
index f259071..23d08e0 100644
--- a/runtime/vm/assembler.cc
+++ b/runtime/vm/assembler.cc
@@ -241,4 +241,63 @@
   return comments;
 }
 
+
+intptr_t ObjectPool::AddObject(const Object& obj, Patchability patchable) {
+  // The object pool cannot be used in the vm isolate.
+  ASSERT(Isolate::Current() != Dart::vm_isolate());
+  if (object_pool_.IsNull()) {
+    object_pool_ = GrowableObjectArray::New(Heap::kOld);
+  }
+  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, object_pool_.Length() - 1));
+  }
+  return object_pool_.Length() - 1;
+}
+
+
+intptr_t ObjectPool::AddExternalLabel(const ExternalLabel* label,
+                                      Patchability patchable) {
+  ASSERT(Isolate::Current() != Dart::vm_isolate());
+  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));
+  return AddObject(smi, patchable);
+}
+
+
+intptr_t ObjectPool::FindObject(const Object& obj, Patchability patchable) {
+  // The object pool cannot be used in the vm isolate.
+  ASSERT(Isolate::Current() != Dart::vm_isolate());
+
+  // If the object is not patchable, check if we've already got it in the
+  // object pool.
+  if (patchable == kNotPatchable && !object_pool_.IsNull()) {
+    intptr_t idx = object_pool_index_table_.Lookup(&obj);
+    if (idx != ObjIndexPair::kNoIndex) {
+      ASSERT(patchable_pool_entries_[idx] == kNotPatchable);
+      return idx;
+    }
+  }
+
+  return AddObject(obj, patchable);
+}
+
+
+intptr_t ObjectPool::FindExternalLabel(const ExternalLabel* label,
+                                       Patchability patchable) {
+  // The object pool cannot be used in the vm isolate.
+  ASSERT(Isolate::Current() != Dart::vm_isolate());
+  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));
+  return FindObject(smi, patchable);
+}
+
+
 }  // namespace dart
diff --git a/runtime/vm/assembler.h b/runtime/vm/assembler.h
index a671d63..d8249ba 100644
--- a/runtime/vm/assembler.h
+++ b/runtime/vm/assembler.h
@@ -9,6 +9,7 @@
 #include "vm/allocation.h"
 #include "vm/globals.h"
 #include "vm/growable_array.h"
+#include "vm/hash_map.h"
 #include "vm/object.h"
 
 namespace dart {
@@ -256,6 +257,37 @@
   Value value_;
 };
 
+
+enum Patchability {
+  kPatchable,
+  kNotPatchable,
+};
+
+
+class ObjectPool : public ValueObject {
+ public:
+  ObjectPool() : object_pool_(GrowableObjectArray::Handle()) { }
+
+  intptr_t AddObject(const Object& obj, Patchability patchable);
+  intptr_t AddExternalLabel(const ExternalLabel* label,
+                            Patchability patchable);
+
+  intptr_t FindObject(const Object& obj, Patchability patchable);
+  intptr_t FindExternalLabel(const ExternalLabel* label,
+                             Patchability patchable);
+  const GrowableObjectArray& data() const { return object_pool_; }
+
+ private:
+  // Objects and jump targets.
+  GrowableObjectArray& object_pool_;
+
+  // Patchability of pool entries.
+  GrowableArray<Patchability> patchable_pool_entries_;
+
+  // Hashmap for fast lookup in object pool.
+  DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
+};
+
 }  // namespace dart
 
 
diff --git a/runtime/vm/assembler_arm.cc b/runtime/vm/assembler_arm.cc
index 0268a62..1ec6a6e 100644
--- a/runtime/vm/assembler_arm.cc
+++ b/runtime/vm/assembler_arm.cc
@@ -1587,8 +1587,8 @@
     // Make sure that class CallPattern is able to decode this load from the
     // object pool.
     const int32_t offset =
-        Array::data_offset() + 4*AddObject(object) - kHeapObjectTag;
-    LoadWordFromPoolOffset(rd, offset, cond);
+        Array::element_offset(object_pool_.FindObject(object, kNotPatchable));
+    LoadWordFromPoolOffset(rd, offset - kHeapObjectTag, cond);
   }
 }
 
@@ -2673,8 +2673,8 @@
   // For added code robustness, use 'blx lr' in a patchable sequence and
   // use 'blx ip' in a non-patchable sequence (see other BranchLink flavors).
   const int32_t offset =
-      Array::data_offset() + 4*AddExternalLabel(label) - kHeapObjectTag;
-  LoadWordFromPoolOffset(LR, offset);
+      Array::element_offset(object_pool_.FindExternalLabel(label, kPatchable));
+  LoadWordFromPoolOffset(LR, offset - kHeapObjectTag);
   blx(LR);  // Use blx instruction so that the return branch prediction works.
 }
 
@@ -3543,44 +3543,6 @@
 }
 
 
-int32_t Assembler::AddObject(const Object& obj) {
-  ASSERT(obj.IsNotTemporaryScopedHandle());
-  ASSERT(obj.IsOld());
-  if (object_pool_.IsNull()) {
-    // The object pool cannot be used in the vm isolate.
-    ASSERT(Isolate::Current() != Dart::vm_isolate());
-    object_pool_ = GrowableObjectArray::New(Heap::kOld);
-  }
-
-  intptr_t index = object_pool_index_table_.Lookup(&obj);
-  if (index != ObjIndexPair::kNoIndex) {
-    return index;
-  }
-
-  object_pool_.Add(obj, Heap::kOld);
-  object_pool_index_table_.Insert(
-      ObjIndexPair(&obj, object_pool_.Length() - 1));
-  return object_pool_.Length() - 1;
-}
-
-
-int32_t Assembler::AddExternalLabel(const ExternalLabel* label) {
-  if (object_pool_.IsNull()) {
-    // The object pool cannot be used in the vm isolate.
-    ASSERT(Isolate::Current() != Dart::vm_isolate());
-    object_pool_ = GrowableObjectArray::New(Heap::kOld);
-  }
-  const word address = label->address();
-  ASSERT(Utils::IsAligned(address, 4));
-  // The address is stored in the object array as a RawSmi.
-  const Smi& smi = Smi::Handle(Smi::New(address >> kSmiTagShift));
-  // Do not reuse an existing entry, since each reference may be patched
-  // independently.
-  object_pool_.Add(smi, Heap::kOld);
-  return object_pool_.Length() - 1;
-}
-
-
 Address Assembler::ElementAddressForIntIndex(bool is_load,
                                              bool is_external,
                                              intptr_t cid,
diff --git a/runtime/vm/assembler_arm.h b/runtime/vm/assembler_arm.h
index c9f1c11..aef4a6b 100644
--- a/runtime/vm/assembler_arm.h
+++ b/runtime/vm/assembler_arm.h
@@ -316,7 +316,6 @@
  public:
   explicit Assembler(bool use_far_branches = false)
       : buffer_(),
-        object_pool_(GrowableObjectArray::Handle()),
         prologue_offset_(-1),
         use_far_branches_(use_far_branches),
         comments_(),
@@ -341,7 +340,7 @@
     ASSERT(buffer_.pointer_offsets().length() == 0);  // No pointers in code.
     return buffer_.pointer_offsets();
   }
-  const GrowableObjectArray& object_pool() const { return object_pool_; }
+  const GrowableObjectArray& object_pool() const { return object_pool_.data(); }
 
   bool use_far_branches() const {
     return FLAG_use_far_branches || use_far_branches_;
@@ -959,10 +958,7 @@
 
  private:
   AssemblerBuffer buffer_;  // Contains position independent code.
-  GrowableObjectArray& object_pool_;  // Objects and patchable jump targets.
-
-  // Hashmap for fast lookup in object pool.
-  DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
+  ObjectPool object_pool_;  // Objects and patchable jump targets.
 
   int32_t prologue_offset_;
 
@@ -973,9 +969,6 @@
   void movw(Register rd, uint16_t imm16, Condition cond = AL);
   void movt(Register rd, uint16_t imm16, Condition cond = AL);
 
-  int32_t AddObject(const Object& obj);
-  int32_t AddExternalLabel(const ExternalLabel* label);
-
   void BindARMv6(Label* label);
   void BindARMv7(Label* label);
 
diff --git a/runtime/vm/assembler_arm64.cc b/runtime/vm/assembler_arm64.cc
index 5320242..0045fbd 100644
--- a/runtime/vm/assembler_arm64.cc
+++ b/runtime/vm/assembler_arm64.cc
@@ -27,73 +27,61 @@
 
 Assembler::Assembler(bool use_far_branches)
     : buffer_(),
-      object_pool_(GrowableObjectArray::Handle()),
-      patchable_pool_entries_(),
       prologue_offset_(-1),
       use_far_branches_(use_far_branches),
       comments_(),
       allow_constant_pool_(true) {
   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);
-    object_pool_index_table_.Insert(ObjIndexPair(&Object::null_object(), 0));
+    intptr_t index =
+        object_pool_.AddObject(Object::null_object(), kNotPatchable);
+    ASSERT(index == 0);
 
-    object_pool_.Add(Bool::True(), Heap::kOld);
-    patchable_pool_entries_.Add(kNotPatchable);
-    object_pool_index_table_.Insert(ObjIndexPair(&Bool::True(), 1));
+    index = object_pool_.AddObject(Bool::True(), kNotPatchable);
+    ASSERT(index == 1);
 
-    object_pool_.Add(Bool::False(), Heap::kOld);
-    patchable_pool_entries_.Add(kNotPatchable);
-    object_pool_index_table_.Insert(ObjIndexPair(&Bool::False(), 2));
+    index = object_pool_.AddObject(Bool::False(), kNotPatchable);
+    ASSERT(index == 2);
 
     const Smi& vacant = Smi::Handle(Smi::New(0xfa >> kSmiTagShift));
     StubCode* stub_code = Isolate::Current()->stub_code();
-
     if (stub_code->UpdateStoreBuffer_entry() != NULL) {
-      FindExternalLabel(&stub_code->UpdateStoreBufferLabel(), kNotPatchable);
+      object_pool_.AddExternalLabel(
+          &stub_code->UpdateStoreBufferLabel(), kNotPatchable);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
 
     if (stub_code->CallToRuntime_entry() != NULL) {
-      FindExternalLabel(&stub_code->CallToRuntimeLabel(), kNotPatchable);
+      object_pool_.AddExternalLabel(
+          &stub_code->CallToRuntimeLabel(), kNotPatchable);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
 
     // Create fixed object pool entries for debugger stubs.
     if (stub_code->ICCallBreakpoint_entry() != NULL) {
       intptr_t index =
-          FindExternalLabel(&stub_code->ICCallBreakpointLabel(),
-                            kNotPatchable);
+          object_pool_.AddExternalLabel(&stub_code->ICCallBreakpointLabel(),
+                                        kNotPatchable);
       ASSERT(index == kICCallBreakpointCPIndex);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
     if (stub_code->ClosureCallBreakpoint_entry() != NULL) {
-      intptr_t index =
-          FindExternalLabel(&stub_code->ClosureCallBreakpointLabel(),
-                            kNotPatchable);
+      intptr_t index = object_pool_.AddExternalLabel(
+          &stub_code->ClosureCallBreakpointLabel(), kNotPatchable);
       ASSERT(index == kClosureCallBreakpointCPIndex);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
     if (stub_code->RuntimeCallBreakpoint_entry() != NULL) {
-      intptr_t index =
-          FindExternalLabel(&stub_code->RuntimeCallBreakpointLabel(),
-                            kNotPatchable);
+      intptr_t index = object_pool_.AddExternalLabel(
+          &stub_code->RuntimeCallBreakpointLabel(), kNotPatchable);
       ASSERT(index == kRuntimeCallBreakpointCPIndex);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
   }
 }
@@ -424,59 +412,10 @@
 }
 
 
-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) {
-    intptr_t idx = object_pool_index_table_.Lookup(&obj);
-    if (idx != ObjIndexPair::kNoIndex) {
-      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, 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);
+  return object_pool_.FindObject(smi, kNotPatchable);
 }
 
 
@@ -527,7 +466,7 @@
   const int64_t target = static_cast<int64_t>(label->address());
   if (CanLoadImmediateFromPool(target, pp)) {
     const int32_t offset =
-        Array::element_offset(FindExternalLabel(label, patchable));
+        Array::element_offset(object_pool_.FindExternalLabel(label, patchable));
     LoadWordFromPoolOffset(dst, pp, offset);
   } else {
     LoadImmediate(dst, target, kNoPP);
@@ -540,7 +479,7 @@
                                        Patchability patchable,
                                        Register pp) {
   const int32_t offset =
-      Array::element_offset(FindExternalLabel(label, patchable));
+      Array::element_offset(object_pool_.FindExternalLabel(label, patchable));
   LoadWordFromPoolOffsetFixed(dst, pp, offset);
 }
 
@@ -553,7 +492,7 @@
 void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
   if (CanLoadObjectFromPool(object)) {
     const int32_t offset =
-        Array::element_offset(FindObject(object, kNotPatchable));
+        Array::element_offset(object_pool_.FindObject(object, kNotPatchable));
     LoadWordFromPoolOffset(dst, pp, offset);
   } else {
     ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
diff --git a/runtime/vm/assembler_arm64.h b/runtime/vm/assembler_arm64.h
index cd934ac..30c61d4 100644
--- a/runtime/vm/assembler_arm64.h
+++ b/runtime/vm/assembler_arm64.h
@@ -475,7 +475,7 @@
     ASSERT(buffer_.pointer_offsets().length() == 0);  // No pointers in code.
     return buffer_.pointer_offsets();
   }
-  const GrowableObjectArray& object_pool() const { return object_pool_; }
+  const GrowableObjectArray& object_pool() const { return object_pool_.data(); }
 
   bool use_far_branches() const {
     return FLAG_use_far_branches || use_far_branches_;
@@ -1299,11 +1299,6 @@
   static const int kClosureCallBreakpointCPIndex = 6;
   static const int kRuntimeCallBreakpointCPIndex = 7;
 
-  enum Patchability {
-    kPatchable,
-    kNotPatchable,
-  };
-
   bool allow_constant_pool() const {
     return allow_constant_pool_;
   }
@@ -1313,9 +1308,6 @@
 
   void LoadWordFromPoolOffset(Register dst, Register pp, uint32_t offset);
   void LoadWordFromPoolOffsetFixed(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);
@@ -1425,13 +1417,7 @@
   AssemblerBuffer buffer_;  // Contains position independent code.
 
   // Objects and patchable jump targets.
-  GrowableObjectArray& object_pool_;
-
-  // Patchability of pool entries.
-  GrowableArray<Patchability> patchable_pool_entries_;
-
-  // Hashmap for fast lookup in object pool.
-  DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
+  ObjectPool object_pool_;
 
   int32_t prologue_offset_;
 
diff --git a/runtime/vm/assembler_arm_test.cc b/runtime/vm/assembler_arm_test.cc
index 7026c2e..f870156 100644
--- a/runtime/vm/assembler_arm_test.cc
+++ b/runtime/vm/assembler_arm_test.cc
@@ -62,13 +62,13 @@
 #if defined(USING_SIMULATOR)
   // ARMv7 is the default.
   HostCPUFeatures::set_arm_version(ARMv6);
-  __ LoadDecodableImmediate(R0, 0x12345678 << 1);
+  __ LoadPatchableImmediate(R0, 0x12345678 << 1);
   HostCPUFeatures::set_arm_version(ARMv7);
-  __ LoadDecodableImmediate(R1, 0x12345678);
+  __ LoadPatchableImmediate(R1, 0x12345678);
   __ sub(R0, R0, Operand(R1));
   __ bx(LR);
 #else
-  __ LoadDecodableImmediate(R0, 0x12345678);
+  __ LoadPatchableImmediate(R0, 0x12345678);
   __ bx(LR);
 #endif
 }
diff --git a/runtime/vm/assembler_mips.cc b/runtime/vm/assembler_mips.cc
index b07004d..b6e3a68 100644
--- a/runtime/vm/assembler_mips.cc
+++ b/runtime/vm/assembler_mips.cc
@@ -472,33 +472,12 @@
     // Make sure that class CallPattern is able to decode this load from the
     // object pool.
     const int32_t offset =
-        Array::data_offset() + 4*AddObject(object) - kHeapObjectTag;
-    LoadWordFromPoolOffset(rd, offset);
+        Array::element_offset(object_pool_.FindObject(object, kNotPatchable));
+    LoadWordFromPoolOffset(rd, offset - kHeapObjectTag);
   }
 }
 
 
-int32_t Assembler::AddObject(const Object& obj) {
-  ASSERT(obj.IsNotTemporaryScopedHandle());
-  ASSERT(obj.IsOld());
-  if (object_pool_.IsNull()) {
-    // The object pool cannot be used in the vm isolate.
-    ASSERT(Isolate::Current() != Dart::vm_isolate());
-    object_pool_ = GrowableObjectArray::New(Heap::kOld);
-  }
-
-  intptr_t index = object_pool_index_table_.Lookup(&obj);
-  if (index != ObjIndexPair::kNoIndex) {
-    return index;
-  }
-
-  object_pool_.Add(obj, Heap::kOld);
-  object_pool_index_table_.Insert(
-      ObjIndexPair(&obj, object_pool_.Length() - 1));
-  return object_pool_.Length() - 1;
-}
-
-
 void Assembler::PushObject(const Object& object) {
   ASSERT(!in_delay_slot_);
   LoadObject(TMP, object);
@@ -1156,23 +1135,6 @@
 }
 
 
-int32_t Assembler::AddExternalLabel(const ExternalLabel* label) {
-  if (object_pool_.IsNull()) {
-    // The object pool cannot be used in the vm isolate.
-    ASSERT(Isolate::Current() != Dart::vm_isolate());
-    object_pool_ = GrowableObjectArray::New(Heap::kOld);
-  }
-  const word address = label->address();
-  ASSERT(Utils::IsAligned(address, 4));
-  // The address is stored in the object array as a RawSmi.
-  const Smi& smi = Smi::Handle(Smi::New(address >> kSmiTagShift));
-  // Do not reuse an existing entry, since each reference may be patched
-  // independently.
-  object_pool_.Add(smi, Heap::kOld);
-  return object_pool_.Length() - 1;
-}
-
-
 Address Assembler::ElementAddressForIntIndex(bool is_external,
                                              intptr_t cid,
                                              intptr_t index_scale,
diff --git a/runtime/vm/assembler_mips.h b/runtime/vm/assembler_mips.h
index 2c0f3bb..86cd17f 100644
--- a/runtime/vm/assembler_mips.h
+++ b/runtime/vm/assembler_mips.h
@@ -236,7 +236,6 @@
  public:
   explicit Assembler(bool use_far_branches = false)
       : buffer_(),
-        object_pool_(GrowableObjectArray::Handle()),
         prologue_offset_(-1),
         use_far_branches_(use_far_branches),
         delay_slot_available_(false),
@@ -263,7 +262,7 @@
   const ZoneGrowableArray<intptr_t>& GetPointerOffsets() const {
     return buffer_.pointer_offsets();
   }
-  const GrowableObjectArray& object_pool() const { return object_pool_; }
+  const GrowableObjectArray& object_pool() const { return object_pool_.data(); }
   void FinalizeInstructions(const MemoryRegion& region) {
     buffer_.FinalizeInstructions(region);
   }
@@ -927,9 +926,9 @@
 
   void BranchLinkPatchable(const ExternalLabel* label) {
     ASSERT(!in_delay_slot_);
-    const int32_t offset =
-        Array::data_offset() + 4*AddExternalLabel(label) - kHeapObjectTag;
-    LoadWordFromPoolOffset(T9, offset);
+    const int32_t offset = Array::element_offset(
+        object_pool_.FindExternalLabel(label, kPatchable));
+    LoadWordFromPoolOffset(T9, offset - kHeapObjectTag);
     jalr(T9);
     delay_slot_available_ = false;  // CodePatcher expects a nop.
   }
@@ -1613,10 +1612,7 @@
 
  private:
   AssemblerBuffer buffer_;
-  GrowableObjectArray& object_pool_;  // Objects and patchable jump targets.
-
-  // Hashmap for fast lookup in object pool.
-  DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
+  ObjectPool object_pool_;  // Objects and patchable jump targets.
 
   intptr_t prologue_offset_;
 
@@ -1624,9 +1620,6 @@
   bool delay_slot_available_;
   bool in_delay_slot_;
 
-  int32_t AddObject(const Object& obj);
-  int32_t AddExternalLabel(const ExternalLabel* label);
-
   class CodeComment : public ZoneAllocated {
    public:
     CodeComment(intptr_t pc_offset, const String& comment)
diff --git a/runtime/vm/assembler_x64.cc b/runtime/vm/assembler_x64.cc
index 8205ce8..a3e6c26 100644
--- a/runtime/vm/assembler_x64.cc
+++ b/runtime/vm/assembler_x64.cc
@@ -23,8 +23,6 @@
 
 Assembler::Assembler(bool use_far_branches)
     : buffer_(),
-      object_pool_(GrowableObjectArray::Handle()),
-      patchable_pool_entries_(),
       prologue_offset_(-1),
       comments_(),
       allow_constant_pool_(true) {
@@ -32,66 +30,55 @@
   ASSERT(!use_far_branches);
   Isolate* isolate = Isolate::Current();
   if (isolate != 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);
-    object_pool_index_table_.Insert(ObjIndexPair(&Object::null_object(), 0));
+    intptr_t index =
+        object_pool_.AddObject(Object::null_object(), kNotPatchable);
+    ASSERT(index == 0);
 
-    object_pool_.Add(Bool::True(), Heap::kOld);
-    patchable_pool_entries_.Add(kNotPatchable);
-    object_pool_index_table_.Insert(ObjIndexPair(&Bool::True(), 1));
+    index = object_pool_.AddObject(Bool::True(), kNotPatchable);
+    ASSERT(index == 1);
 
-    object_pool_.Add(Bool::False(), Heap::kOld);
-    patchable_pool_entries_.Add(kNotPatchable);
-    object_pool_index_table_.Insert(ObjIndexPair(&Bool::False(), 2));
+    index = object_pool_.AddObject(Bool::False(), kNotPatchable);
+    ASSERT(index == 2);
 
     const Smi& vacant = Smi::Handle(Smi::New(0xfa >> kSmiTagShift));
-
     StubCode* stub_code = isolate->stub_code();
     if (stub_code->UpdateStoreBuffer_entry() != NULL) {
-      FindExternalLabel(&stub_code->UpdateStoreBufferLabel(), kNotPatchable);
+      object_pool_.AddExternalLabel(&stub_code->UpdateStoreBufferLabel(),
+                                    kNotPatchable);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
 
     if (stub_code->CallToRuntime_entry() != NULL) {
-      FindExternalLabel(&stub_code->CallToRuntimeLabel(), kNotPatchable);
+      object_pool_.AddExternalLabel(&stub_code->CallToRuntimeLabel(),
+                                    kNotPatchable);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
 
     // Create fixed object pool entries for debugger stubs.
     if (stub_code->ICCallBreakpoint_entry() != NULL) {
-      intptr_t index =
-          FindExternalLabel(&stub_code->ICCallBreakpointLabel(),
-                            kNotPatchable);
+      index = object_pool_.AddExternalLabel(
+          &stub_code->ICCallBreakpointLabel(), kNotPatchable);
       ASSERT(index == kICCallBreakpointCPIndex);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
     if (stub_code->ClosureCallBreakpoint_entry() != NULL) {
-      intptr_t index =
-          FindExternalLabel(&stub_code->ClosureCallBreakpointLabel(),
-                            kNotPatchable);
+      index = object_pool_.AddExternalLabel(
+          &stub_code->ClosureCallBreakpointLabel(), kNotPatchable);
       ASSERT(index == kClosureCallBreakpointCPIndex);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
     if (stub_code->RuntimeCallBreakpoint_entry() != NULL) {
-      intptr_t index =
-          FindExternalLabel(&stub_code->RuntimeCallBreakpointLabel(),
-                            kNotPatchable);
+      index = object_pool_.AddExternalLabel(
+          &stub_code->RuntimeCallBreakpointLabel(), kNotPatchable);
       ASSERT(index == kRuntimeCallBreakpointCPIndex);
     } else {
-      object_pool_.Add(vacant, Heap::kOld);
-      patchable_pool_entries_.Add(kNotPatchable);
+      object_pool_.AddObject(vacant, kNotPatchable);
     }
   }
 }
@@ -132,7 +119,7 @@
                                   Patchability patchable,
                                   Register pp) {
   const int32_t offset =
-      Array::element_offset(FindExternalLabel(label, patchable));
+      Array::element_offset(object_pool_.FindExternalLabel(label, patchable));
   LoadWordFromPoolOffset(dst, pp, offset - kHeapObjectTag);
 }
 
@@ -152,7 +139,7 @@
   ASSERT(allow_constant_pool());
   intptr_t call_start = buffer_.GetPosition();
   const int32_t offset =
-      Array::element_offset(FindExternalLabel(label, kPatchable));
+      Array::element_offset(object_pool_.FindExternalLabel(label, kPatchable));
   call(Address::AddressBaseImm32(PP, offset - kHeapObjectTag));
   ASSERT((buffer_.GetPosition() - call_start) == kCallExternalLabelSize);
 }
@@ -162,8 +149,8 @@
   if (Isolate::Current() == Dart::vm_isolate()) {
     call(label);
   } else {
-    const int32_t offset =
-        Array::element_offset(FindExternalLabel(label, kNotPatchable));
+    const int32_t offset = Array::element_offset(
+        object_pool_.FindExternalLabel(label, kNotPatchable));
     call(Address::AddressBaseImm32(pp, offset - kHeapObjectTag));
   }
 }
@@ -2620,7 +2607,7 @@
   ASSERT(allow_constant_pool());
   intptr_t call_start = buffer_.GetPosition();
   const int32_t offset =
-      Array::element_offset(FindExternalLabel(label, kPatchable));
+      Array::element_offset(object_pool_.FindExternalLabel(label, kPatchable));
   // Patchable jumps always use a 32-bit immediate encoding.
   jmp(Address::AddressBaseImm32(pp, offset - kHeapObjectTag));
   ASSERT((buffer_.GetPosition() - call_start) == JumpPattern::kLengthInBytes);
@@ -2628,8 +2615,8 @@
 
 
 void Assembler::Jmp(const ExternalLabel* label, Register pp) {
-  const int32_t offset =
-      Array::element_offset(FindExternalLabel(label, kNotPatchable));
+  const int32_t offset = Array::element_offset(
+      object_pool_.FindExternalLabel(label, kNotPatchable));
   jmp(Address(pp, offset - kHeapObjectTag));
 }
 
@@ -2786,54 +2773,6 @@
 }
 
 
-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) {
-    intptr_t idx = object_pool_index_table_.Lookup(&obj);
-    if (idx != ObjIndexPair::kNoIndex) {
-      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, object_pool_.Length() - 1));
-  }
-  return object_pool_.Length() - 1;
-}
-
-
-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;
-}
-
-
 // A set of VM objects that are present in every constant pool.
 static bool IsAlwaysInConstantPool(const Object& object) {
   // TODO(zra): Evaluate putting all VM heap objects into the pool.
@@ -2877,7 +2816,7 @@
 void Assembler::LoadObject(Register dst, const Object& object, Register pp) {
   if (CanLoadFromObjectPool(object)) {
     const int32_t offset =
-        Array::element_offset(FindObject(object, kNotPatchable));
+        Array::element_offset(object_pool_.FindObject(object, kNotPatchable));
     LoadWordFromPoolOffset(dst, pp, offset - kHeapObjectTag);
   } else {
     ASSERT((Isolate::Current() == Dart::vm_isolate()) ||
@@ -2912,7 +2851,7 @@
 void Assembler::CompareObject(Register reg, const Object& object, Register pp) {
   if (CanLoadFromObjectPool(object)) {
     const int32_t offset =
-        Array::element_offset(FindObject(object, kNotPatchable));
+        Array::element_offset(object_pool_.FindObject(object, kNotPatchable));
     cmpq(reg, Address(pp, offset-kHeapObjectTag));
   } else {
     CompareImmediate(
@@ -2923,9 +2862,8 @@
 
 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);
+  return object_pool_.FindObject(smi, kNotPatchable);
 }
 
 
diff --git a/runtime/vm/assembler_x64.h b/runtime/vm/assembler_x64.h
index 9b1da4d..93ddd1f 100644
--- a/runtime/vm/assembler_x64.h
+++ b/runtime/vm/assembler_x64.h
@@ -745,11 +745,6 @@
 
   void Drop(intptr_t stack_elements, Register tmp = TMP);
 
-  enum Patchability {
-    kPatchable,
-    kNotPatchable,
-  };
-
   bool allow_constant_pool() const {
     return allow_constant_pool_;
   }
@@ -900,7 +895,7 @@
   const ZoneGrowableArray<intptr_t>& GetPointerOffsets() const {
     return buffer_.pointer_offsets();
   }
-  const GrowableObjectArray& object_pool() const { return object_pool_; }
+  const GrowableObjectArray& object_pool() const { return object_pool_.data(); }
 
   void FinalizeInstructions(const MemoryRegion& region) {
     buffer_.FinalizeInstructions(region);
@@ -1033,13 +1028,7 @@
   AssemblerBuffer buffer_;
 
   // Objects and jump targets.
-  GrowableObjectArray& object_pool_;
-
-  // Patchability of pool entries.
-  GrowableArray<Patchability> patchable_pool_entries_;
-
-  // Hashmap for fast lookup in object pool.
-  DirectChainedHashMap<ObjIndexPair> object_pool_index_table_;
+  ObjectPool object_pool_;
 
   intptr_t prologue_offset_;
 
@@ -1061,9 +1050,6 @@
   GrowableArray<CodeComment*> comments_;
   bool allow_constant_pool_;
 
-  intptr_t FindObject(const Object& obj, Patchability patchable);
-  intptr_t FindExternalLabel(const ExternalLabel* label,
-                             Patchability patchable);
   intptr_t FindImmediate(int64_t imm);
   void LoadExternalLabel(Register dst,
                          const ExternalLabel* label,
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index c80e075..93815ca 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -1420,7 +1420,7 @@
     name = function.name();
     // Report signature conflicts only.
     if (Isolate::Current()->ErrorOnBadOverrideEnabled() &&
-        !function.is_static() && !function.IsConstructor()) {
+        !function.is_static() && !function.IsGenerativeConstructor()) {
       // A constructor cannot override anything.
       for (intptr_t i = 0; i < interfaces.Length(); i++) {
         super_class ^= interfaces.At(i);
@@ -2044,7 +2044,7 @@
   Function& func = Function::Handle();
   for (intptr_t i = 0; i < num_functions; i++) {
     func ^= functions.At(i);
-    if (func.IsConstructor()) {
+    if (func.IsGenerativeConstructor()) {
       // Build constructor name from mixin application class name
       // and name of cloned super class constructor.
       const String& ctor_name = String::Handle(func.name());
@@ -2130,7 +2130,7 @@
   const intptr_t num_functions = functions.Length();
   for (intptr_t i = 0; i < num_functions; i++) {
     func ^= functions.At(i);
-    if (func.IsConstructor()) {
+    if (func.IsGenerativeConstructor()) {
       // A mixin class must not have explicit constructors.
       if (!func.IsImplicitConstructor()) {
         ReportError(cls, cls.token_pos(),
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 687f566..0a2269d 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -65,8 +65,9 @@
     "Enable compiler verification assertions");
 
 DECLARE_FLAG(bool, trace_failed_optimization_attempts);
-DECLARE_FLAG(bool, trace_patching);
+DECLARE_FLAG(bool, trace_inlining_intervals);
 DECLARE_FLAG(bool, trace_irregexp);
+DECLARE_FLAG(bool, trace_patching);
 
 // TODO(zerny): Factor out unoptimizing/optimizing pipelines and remove
 // separate helpers functions & `optimizing` args.
@@ -184,7 +185,7 @@
     if (FLAG_trace_compiler) {
       const String& script_url = String::Handle(script.url());
       // TODO(iposva): Extract script kind.
-      OS::Print("Compiling %s '%s'\n", "", script_url.ToCString());
+      ISL_Print("Compiling %s '%s'\n", "", script_url.ToCString());
     }
     const String& library_key = String::Handle(library.private_key());
     script.Tokenize(library_key);
@@ -294,7 +295,7 @@
   LongJumpScope jump;
   if (setjmp(*jump.Set()) == 0) {
     if (FLAG_trace_compiler) {
-      OS::Print("Compiling Class %s '%s'\n", "", cls.ToCString());
+      ISL_Print("Compiling Class %s '%s'\n", "", cls.ToCString());
     }
 
     // Add the primary class which needs to be parsed to the parse list.
@@ -414,7 +415,7 @@
           if (FLAG_print_ic_data_map) {
             for (intptr_t i = 0; i < ic_data_array->length(); i++) {
               if ((*ic_data_array)[i] != NULL) {
-                OS::Print("%" Pd " ", i);
+                ISL_Print("%" Pd " ", i);
                 FlowGraphPrinter::PrintICData(*(*ic_data_array)[i]);
               }
             }
@@ -427,8 +428,9 @@
       }
 
       const bool print_flow_graph =
-          FLAG_print_flow_graph ||
-          (optimized && FLAG_print_flow_graph_optimized);
+          (FLAG_print_flow_graph ||
+          (optimized && FLAG_print_flow_graph_optimized)) &&
+          FlowGraphPrinter::ShouldPrint(function);
 
       if (print_flow_graph) {
         if (osr_id == Isolate::kNoDeoptId) {
@@ -460,7 +462,11 @@
       // Maps inline_id_to_function[inline_id] -> function. Top scope
       // function has inline_id 0. The map is populated by the inliner.
       GrowableArray<const Function*> inline_id_to_function;
+      // For a given inlining-id(index) specifies the caller's inlining-id.
+      GrowableArray<intptr_t> caller_inline_id;
       inline_id_to_function.Add(&function);
+      // Top scope function has no caller (-1).
+      caller_inline_id.Add(-1);
       // Collect all instance fields that are loaded in the graph and
       // have non-generic type feedback attached to them that can
       // potentially affect optimizations.
@@ -478,7 +484,7 @@
         optimizer.TryOptimizePatterns();
         DEBUG_ASSERT(flow_graph->VerifyUseLists());
 
-        FlowGraphInliner::SetInliningId(*flow_graph, 0);
+        FlowGraphInliner::SetInliningId(flow_graph, 0);
 
         // Inlining (mutates the flow graph)
         if (FLAG_use_inlining) {
@@ -492,7 +498,9 @@
           optimizer.ApplyClassIds();
           DEBUG_ASSERT(flow_graph->VerifyUseLists());
 
-          FlowGraphInliner inliner(flow_graph, &inline_id_to_function);
+          FlowGraphInliner inliner(flow_graph,
+                                   &inline_id_to_function,
+                                   &caller_inline_id);
           inliner.Inline();
           // Use lists are maintained and validated by the inliner.
           DEBUG_ASSERT(flow_graph->VerifyUseLists());
@@ -685,10 +693,12 @@
         }
       }
 
+      ASSERT(inline_id_to_function.length() == caller_inline_id.length());
       Assembler assembler(use_far_branches);
       FlowGraphCompiler graph_compiler(&assembler, flow_graph,
                                        *parsed_function, optimized,
-                                       inline_id_to_function);
+                                       inline_id_to_function,
+                                       caller_inline_id);
       {
         TimerScope timer(FLAG_compiler_stats,
                          &CompilerStats::graphcompiler_timer,
@@ -704,6 +714,8 @@
             Code::FinalizeCode(function, &assembler, optimized));
         code.set_is_optimized(optimized);
         code.set_inlined_intervals(graph_compiler.inlined_code_intervals());
+        code.set_inlined_id_to_function(
+            Array::Handle(graph_compiler.InliningIdToFunction()));
         graph_compiler.FinalizePcDescriptors(code);
         graph_compiler.FinalizeDeoptInfo(code);
         graph_compiler.FinalizeStackmaps(code);
@@ -716,9 +728,9 @@
             CodePatcher::PatchEntry(Code::Handle(function.CurrentCode()));
             if (FLAG_trace_compiler || FLAG_trace_patching) {
               if (FLAG_trace_compiler) {
-                OS::Print("  ");
+                ISL_Print("  ");
               }
-              OS::Print("Patch unoptimized '%s' entry point %#" Px "\n",
+              ISL_Print("Patch unoptimized '%s' entry point %#" Px "\n",
                   function.ToFullyQualifiedCString(),
                   Code::Handle(function.unoptimized_code()).EntryPoint());
             }
@@ -772,7 +784,7 @@
         // try again (done = true), and indicate that we did not finish
         // compiling (is_compiled = false).
         if (FLAG_trace_bailout) {
-          OS::Print("%s\n", error.ToErrorCString());
+          ISL_Print("%s\n", error.ToErrorCString());
         }
         done = true;
         ASSERT(optimized);
@@ -794,35 +806,35 @@
 
 static void DisassembleCode(const Function& function, bool optimized) {
   const char* function_fullname = function.ToFullyQualifiedCString();
-  OS::Print("Code for %sfunction '%s' {\n",
+  ISL_Print("Code for %sfunction '%s' {\n",
             optimized ? "optimized " : "",
             function_fullname);
   const Code& code = Code::Handle(function.CurrentCode());
   code.Disassemble();
-  OS::Print("}\n");
+  ISL_Print("}\n");
 
-  OS::Print("Pointer offsets for function: {\n");
+  ISL_Print("Pointer offsets for function: {\n");
   // Pointer offsets are stored in descending order.
   Object& obj = Object::Handle();
   for (intptr_t i = code.pointer_offsets_length() - 1; i >= 0; i--) {
     const uword addr = code.GetPointerOffsetAt(i) + code.EntryPoint();
     obj = *reinterpret_cast<RawObject**>(addr);
-    OS::Print(" %d : %#" Px " '%s'\n",
+    ISL_Print(" %d : %#" Px " '%s'\n",
               code.GetPointerOffsetAt(i), addr, obj.ToCString());
   }
-  OS::Print("}\n");
+  ISL_Print("}\n");
 
-  OS::Print("PC Descriptors for function '%s' {\n", function_fullname);
+  ISL_Print("PC Descriptors for function '%s' {\n", function_fullname);
   PcDescriptors::PrintHeaderString();
   const PcDescriptors& descriptors =
       PcDescriptors::Handle(code.pc_descriptors());
-  OS::Print("%s}\n", descriptors.ToCString());
+  ISL_Print("%s}\n", descriptors.ToCString());
 
   uword start = Instructions::Handle(code.instructions()).EntryPoint();
   const Array& deopt_table = Array::Handle(code.deopt_info_array());
   intptr_t deopt_table_length = DeoptTable::GetLength(deopt_table);
   if (deopt_table_length > 0) {
-    OS::Print("DeoptInfo: {\n");
+    ISL_Print("DeoptInfo: {\n");
     Smi& offset = Smi::Handle();
     DeoptInfo& info = DeoptInfo::Handle();
     Smi& reason_and_flags = Smi::Handle();
@@ -831,49 +843,49 @@
       const intptr_t reason =
           DeoptTable::ReasonField::decode(reason_and_flags.Value());
       ASSERT((0 <= reason) && (reason < ICData::kDeoptNumReasons));
-      OS::Print("%4" Pd ": 0x%" Px "  %s  (%s)\n",
+      ISL_Print("%4" Pd ": 0x%" Px "  %s  (%s)\n",
                 i,
                 start + offset.Value(),
                 info.ToCString(),
                 DeoptReasonToCString(
                     static_cast<ICData::DeoptReasonId>(reason)));
     }
-    OS::Print("}\n");
+    ISL_Print("}\n");
   }
 
   const Array& object_table = Array::Handle(code.object_table());
   if (object_table.Length() > 0) {
-    OS::Print("Object Table: {\n");
+    ISL_Print("Object Table: {\n");
     for (intptr_t i = 0; i < object_table.Length(); i++) {
-      OS::Print("  %" Pd ": %s\n", i,
+      ISL_Print("  %" Pd ": %s\n", i,
           Object::Handle(object_table.At(i)).ToCString());
     }
-    OS::Print("}\n");
+    ISL_Print("}\n");
   }
 
   const Array& object_pool = Array::Handle(
       Instructions::Handle(code.instructions()).object_pool());
   if (object_pool.Length() > 0) {
-    OS::Print("Object Pool: {\n");
+    ISL_Print("Object Pool: {\n");
     for (intptr_t i = 0; i < object_pool.Length(); i++) {
-      OS::Print("  %" Pd ": %s\n", i,
+      ISL_Print("  %" Pd ": %s\n", i,
           Object::Handle(object_pool.At(i)).ToCString());
     }
-    OS::Print("}\n");
+    ISL_Print("}\n");
   }
 
-  OS::Print("Stackmaps for function '%s' {\n", function_fullname);
+  ISL_Print("Stackmaps for function '%s' {\n", function_fullname);
   if (code.stackmaps() != Array::null()) {
     const Array& stackmap_table = Array::Handle(code.stackmaps());
     Stackmap& map = Stackmap::Handle();
     for (intptr_t i = 0; i < stackmap_table.Length(); ++i) {
       map ^= stackmap_table.At(i);
-      OS::Print("%s\n", map.ToCString());
+      ISL_Print("%s\n", map.ToCString());
     }
   }
-  OS::Print("}\n");
+  ISL_Print("}\n");
 
-  OS::Print("Variable Descriptors for function '%s' {\n",
+  ISL_Print("Variable Descriptors for function '%s' {\n",
             function_fullname);
   const LocalVarDescriptors& var_descriptors =
       LocalVarDescriptors::Handle(code.var_descriptors());
@@ -886,31 +898,31 @@
     var_descriptors.GetInfo(i, &var_info);
     const int8_t kind = var_info.kind();
     if (kind == RawLocalVarDescriptors::kSavedCurrentContext) {
-      OS::Print("  saved current CTX reg offset %d\n", var_info.index());
+      ISL_Print("  saved current CTX reg offset %d\n", var_info.index());
     } else {
       if (kind == RawLocalVarDescriptors::kContextLevel) {
-        OS::Print("  context level %d scope %d", var_info.index(),
+        ISL_Print("  context level %d scope %d", var_info.index(),
             var_info.scope_id);
       } else if (kind == RawLocalVarDescriptors::kStackVar) {
-        OS::Print("  stack var '%s' offset %d",
+        ISL_Print("  stack var '%s' offset %d",
           var_name.ToCString(), var_info.index());
       } else {
         ASSERT(kind == RawLocalVarDescriptors::kContextVar);
-        OS::Print("  context var '%s' level %d offset %d",
+        ISL_Print("  context var '%s' level %d offset %d",
             var_name.ToCString(), var_info.scope_id, var_info.index());
       }
-      OS::Print(" (valid %d-%d)\n", var_info.begin_pos, var_info.end_pos);
+      ISL_Print(" (valid %d-%d)\n", var_info.begin_pos, var_info.end_pos);
     }
   }
-  OS::Print("}\n");
+  ISL_Print("}\n");
 
-  OS::Print("Exception Handlers for function '%s' {\n", function_fullname);
+  ISL_Print("Exception Handlers for function '%s' {\n", function_fullname);
   const ExceptionHandlers& handlers =
         ExceptionHandlers::Handle(code.exception_handlers());
-  OS::Print("%s}\n", handlers.ToCString());
+  ISL_Print("%s}\n", handlers.ToCString());
 
   {
-    OS::Print("Static call target functions {\n");
+    ISL_Print("Static call target functions {\n");
     const Array& table = Array::Handle(code.static_calls_target_table());
     Smi& offset = Smi::Handle();
     Function& function = Function::Handle();
@@ -923,18 +935,21 @@
       if (function.IsNull()) {
         Class& cls = Class::Handle();
         cls ^= code.owner();
-        OS::Print("  0x%" Px ": allocation stub for %s, %p\n",
+        ISL_Print("  0x%" Px ": allocation stub for %s, %p\n",
             start + offset.Value(),
             cls.ToCString(),
             code.raw());
       } else {
-        OS::Print("  0x%" Px ": %s, %p\n",
+        ISL_Print("  0x%" Px ": %s, %p\n",
             start + offset.Value(),
             function.ToFullyQualifiedCString(),
             code.raw());
       }
     }
-    OS::Print("}\n");
+    ISL_Print("}\n");
+  }
+  if (optimized && FLAG_trace_inlining_intervals) {
+    code.DumpInlinedIntervals();
   }
 }
 
@@ -955,7 +970,7 @@
     ParsedFunction* parsed_function = new(zone) ParsedFunction(
         thread, Function::ZoneHandle(zone, function.raw()));
     if (FLAG_trace_compiler) {
-      OS::Print("Compiling %s%sfunction: '%s' @ token %" Pd ", size %" Pd "\n",
+      ISL_Print("Compiling %s%sfunction: '%s' @ token %" Pd ", size %" Pd "\n",
                 (osr_id == Isolate::kNoDeoptId ? "" : "osr "),
                 (optimized ? "optimized " : ""),
                 function.ToFullyQualifiedCString(),
@@ -975,10 +990,10 @@
       if (optimized) {
         // Optimizer bailed out. Disable optimizations and to never try again.
         if (FLAG_trace_compiler) {
-          OS::Print("--> disabling optimizations for '%s'\n",
+          ISL_Print("--> disabling optimizations for '%s'\n",
                     function.ToFullyQualifiedCString());
         } else if (FLAG_trace_failed_optimization_attempts) {
-          OS::Print("Cannot optimize: %s\n",
+          ISL_Print("Cannot optimize: %s\n",
                     function.ToFullyQualifiedCString());
         }
         function.SetIsOptimizable(false);
@@ -990,7 +1005,7 @@
     per_compile_timer.Stop();
 
     if (FLAG_trace_compiler) {
-      OS::Print("--> '%s' entry: %#" Px " size: %" Pd " time: %" Pd64 " us\n",
+      ISL_Print("--> '%s' entry: %#" Px " size: %" Pd " time: %" Pd64 " us\n",
                 function.ToFullyQualifiedCString(),
                 Code::Handle(function.CurrentCode()).EntryPoint(),
                 Code::Handle(function.CurrentCode()).Size(),
@@ -999,15 +1014,16 @@
 
     isolate->debugger()->NotifyCompilation(function);
 
-    if (FLAG_disassemble) {
+    if (FLAG_disassemble && FlowGraphPrinter::ShouldPrint(function)) {
       DisassembleCode(function, optimized);
-    } else if (FLAG_disassemble_optimized && optimized) {
+    } else if (FLAG_disassemble_optimized &&
+               optimized &&
+               FlowGraphPrinter::ShouldPrint(function)) {
       // TODO(fschneider): Print unoptimized code along with the optimized code.
-      OS::Print("*** BEGIN CODE\n");
+      ISL_Print("*** BEGIN CODE\n");
       DisassembleCode(function, true);
-      OS::Print("*** END CODE\n");
+      ISL_Print("*** END CODE\n");
     }
-
     return Error::null();
   } else {
     Error& error = Error::Handle();
@@ -1155,7 +1171,7 @@
   LongJumpScope jump;
   if (setjmp(*jump.Set()) == 0) {
     if (FLAG_trace_compiler) {
-      OS::Print("compiling expression: ");
+      ISL_Print("compiling expression: ");
       AstPrinter::PrintNode(fragment);
     }
 
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 04eabbb..64b96c80 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -47,6 +47,8 @@
             "Check function fingerprints");
 DEFINE_FLAG(bool, trace_api, false,
             "Trace invocation of API calls (debug mode only)");
+DEFINE_FLAG(bool, verify_acquired_data, false,
+            "Verify correct API acquire/release of typed data.");
 
 ThreadLocalKey Api::api_native_key_ = OSThread::kUnsetThreadLocalKey;
 Dart_Handle Api::true_handle_ = NULL;
@@ -1563,7 +1565,12 @@
 
   ASSERT(isolate->GetAndClearResumeRequest() == false);
   isolate->message_handler()->HandleOOBMessages();
-  return isolate->GetAndClearResumeRequest();
+  bool resume = isolate->GetAndClearResumeRequest();
+  if (resume && Service::NeedsDebuggerEvents()) {
+    DebuggerEvent resumeEvent(isolate, DebuggerEvent::kIsolateResumed);
+    Service::HandleDebuggerEvent(&resumeEvent);
+  }
+  return resume;
 }
 
 
@@ -2677,7 +2684,7 @@
   ASSERT(result.IsFunction());
   Function& constructor = Function::Handle(isolate);
   constructor ^= result.raw();
-  if (!constructor.IsConstructor()) {
+  if (!constructor.IsGenerativeConstructor()) {
     const String& message = String::Handle(
         String::NewFormatted("%s: class '%s' is not a constructor.",
                              CURRENT_FUNC, class_name.ToCString()));
@@ -3144,7 +3151,7 @@
   ASSERT(!result.IsNull());
   ASSERT(result.IsFunction());
   const Function& factory = Function::Cast(result);
-  ASSERT(!factory.IsConstructor());
+  ASSERT(!factory.IsGenerativeConstructor());
 
   // Create the argument list.
   const Array& args = Array::Handle(isolate, Array::New(2));
@@ -3193,7 +3200,7 @@
   ASSERT(!result.IsNull());
   ASSERT(result.IsFunction());
   const Function& factory = Function::Cast(result);
-  ASSERT(!factory.IsConstructor());
+  ASSERT(!factory.IsGenerativeConstructor());
 
   // Create the argument list.
   const intptr_t num_args = 3;
@@ -3374,7 +3381,7 @@
   ASSERT(!result.IsNull());
   ASSERT(result.IsFunction());
   const Function& factory = Function::Cast(result);
-  ASSERT(!factory.IsConstructor());
+  ASSERT(!factory.IsGenerativeConstructor());
 
   // Create the argument list.
   const Array& args = Array::Handle(isolate, Array::New(2));
@@ -3449,6 +3456,20 @@
       *data = data_obj.DataAddr(offset_in_bytes);
     }
   }
+  if (FLAG_verify_acquired_data) {
+    // For now, we just verify that acquire/release are properly matched
+    // per object.
+    // TODO(koda): Copy internal data to/from a side buffer which is unmapped
+    // on release to catch use-after-release bugs.
+    const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object));
+    WeakTable* table = isolate->api_state()->acquired_table();
+    intptr_t current = table->GetValue(obj.raw());
+    if (current != 0) {
+      ASSERT(current == 1);
+      return Api::NewError("Data was already acquired for this object.");
+    }
+    table->SetValue(obj.raw(), 1);
+  }
   return Api::Success();
 }
 
@@ -3466,6 +3487,17 @@
     isolate->DecrementNoGCScopeDepth();
     END_NO_CALLBACK_SCOPE(isolate);
   }
+  if (FLAG_verify_acquired_data) {
+    const Object& obj = Object::Handle(isolate, Api::UnwrapHandle(object));
+    WeakTable* table = isolate->api_state()->acquired_table();
+    intptr_t current = table->GetValue(obj.raw());
+    if (current != 1) {
+      ASSERT(current == 0);
+      return Api::NewError("Data was not acquired for this object.");
+    }
+    // Delete entry from table.
+    table->SetValue(obj.raw(), 0);
+  }
   return Api::Success();
 }
 
@@ -3494,7 +3526,7 @@
   const Function& constructor =
       Function::Handle(cls.LookupFunctionAllowPrivate(constr_name));
   if (constructor.IsNull() ||
-      (!constructor.IsConstructor() && !constructor.IsFactory())) {
+      (!constructor.IsGenerativeConstructor() && !constructor.IsFactory())) {
     const String& lookup_class_name = String::Handle(cls.Name());
     if (!class_name.Equals(lookup_class_name)) {
       // When the class name used to build the constructor name is
@@ -3516,7 +3548,7 @@
       return ApiError::New(message);
     }
   }
-  int extra_args = (constructor.IsConstructor() ? 2 : 1);
+  int extra_args = (constructor.IsGenerativeConstructor() ? 2 : 1);
   String& error_message = String::Handle();
   if (!constructor.AreValidArgumentCounts(num_args + extra_args,
                                           0,
@@ -3619,17 +3651,17 @@
 
     cls = type_obj.type_class();
   }
-  if (constructor.IsConstructor()) {
+  if (constructor.IsGenerativeConstructor()) {
     // Create the new object.
     new_object = Instance::New(cls);
   }
 
   // Create the argument list.
   intptr_t arg_index = 0;
-  int extra_args = (constructor.IsConstructor() ? 2 : 1);
+  int extra_args = (constructor.IsGenerativeConstructor() ? 2 : 1);
   const Array& args =
       Array::Handle(isolate, Array::New(number_of_arguments + extra_args));
-  if (constructor.IsConstructor()) {
+  if (constructor.IsGenerativeConstructor()) {
     // Constructors get the uninitialized object and a constructor phase.
     if (!type_arguments.IsNull()) {
       // The type arguments will be null if the class has no type parameters, in
@@ -3665,7 +3697,7 @@
     return Api::NewHandle(isolate, result.raw());
   }
 
-  if (constructor.IsConstructor()) {
+  if (constructor.IsGenerativeConstructor()) {
     ASSERT(result.IsNull());
   } else {
     ASSERT(result.IsNull() || result.IsInstance());
@@ -3823,7 +3855,7 @@
     Function::Handle(isolate, cls.LookupFunctionAllowPrivate(dot_name));
   const int extra_args = 2;
   if (!constructor.IsNull() &&
-      constructor.IsConstructor() &&
+      constructor.IsGenerativeConstructor() &&
       constructor.AreValidArgumentCounts(number_of_arguments + extra_args,
                                          0,
                                          NULL)) {
diff --git a/runtime/vm/dart_api_state.h b/runtime/vm/dart_api_state.h
index e9cf183..31be5fb 100644
--- a/runtime/vm/dart_api_state.h
+++ b/runtime/vm/dart_api_state.h
@@ -18,6 +18,7 @@
 #include "vm/raw_object.h"
 #include "vm/os_thread.h"
 #include "vm/visitor.h"
+#include "vm/weak_table.h"
 
 #include "vm/handles_impl.h"
 
@@ -832,6 +833,8 @@
 
   void DelayWeakReferenceSet(WeakReferenceSet* reference_set);
 
+  WeakTable* acquired_table() { return &acquired_table_; }
+
  private:
   PersistentHandles persistent_handles_;
   FinalizablePersistentHandles weak_persistent_handles_;
@@ -839,6 +842,7 @@
   ApiLocalScope* reusable_scope_;
   ApiLocalScope* top_scope_;
   WeakReferenceSet* delayed_weak_reference_sets_;
+  WeakTable acquired_table_;
 
   // Persistent handles to important objects.
   PersistentHandle* null_;
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index e83a1a9..3734659 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -522,6 +522,8 @@
       return "IsolateShutdown";
     case kIsolateInterrupted:
       return "IsolateInterrupted";
+    case kIsolateResumed:
+      return "IsolateResumed";
     default:
       UNREACHABLE();
       return "Unknown";
@@ -538,6 +540,7 @@
   jsobj.AddProperty("isolate", isolate());
   if ((type() == kBreakpointResolved || type() == kBreakpointReached) &&
       breakpoint() != NULL) {
+    // TODO(turnidge): Make this a breakpoint ref.
     jsobj.AddProperty("breakpoint", breakpoint());
   }
   if (type() == kExceptionThrown) {
@@ -2398,12 +2401,14 @@
       GrowableObjectArray::Handle(isolate_->object_store()->libraries());
   while (bpt != NULL) {
     url = bpt->url();
+    bool found_match = false;
     for (intptr_t i = 0; i < libs.Length(); i++) {
       lib ^= libs.At(i);
       script = lib.LookupScript(url);
       if (!script.IsNull()) {
         // Found a script with matching url for this latent breakpoint.
         // Unlink the latent breakpoint from the list.
+        found_match = true;
         SourceBreakpoint* matched_bpt = bpt;
         bpt = bpt->next();
         if (prev_bpt == NULL) {
@@ -2468,6 +2473,16 @@
         }
       }
     }
+    if (!found_match) {
+      // No matching url found in any of the libraries.
+      if (FLAG_verbose_debug) {
+        OS::Print("No match found for latent breakpoint id "
+                  "%" Pd " with url '%s'\n",
+                  bpt->id(),
+                  url.ToCString());
+      }
+      bpt = bpt->next();
+    }
   }
 }
 
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index 0abd4f4..3f9c207 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -288,6 +288,7 @@
     kIsolateCreated = 4,
     kIsolateShutdown = 5,
     kIsolateInterrupted = 6,
+    kIsolateResumed = 7,
   };
 
   explicit DebuggerEvent(Isolate* isolate, EventType event_type)
diff --git a/runtime/vm/disassembler.cc b/runtime/vm/disassembler.cc
index b70c760..4d5a350 100644
--- a/runtime/vm/disassembler.cc
+++ b/runtime/vm/disassembler.cc
@@ -7,6 +7,7 @@
 #include "vm/assembler.h"
 #include "vm/globals.h"
 #include "vm/os.h"
+#include "vm/log.h"
 #include "vm/json_stream.h"
 
 namespace dart {
@@ -18,22 +19,22 @@
                                              uword pc) {
   static const int kHexColumnWidth = 23;
   uint8_t* pc_ptr = reinterpret_cast<uint8_t*>(pc);
-  OS::Print("%p    %s", pc_ptr, hex_buffer);
+  ISL_Print("%p    %s", pc_ptr, hex_buffer);
   int hex_length = strlen(hex_buffer);
   if (hex_length < kHexColumnWidth) {
     for (int i = kHexColumnWidth - hex_length; i > 0; i--) {
-      OS::Print(" ");
+      ISL_Print(" ");
     }
   }
-  OS::Print("%s", human_buffer);
-  OS::Print("\n");
+  ISL_Print("%s", human_buffer);
+  ISL_Print("\n");
 }
 
 
 void DisassembleToStdout::Print(const char* format, ...) {
   va_list args;
   va_start(args, format);
-  OS::VFPrint(stdout, format, args);
+  ISL_VPrint(format, args);
   va_end(args);
 }
 
@@ -131,15 +132,19 @@
     if (old_comment_finger != comment_finger) {
       // Comment emitted, emit inlining information.
       code.GetInlinedFunctionsAt(offset, &inlined_functions);
-      // Skip top scope function printing.
-      for (intptr_t i = 1; i < inlined_functions.length(); i++) {
-        if (i == 1) {
-          formatter->Print("        ;; Inlined ");
+      // Skip top scope function printing (last entry in 'inlined_functions').
+      bool first = true;
+      for (intptr_t i = inlined_functions.length() - 2; i >= 0; i--) {
+        const char* name = inlined_functions[i]->ToQualifiedCString();
+        if (first) {
+          formatter->Print("        ;; Inlined [%s", name);
+          first = false;
+        } else {
+          formatter->Print(" -> %s", name);
         }
-        formatter->Print("-> %s ", inlined_functions[i]->ToQualifiedCString());
       }
-      if (inlined_functions.length() > 1) {
-        formatter->Print("\n");
+      if (!first) {
+        formatter->Print("]\n");
       }
     }
     int instruction_length;
diff --git a/runtime/vm/disassembler.h b/runtime/vm/disassembler.h
index 001649e..86413f8 100644
--- a/runtime/vm/disassembler.h
+++ b/runtime/vm/disassembler.h
@@ -8,6 +8,7 @@
 #include "vm/allocation.h"
 #include "vm/assembler.h"
 #include "vm/globals.h"
+#include "vm/log.h"
 
 namespace dart {
 
@@ -98,11 +99,13 @@
                           uword end,
                           const Code& code) {
     DisassembleToStdout stdout_formatter;
+    LogBlock lb(Isolate::Current());
     Disassemble(start, end, &stdout_formatter, code);
   }
 
   static void Disassemble(uword start, uword end) {
     DisassembleToStdout stdout_formatter;
+    LogBlock lb(Isolate::Current());
     Disassemble(start, end, &stdout_formatter);
   }
 
diff --git a/runtime/vm/disassembler_arm64.cc b/runtime/vm/disassembler_arm64.cc
index 0ef9273..783379c 100644
--- a/runtime/vm/disassembler_arm64.cc
+++ b/runtime/vm/disassembler_arm64.cc
@@ -375,7 +375,7 @@
         const uint64_t imm = instr->ImmLogical();
         buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
                                    remaining_size_in_buffer(),
-                                   "0x%"Px64,
+                                   "0x%" Px64,
                                    imm);
         return 6;
       } else {
@@ -527,7 +527,7 @@
         }
         buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
                                    remaining_size_in_buffer(),
-                                   "0x%"Px64,
+                                   "0x%" Px64,
                                    imm);
         return ret;
       } else {
@@ -557,7 +557,7 @@
           const int64_t dest = pc + off;
           buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
                                      remaining_size_in_buffer(),
-                                     "0x%"Px64,
+                                     "0x%" Px64,
                                      dest);
         } else {
           ASSERT(STRING_STARTS_WITH(format, "pcldr"));
@@ -566,7 +566,7 @@
           const int64_t dest = pc + off;
           buffer_pos_ += OS::SNPrint(current_position_in_buffer(),
                                      remaining_size_in_buffer(),
-                                     "0x%"Px64,
+                                     "0x%" Px64,
                                      dest);
         }
         return 5;
diff --git a/runtime/vm/flags.cc b/runtime/vm/flags.cc
index cef0667..c305ad1 100644
--- a/runtime/vm/flags.cc
+++ b/runtime/vm/flags.cc
@@ -26,6 +26,7 @@
   enum FlagType {
     kBoolean,
     kInteger,
+    kUint64,
     kString,
     kFunc,
     kNumFlagTypes
@@ -53,6 +54,10 @@
         OS::Print("%s: %d (%s)\n", name_, *this->int_ptr_, comment_);
         break;
       }
+      case kUint64: {
+        OS::Print("%s: %" Pu64 " (%s)\n", name_, *this->uint64_ptr_, comment_);
+        break;
+      }
       case kString: {
         if (*this->charp_ptr_ != NULL) {
           OS::Print("%s: '%s' (%s)\n", name_, *this->charp_ptr_, comment_);
@@ -81,6 +86,7 @@
     void* addr_;
     bool* bool_ptr_;
     int* int_ptr_;
+    uint64_t* uint64_ptr_;
     charp* charp_ptr_;
     FlagHandler handler_;
   };
@@ -158,6 +164,19 @@
 }
 
 
+uint64_t Flags::Register_uint64_t(uint64_t* addr,
+                                  const char* name,
+                                  uint64_t default_value,
+                                  const char* comment) {
+  ASSERT(Lookup(name) == NULL);
+
+  Flag* flag = new Flag(name, comment, addr, Flag::kUint64);
+  AddFlag(flag);
+
+  return default_value;
+}
+
+
 const char* Flags::Register_charp(charp* addr,
                                   const char* name,
                                   const char* default_value,
@@ -221,6 +240,21 @@
       }
       break;
     }
+    case Flag::kUint64: {
+      char* endptr = NULL;
+      const intptr_t len = strlen(argument);
+      int base = 10;
+      if ((len > 2) && (argument[0] == '0') && (argument[1] == 'x')) {
+        base = 16;
+      }
+      int64_t val = strtoll(argument, &endptr, base);
+      if (endptr == argument + len) {
+        *flag->uint64_ptr_ = static_cast<uint64_t>(val);
+      } else {
+        return false;
+      }
+      break;
+    }
     case Flag::kFunc: {
       if (strcmp(argument, "true") == 0) {
         (flag->handler_)(true);
@@ -405,6 +439,11 @@
       jsflag.AddPropertyF("valueAsString", "%d", *flag->int_ptr_);
       break;
     }
+    case Flag::kUint64: {
+      jsflag.AddProperty("flagType", "uint64_t");
+      jsflag.AddPropertyF("valueAsString", "%" Pu64, *flag->uint64_ptr_);
+      break;
+    }
     case Flag::kString: {
       jsflag.AddProperty("flagType", "string");
       if (flag->charp_ptr_ != NULL) {
diff --git a/runtime/vm/flags.h b/runtime/vm/flags.h
index 2fbec8f..f703c10 100644
--- a/runtime/vm/flags.h
+++ b/runtime/vm/flags.h
@@ -53,6 +53,11 @@
                           int default_value,
                           const char* comment);
 
+  static uint64_t Register_uint64_t(uint64_t* addr,
+                                    const char* name,
+                                    uint64_t default_value,
+                                    const char* comment);
+
   static const char* Register_charp(charp* addr,
                                     const char* name,
                                     const char* default_value,
diff --git a/runtime/vm/flow_graph.cc b/runtime/vm/flow_graph.cc
index 2cf115c..fbef3fe 100644
--- a/runtime/vm/flow_graph.cc
+++ b/runtime/vm/flow_graph.cc
@@ -6,6 +6,7 @@
 
 #include "vm/bit_vector.h"
 #include "vm/flow_graph_builder.h"
+#include "vm/il_printer.h"
 #include "vm/intermediate_language.h"
 #include "vm/growable_array.h"
 #include "vm/object_store.h"
@@ -43,7 +44,8 @@
     loop_invariant_loads_(NULL),
     guarded_fields_(parsed_function.guarded_fields()),
     deferred_prefixes_(parsed_function.deferred_prefixes()),
-    captured_parameters_(new(zone()) BitVector(zone(), variable_count())) {
+    captured_parameters_(new(zone()) BitVector(zone(), variable_count())),
+    inlining_id_(-1) {
   DiscoverBlocks();
 }
 
diff --git a/runtime/vm/flow_graph.h b/runtime/vm/flow_graph.h
index b9b932f..f5cf662 100644
--- a/runtime/vm/flow_graph.h
+++ b/runtime/vm/flow_graph.h
@@ -287,6 +287,9 @@
     return captured_parameters_;
   }
 
+  intptr_t inlining_id() const { return inlining_id_; }
+  void set_inlining_id(intptr_t value) { inlining_id_ = value; }
+
  private:
   friend class IfConverter;
   friend class BranchSimplifier;
@@ -363,6 +366,8 @@
   ZoneGrowableArray<const LibraryPrefix*>* deferred_prefixes_;
   DirectChainedHashMap<ConstantPoolTrait> constant_instr_pool_;
   BitVector* captured_parameters_;
+
+  intptr_t inlining_id_;
 };
 
 
diff --git a/runtime/vm/flow_graph_allocator.cc b/runtime/vm/flow_graph_allocator.cc
index 5613de1..78f850e 100644
--- a/runtime/vm/flow_graph_allocator.cc
+++ b/runtime/vm/flow_graph_allocator.cc
@@ -9,6 +9,7 @@
 #include "vm/il_printer.h"
 #include "vm/flow_graph.h"
 #include "vm/flow_graph_compiler.h"
+#include "vm/log.h"
 #include "vm/parser.h"
 
 namespace dart {
@@ -448,21 +449,21 @@
     return;
   }
 
-  OS::Print("  live range v%" Pd " [%" Pd ", %" Pd ") in ", vreg(),
+  ISL_Print("  live range v%" Pd " [%" Pd ", %" Pd ") in ", vreg(),
                                                             Start(),
                                                             End());
   assigned_location().Print();
   if (spill_slot_.HasStackIndex()) {
     intptr_t stack_slot = spill_slot_.stack_index();
-    OS::Print(" allocated spill slot: %" Pd "", stack_slot);
+    ISL_Print(" allocated spill slot: %" Pd "", stack_slot);
   }
-  OS::Print("\n");
+  ISL_Print("\n");
 
   SafepointPosition* safepoint = first_safepoint();
   while (safepoint != NULL) {
-    OS::Print("    Safepoint [%" Pd "]: ", safepoint->pos());
+    ISL_Print("    Safepoint [%" Pd "]: ", safepoint->pos());
     safepoint->locs()->stack_bitmap()->Print();
-    OS::Print("\n");
+    ISL_Print("\n");
     safepoint = safepoint->next();
   }
 
@@ -470,16 +471,16 @@
   for (UseInterval* interval = first_use_interval_;
        interval != NULL;
        interval = interval->next()) {
-    OS::Print("    use interval [%" Pd ", %" Pd ")\n",
+    ISL_Print("    use interval [%" Pd ", %" Pd ")\n",
               interval->start(),
               interval->end());
     while ((use_pos != NULL) && (use_pos->pos() <= interval->end())) {
-      OS::Print("      use at %" Pd "", use_pos->pos());
+      ISL_Print("      use at %" Pd "", use_pos->pos());
       if (use_pos->location_slot() != NULL) {
-        OS::Print(" as ");
+        ISL_Print(" as ");
         use_pos->location_slot()->Print();
       }
-      OS::Print("\n");
+      ISL_Print("\n");
       use_pos = use_pos->next();
     }
   }
@@ -1782,7 +1783,7 @@
                                 first_safepoint_after_split,
                                 next_sibling_);
 
-  TRACE_ALLOC(OS::Print("  split sibling [%" Pd ", %" Pd ")\n",
+  TRACE_ALLOC(ISL_Print("  split sibling [%" Pd ", %" Pd ")\n",
                         next_sibling_->Start(), next_sibling_->End()));
 
   last_use_interval_ = last_before_split;
@@ -1799,7 +1800,7 @@
 LiveRange* FlowGraphAllocator::SplitBetween(LiveRange* range,
                                             intptr_t from,
                                             intptr_t to) {
-  TRACE_ALLOC(OS::Print("split v%" Pd " [%" Pd ", %" Pd
+  TRACE_ALLOC(ISL_Print("split v%" Pd " [%" Pd ", %" Pd
                         ") between [%" Pd ", %" Pd ")\n",
                         range->vreg(), range->Start(), range->End(), from, to));
 
@@ -1839,7 +1840,7 @@
                                       intptr_t from,
                                       intptr_t to) {
   ASSERT(from < to);
-  TRACE_ALLOC(OS::Print("spill v%" Pd " [%" Pd ", %" Pd ") "
+  TRACE_ALLOC(ISL_Print("spill v%" Pd " [%" Pd ", %" Pd ") "
                         "between [%" Pd ", %" Pd ")\n",
                         range->vreg(), range->Start(), range->End(), from, to));
   LiveRange* tail = range->SplitAt(from);
@@ -1857,7 +1858,7 @@
 
 
 void FlowGraphAllocator::SpillAfter(LiveRange* range, intptr_t from) {
-  TRACE_ALLOC(OS::Print("spill v%" Pd " [%" Pd ", %" Pd ") after %" Pd "\n",
+  TRACE_ALLOC(ISL_Print("spill v%" Pd " [%" Pd ", %" Pd ") after %" Pd "\n",
                         range->vreg(), range->Start(), range->End(), from));
 
   // When spilling the value inside the loop check if this spill can
@@ -1871,7 +1872,7 @@
         RangeHasOnlyUnconstrainedUsesInLoop(range, loop_header->loop_id())) {
       ASSERT(loop_header->entry()->start_pos() <= from);
       from = loop_header->entry()->start_pos();
-      TRACE_ALLOC(OS::Print("  moved spill position to loop header %" Pd "\n",
+      TRACE_ALLOC(ISL_Print("  moved spill position to loop header %" Pd "\n",
                             from));
     }
   }
@@ -2103,7 +2104,7 @@
       candidate = hint.register_code();
     }
 
-    TRACE_ALLOC(OS::Print("found hint %s for v%" Pd ": free until %" Pd "\n",
+    TRACE_ALLOC(ISL_Print("found hint %s for v%" Pd ": free until %" Pd "\n",
                           hint.Name(),
                           unallocated->vreg(),
                           free_until));
@@ -2166,7 +2167,7 @@
     }
 
     if (used_on_backedge[candidate]) {
-      TRACE_ALLOC(OS::Print(
+      TRACE_ALLOC(ISL_Print(
           "considering %s for v%" Pd ": has interference on the back edge"
           " {loop [%" Pd ", %" Pd ")}\n",
           MakeRegisterLocation(candidate).Name(),
@@ -2185,7 +2186,7 @@
         if (intersection >= free_until) {
           candidate = reg;
           free_until = intersection;
-          TRACE_ALLOC(OS::Print(
+          TRACE_ALLOC(ISL_Print(
               "found %s for v%" Pd " with no interference on the back edge\n",
               MakeRegisterLocation(candidate).Name(),
               candidate));
@@ -2195,13 +2196,13 @@
     }
   }
 
-  TRACE_ALLOC(OS::Print("assigning free register "));
+  TRACE_ALLOC(ISL_Print("assigning free register "));
   TRACE_ALLOC(MakeRegisterLocation(candidate).Print());
-  TRACE_ALLOC(OS::Print(" to v%" Pd "\n", unallocated->vreg()));
+  TRACE_ALLOC(ISL_Print(" to v%" Pd "\n", unallocated->vreg()));
 
   if (free_until != kMaxPosition) {
     // There was an intersection. Split unallocated.
-    TRACE_ALLOC(OS::Print("  splitting at %" Pd "\n", free_until));
+    TRACE_ALLOC(ISL_Print("  splitting at %" Pd "\n", free_until));
     LiveRange* tail = unallocated->SplitAt(free_until);
     AddToUnallocated(tail);
   }
@@ -2300,9 +2301,9 @@
 
   ASSERT(candidate != kNoRegister);
 
-  TRACE_ALLOC(OS::Print("assigning blocked register "));
+  TRACE_ALLOC(ISL_Print("assigning blocked register "));
   TRACE_ALLOC(MakeRegisterLocation(candidate).Print());
-  TRACE_ALLOC(OS::Print(" to live range v%" Pd " until %" Pd "\n",
+  TRACE_ALLOC(ISL_Print(" to live range v%" Pd " until %" Pd "\n",
                         unallocated->vreg(), blocked_at));
 
   if (blocked_at < unallocated->End()) {
@@ -2467,9 +2468,9 @@
   ASSERT(use->location_slot() != NULL);
   Location* slot = use->location_slot();
   ASSERT(slot->IsUnallocated());
-  TRACE_ALLOC(OS::Print("  use at %" Pd " converted to ", use->pos()));
+  TRACE_ALLOC(ISL_Print("  use at %" Pd " converted to ", use->pos()));
   TRACE_ALLOC(loc.Print());
-  TRACE_ALLOC(OS::Print("\n"));
+  TRACE_ALLOC(ISL_Print("\n"));
   *slot = loc;
 }
 
@@ -2480,11 +2481,11 @@
   const Location loc = range->assigned_location();
   ASSERT(!loc.IsInvalid());
 
-  TRACE_ALLOC(OS::Print("range [%" Pd ", %" Pd ") "
+  TRACE_ALLOC(ISL_Print("range [%" Pd ", %" Pd ") "
                         "for v%" Pd " has been allocated to ",
                         range->Start(), range->End(), range->vreg()));
   TRACE_ALLOC(loc.Print());
-  TRACE_ALLOC(OS::Print(":\n"));
+  TRACE_ALLOC(ISL_Print(":\n"));
 
   for (UsePosition* use = range->first_use(); use != NULL; use = use->next()) {
     ConvertUseTo(use, loc);
@@ -2660,7 +2661,7 @@
   while (!unallocated_.is_empty()) {
     LiveRange* range = unallocated_.RemoveLast();
     const intptr_t start = range->Start();
-    TRACE_ALLOC(OS::Print("Processing live range for v%" Pd " "
+    TRACE_ALLOC(ISL_Print("Processing live range for v%" Pd " "
                           "starting at %" Pd "\n",
                           range->vreg(),
                           start));
@@ -2684,7 +2685,7 @@
 
   // Finish allocation.
   AdvanceActiveIntervals(kMaxPosition);
-  TRACE_ALLOC(OS::Print("Allocation completed\n"));
+  TRACE_ALLOC(ISL_Print("Allocation completed\n"));
 }
 
 
@@ -2703,13 +2704,13 @@
 void FlowGraphAllocator::ConnectSplitSiblings(LiveRange* parent,
                                               BlockEntryInstr* source_block,
                                               BlockEntryInstr* target_block) {
-  TRACE_ALLOC(OS::Print("Connect v%" Pd " on the edge B%" Pd " -> B%" Pd "\n",
+  TRACE_ALLOC(ISL_Print("Connect v%" Pd " on the edge B%" Pd " -> B%" Pd "\n",
                         parent->vreg(),
                         source_block->block_id(),
                         target_block->block_id()));
   if (parent->next_sibling() == NULL) {
     // Nothing to connect. The whole range was allocated to the same location.
-    TRACE_ALLOC(OS::Print("range v%" Pd " has no siblings\n", parent->vreg()));
+    TRACE_ALLOC(ISL_Print("range v%" Pd " has no siblings\n", parent->vreg()));
     return;
   }
 
@@ -2746,7 +2747,7 @@
     range = range->next_sibling();
   }
 
-  TRACE_ALLOC(OS::Print("connecting v%" Pd " between [%" Pd ", %" Pd ") {%s} "
+  TRACE_ALLOC(ISL_Print("connecting v%" Pd " between [%" Pd ", %" Pd ") {%s} "
                         "to [%" Pd ", %" Pd ") {%s}\n",
                         parent->vreg(),
                         source_cover->Start(),
@@ -2783,13 +2784,13 @@
 
     while (range->next_sibling() != NULL) {
       LiveRange* sibling = range->next_sibling();
-      TRACE_ALLOC(OS::Print("connecting [%" Pd ", %" Pd ") [",
+      TRACE_ALLOC(ISL_Print("connecting [%" Pd ", %" Pd ") [",
                             range->Start(), range->End()));
       TRACE_ALLOC(range->assigned_location().Print());
-      TRACE_ALLOC(OS::Print("] to [%" Pd ", %" Pd ") [",
+      TRACE_ALLOC(ISL_Print("] to [%" Pd ", %" Pd ") [",
                             sibling->Start(), sibling->End()));
       TRACE_ALLOC(sibling->assigned_location().Print());
-      TRACE_ALLOC(OS::Print("]\n"));
+      TRACE_ALLOC(ISL_Print("]\n"));
       if ((range->End() == sibling->Start()) &&
           !TargetLocationIsSpillSlot(range, sibling->assigned_location()) &&
           !range->assigned_location().Equals(sibling->assigned_location()) &&
@@ -2920,16 +2921,16 @@
 
   if (FLAG_print_ssa_liveranges) {
     const Function& function = flow_graph_.function();
-    OS::Print("-- [before ssa allocator] ranges [%s] ---------\n",
+    ISL_Print("-- [before ssa allocator] ranges [%s] ---------\n",
               function.ToFullyQualifiedCString());
     PrintLiveRanges();
-    OS::Print("----------------------------------------------\n");
+    ISL_Print("----------------------------------------------\n");
 
-    OS::Print("-- [before ssa allocator] ir [%s] -------------\n",
+    ISL_Print("-- [before ssa allocator] ir [%s] -------------\n",
               function.ToFullyQualifiedCString());
     FlowGraphPrinter printer(flow_graph_, true);
     printer.PrintBlocks();
-    OS::Print("----------------------------------------------\n");
+    ISL_Print("----------------------------------------------\n");
   }
 
   PrepareForAllocation(Location::kRegister,
@@ -2961,16 +2962,16 @@
   if (FLAG_print_ssa_liveranges) {
     const Function& function = flow_graph_.function();
 
-    OS::Print("-- [after ssa allocator] ranges [%s] ---------\n",
+    ISL_Print("-- [after ssa allocator] ranges [%s] ---------\n",
               function.ToFullyQualifiedCString());
     PrintLiveRanges();
-    OS::Print("----------------------------------------------\n");
+    ISL_Print("----------------------------------------------\n");
 
-    OS::Print("-- [after ssa allocator] ir [%s] -------------\n",
+    ISL_Print("-- [after ssa allocator] ir [%s] -------------\n",
               function.ToFullyQualifiedCString());
     FlowGraphPrinter printer(flow_graph_, true);
     printer.PrintBlocks();
-    OS::Print("----------------------------------------------\n");
+    ISL_Print("----------------------------------------------\n");
   }
 }
 
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index afc250d..dad8cef 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -1056,7 +1056,7 @@
     // The body of a constructor cannot modify the type of the
     // constructed instance, which is passed in as an implicit parameter.
     // However, factories may create an instance of the wrong type.
-    if (!is_implicit_dynamic_getter && !function.IsConstructor()) {
+    if (!is_implicit_dynamic_getter && !function.IsGenerativeConstructor()) {
       const AbstractType& dst_type =
           AbstractType::ZoneHandle(I, function.result_type());
       return_value = BuildAssignableValue(node->value()->token_pos(),
@@ -3851,7 +3851,7 @@
   if (Isolate::Current()->TypeChecksEnabled() && is_top_level_sequence) {
     const int num_params = function.NumParameters();
     int pos = 0;
-    if (function.IsConstructor()) {
+    if (function.IsGenerativeConstructor()) {
       // Skip type checking of receiver and phase for constructor functions.
       pos = 2;
     } else if (function.IsFactory() || function.IsDynamicFunction()) {
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index bc7e6b6..6b1df12 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -16,6 +16,7 @@
 #include "vm/il_printer.h"
 #include "vm/intrinsifier.h"
 #include "vm/locations.h"
+#include "vm/log.h"
 #include "vm/longjump.h"
 #include "vm/object_store.h"
 #include "vm/parser.h"
@@ -26,30 +27,33 @@
 
 namespace dart {
 
+DEFINE_FLAG(bool, trace_inlining_intervals, false,
+    "Inlining interval diagnostics");
+DEFINE_FLAG(bool, enable_simd_inline, true,
+    "Enable inlining of SIMD related method calls.");
+DEFINE_FLAG(int, min_optimization_counter_threshold, 5000,
+    "The minimum invocation count for a function.");
+DEFINE_FLAG(int, optimization_counter_scale, 2000,
+    "The scale of invocation count, by size of the function.");
+DEFINE_FLAG(bool, source_lines, false, "Emit source line as assembly comment.");
+
 DECLARE_FLAG(bool, code_comments);
+DECLARE_FLAG(int, deoptimize_every);
+DECLARE_FLAG(charp, deoptimize_filter);
 DECLARE_FLAG(bool, disassemble);
 DECLARE_FLAG(bool, disassemble_optimized);
 DECLARE_FLAG(bool, emit_edge_counters);
 DECLARE_FLAG(bool, enable_type_checks);
 DECLARE_FLAG(bool, intrinsify);
-DECLARE_FLAG(bool, propagate_ic_data);
 DECLARE_FLAG(int, optimization_counter_threshold);
+DECLARE_FLAG(bool, propagate_ic_data);
 DECLARE_FLAG(int, regexp_optimization_counter_threshold);
-DEFINE_FLAG(int, optimization_counter_scale, 2000,
-    "The scale of invocation count, by size of the function.");
-DEFINE_FLAG(int, min_optimization_counter_threshold, 5000,
-    "The minimum invocation count for a function.");
 DECLARE_FLAG(int, reoptimization_counter_threshold);
-DECLARE_FLAG(bool, use_cha);
-DECLARE_FLAG(bool, use_osr);
 DECLARE_FLAG(int, stacktrace_every);
 DECLARE_FLAG(charp, stacktrace_filter);
-DECLARE_FLAG(int, deoptimize_every);
-DECLARE_FLAG(charp, deoptimize_filter);
+DECLARE_FLAG(bool, use_cha);
+DECLARE_FLAG(bool, use_osr);
 DECLARE_FLAG(bool, warn_on_javascript_compatibility);
-DEFINE_FLAG(bool, enable_simd_inline, true,
-    "Enable inlining of SIMD related method calls.");
-DEFINE_FLAG(bool, source_lines, false, "Emit source line as assembly comment.");
 
 // Quick access to the locally defined isolate() method.
 #define I (isolate())
@@ -89,7 +93,8 @@
     FlowGraph* flow_graph,
     const ParsedFunction& parsed_function,
     bool is_optimizing,
-    const GrowableArray<const Function*>& inline_id_to_function)
+    const GrowableArray<const Function*>& inline_id_to_function,
+    const GrowableArray<intptr_t>& caller_inline_id)
       : isolate_(Isolate::Current()),
         assembler_(assembler),
         parsed_function_(parsed_function),
@@ -127,7 +132,8 @@
         lazy_deopt_pc_offset_(Code::kInvalidPc),
         deopt_id_to_ic_data_(NULL),
         inlined_code_intervals_(NULL),
-        inline_id_to_function_(inline_id_to_function) {
+        inline_id_to_function_(inline_id_to_function),
+        caller_inline_id_(caller_inline_id) {
   ASSERT(flow_graph->parsed_function().function().raw() ==
          parsed_function.function().raw());
   if (!is_optimizing) {
@@ -352,10 +358,11 @@
 struct IntervalStruct {
   // 'start' and 'end' are pc-offsets.
   intptr_t start;
-  intptr_t end;
   intptr_t inlining_id;
-  IntervalStruct(intptr_t s, intptr_t e, intptr_t id)
-      : start(s), end(e), inlining_id(id) {}
+  IntervalStruct(intptr_t s, intptr_t id) : start(s), inlining_id(id) {}
+  void Dump() {
+    OS::Print("start: %" Px " id: %" Pd "",  start, inlining_id);
+  }
 };
 
 
@@ -394,7 +401,6 @@
       if (instr->has_inlining_id() && is_optimizing()) {
         if (prev_inlining_id != instr->inlining_id()) {
           intervals.Add(IntervalStruct(prev_offset,
-                                       assembler()->CodeSize(),
                                        prev_inlining_id));
           prev_offset = assembler()->CodeSize();
           prev_inlining_id = instr->inlining_id();
@@ -424,53 +430,51 @@
     }
   }
 
-  intervals.Add(IntervalStruct(prev_offset, assembler()->CodeSize(),
-                               prev_inlining_id));
-  // Note that ranges [start..end] must be monotonically increasing in
-  // 'intervals' array.
-  inlined_code_intervals_ = &Array::ZoneHandle(Array::New(
-      (max_inlining_id + 1) * Code::kInlIntNumEntries, Heap::kOld));
+  if (inline_id_to_function_.length() > max_inlining_id + 1) {
+    // TODO(srdjan): Some inlined function can disappear,
+    // truncate 'inline_id_to_function_'.
+  }
 
+  intervals.Add(IntervalStruct(prev_offset, prev_inlining_id));
+  inlined_code_intervals_ = &Array::ZoneHandle(Array::New(
+      intervals.length() * Code::kInlIntNumEntries, Heap::kOld));
   Smi& start_h = Smi::Handle();
-  Smi& end_h = Smi::Handle();
+  Smi& caller_inline_id = Smi::Handle();
+  Smi& inline_id = Smi::Handle();
   for (intptr_t i = 0; i < intervals.length(); i++) {
-    const intptr_t id = intervals[i].inlining_id;
-    end_h = Smi::New(intervals[i].end);
-    if (inlined_code_intervals_->At
-          (id * Code::kInlIntNumEntries + Code::kInlIntFunction) ==
-        Object::null()) {
-      start_h = Smi::New(intervals[i].start);
-      inlined_code_intervals_->SetAt(
-          id * Code::kInlIntNumEntries + Code::kInlIntStart, start_h);
-      inlined_code_intervals_->SetAt(
-          id * Code::kInlIntNumEntries + Code::kInlIntEnd, end_h);
+    if (FLAG_trace_inlining_intervals && is_optimizing()) {
       const Function* function =
           inline_id_to_function_.At(intervals[i].inlining_id);
-      inlined_code_intervals_->SetAt(
-          id * Code::kInlIntNumEntries + Code::kInlIntFunction, *function);
-    } else {
-      // Check for monotonic increase.
-#if defined(DEBUG)
-      Smi& temp = Smi::Handle();
-      temp ^= inlined_code_intervals_->At(
-          id *  Code::kInlIntNumEntries + Code::kInlIntStart);
-      start_h = Smi::New(intervals[i].start);
-      ASSERT(temp.Value() <= start_h.Value());
-      temp ^= inlined_code_intervals_->At(
-          id * Code::kInlIntNumEntries + Code::kInlIntEnd);
-      ASSERT(temp.Value() <= end_h.Value());
-      const Function* function =
-          inline_id_to_function_.At(intervals[i].inlining_id);
-      Function& f = Function::Handle();
-      f ^= inlined_code_intervals_->At(
-          id * Code::kInlIntNumEntries + Code::kInlIntFunction);
-      ASSERT(function->raw() == f.raw());
-#endif
-      inlined_code_intervals_->SetAt(
-          id * Code::kInlIntNumEntries + Code::kInlIntEnd, end_h);
+      intervals[i].Dump();
+      OS::Print(" %s parent %" Pd "\n",
+          function->ToQualifiedCString(),
+          caller_inline_id_[intervals[i].inlining_id]);
     }
+    const intptr_t id = intervals[i].inlining_id;
+    start_h = Smi::New(intervals[i].start);
+    inline_id = Smi::New(id);
+    caller_inline_id = Smi::New(caller_inline_id_[intervals[i].inlining_id]);
+
+    const intptr_t p = i * Code::kInlIntNumEntries;
+    inlined_code_intervals_->SetAt(p + Code::kInlIntStart, start_h);
+    inlined_code_intervals_->SetAt(p + Code::kInlIntInliningId, inline_id);
+    inlined_code_intervals_->SetAt(p + Code::kInlIntCallerId, caller_inline_id);
   }
   set_current_block(NULL);
+  if (FLAG_trace_inlining_intervals && is_optimizing()) {
+    OS::Print("Intervals:\n");
+    Smi& temp = Smi::Handle();
+    for (intptr_t i = 0; i < inlined_code_intervals_->Length();
+         i += Code::kInlIntNumEntries) {
+      temp ^= inlined_code_intervals_->At(i + Code::kInlIntStart);
+      ASSERT(!temp.IsNull());
+      OS::Print("% " Pd " start: %" Px " ", i, temp.Value());
+      temp ^= inlined_code_intervals_->At(i + Code::kInlIntInliningId);
+      OS::Print("inl-id: %" Pd " ", temp.Value());
+      temp ^= inlined_code_intervals_->At(i + Code::kInlIntCallerId);
+      OS::Print("caller-id: %" Pd " \n", temp.Value());
+    }
+  }
 }
 
 
@@ -744,7 +748,7 @@
   Environment* env = instruction->env()->DeepCopy(isolate());
   // 1. Iterate the registers in the order they will be spilled to compute
   //    the slots they will be spilled to.
-  intptr_t next_slot = StackSize();
+  intptr_t next_slot = StackSize() + env->CountArgsPushed();
   RegisterSet* regs = instruction->locs()->live_registers();
   intptr_t fpu_reg_slots[kNumberOfFpuRegisters];
   intptr_t cpu_reg_slots[kNumberOfCpuRegisters];
@@ -1580,4 +1584,14 @@
 }
 
 
+RawArray* FlowGraphCompiler::InliningIdToFunction() const {
+  const Array& res = Array::Handle(
+      Array::New(inline_id_to_function_.length(), Heap::kOld));
+  for (intptr_t i = 0; i < inline_id_to_function_.length(); i++) {
+    res.SetAt(i, *inline_id_to_function_[i]);
+  }
+  return res.raw();
+}
+
+
 }  // namespace dart
diff --git a/runtime/vm/flow_graph_compiler.h b/runtime/vm/flow_graph_compiler.h
index ef4fe00..6e9c0df 100644
--- a/runtime/vm/flow_graph_compiler.h
+++ b/runtime/vm/flow_graph_compiler.h
@@ -270,7 +270,8 @@
       FlowGraph* flow_graph,
       const ParsedFunction& parsed_function,
       bool is_optimizing,
-      const GrowableArray<const Function*>& inline_id_to_function);
+      const GrowableArray<const Function*>& inline_id_to_function,
+      const GrowableArray<intptr_t>& caller_inline_id);
 
   ~FlowGraphCompiler();
 
@@ -528,6 +529,8 @@
     return *inlined_code_intervals_;
   }
 
+  RawArray* InliningIdToFunction() const;
+
  private:
   friend class CheckStackOverflowSlowPath;  // For pending_deoptimization_env_.
 
@@ -685,6 +688,7 @@
 
   const Array* inlined_code_intervals_;
   const GrowableArray<const Function*>& inline_id_to_function_;
+  const GrowableArray<intptr_t>& caller_inline_id_;
 
   DISALLOW_COPY_AND_ASSIGN(FlowGraphCompiler);
 };
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index 92f92ad..b18a838 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -69,7 +69,7 @@
 
 #define TRACE_INLINING(statement)                                              \
   do {                                                                         \
-    if (FLAG_trace_inlining) statement;                                        \
+    if (trace_inlining()) statement;                                           \
   } while (false)
 
 #define PRINT_INLINING_TREE(comment, caller, target, instance_call)            \
@@ -192,30 +192,33 @@
   struct InstanceCallInfo {
     PolymorphicInstanceCallInstr* call;
     double ratio;
-    const Function* caller;
+    const FlowGraph* caller_graph;
     InstanceCallInfo(PolymorphicInstanceCallInstr* call_arg,
                      FlowGraph* flow_graph)
         : call(call_arg),
           ratio(0.0),
-          caller(&flow_graph->function()) {}
+          caller_graph(flow_graph) {}
+    const Function& caller() const { return caller_graph->function(); }
   };
 
   struct StaticCallInfo {
     StaticCallInstr* call;
     double ratio;
-    const Function* caller;
+    FlowGraph* caller_graph;
     StaticCallInfo(StaticCallInstr* value, FlowGraph* flow_graph)
         : call(value),
           ratio(0.0),
-          caller(&flow_graph->function()) {}
+          caller_graph(flow_graph) {}
+    const Function& caller() const { return caller_graph->function(); }
   };
 
   struct ClosureCallInfo {
     ClosureCallInstr* call;
-    const Function* caller;
+    FlowGraph* caller_graph;
     ClosureCallInfo(ClosureCallInstr* value, FlowGraph* flow_graph)
         : call(value),
-          caller(&flow_graph->function()) {}
+          caller_graph(flow_graph) {}
+    const Function& caller() const { return caller_graph->function(); }
   };
 
   const GrowableArray<InstanceCallInfo>& instance_calls() const {
@@ -404,13 +407,15 @@
 struct InlinedCallData {
   InlinedCallData(Definition* call,
                   GrowableArray<Value*>* arguments,
-                  const Function& caller)
+                  const Function& caller,
+                  intptr_t caller_inlining_id)
       : call(call),
         arguments(arguments),
         callee_graph(NULL),
         parameter_stubs(NULL),
         exit_collector(NULL),
-        caller(caller) { }
+        caller(caller),
+        caller_inlining_id_(caller_inlining_id) { }
 
   Definition* call;
   GrowableArray<Value*>* arguments;
@@ -418,6 +423,7 @@
   ZoneGrowableArray<Definition*>* parameter_stubs;
   InlineExitCollector* exit_collector;
   const Function& caller;
+  const intptr_t caller_inlining_id_;
 };
 
 
@@ -427,7 +433,8 @@
  public:
   PolymorphicInliner(CallSiteInliner* owner,
                      PolymorphicInstanceCallInstr* call,
-                     const Function& caller_function);
+                     const Function& caller_function,
+                     intptr_t caller_inlining_id);
 
   void Inline();
 
@@ -454,6 +461,7 @@
   InlineExitCollector* exit_collector_;
 
   const Function& caller_function_;
+  const intptr_t caller_inlining_id_;
 };
 
 
@@ -498,12 +506,14 @@
   Isolate* isolate() const { return caller_graph_->isolate(); }
   Zone* zone() const { return caller_graph_->zone(); }
 
+  bool trace_inlining() const { return inliner_->trace_inlining(); }
+
   // Inlining heuristics based on Cooper et al. 2008.
   bool ShouldWeInline(const Function& callee,
                       intptr_t instr_count,
                       intptr_t call_site_count,
                       intptr_t const_arg_count) {
-    if (FlowGraphInliner::AlwaysInline(callee)) {
+    if (inliner_->AlwaysInline(callee)) {
       return true;
     }
     if (inlined_size_ > FLAG_inlining_caller_size_threshold) {
@@ -549,13 +559,13 @@
                                          inlining_depth_,
                                          &inlined_info_);
     while (collected_call_sites_->HasCalls()) {
-      TRACE_INLINING(OS::Print("  Depth %" Pd " ----------\n",
+      TRACE_INLINING(ISL_Print("  Depth %" Pd " ----------\n",
                                inlining_depth_));
       if (collected_call_sites_->NumCalls() > FLAG_max_inlined_per_depth) {
         break;
       }
       if (FLAG_print_inlining_tree) {
-        OS::Print("**Depth % " Pd " calls to inline %" Pd "\n",
+        ISL_Print("**Depth % " Pd " calls to inline %" Pd "\n",
             inlining_depth_, collected_call_sites_->NumCalls());
       }
       // Swap collected and inlining arrays and clear the new collecting array.
@@ -600,7 +610,7 @@
   bool TryInlining(const Function& function,
                    const Array& argument_names,
                    InlinedCallData* call_data) {
-    TRACE_INLINING(OS::Print("  => %s (deopt count %d)\n",
+    TRACE_INLINING(ISL_Print("  => %s (deopt count %d)\n",
                              function.ToCString(),
                              function.deoptimization_counter()));
 
@@ -609,7 +619,7 @@
     const Code& unoptimized_code = Code::Handle(function.unoptimized_code());
     // Abort if the inlinable bit on the function is low.
     if (!function.CanBeInlined()) {
-      TRACE_INLINING(OS::Print("     Bailout: not inlinable\n"));
+      TRACE_INLINING(ISL_Print("     Bailout: not inlinable\n"));
       PRINT_INLINING_TREE("Not inlinable",
           &call_data->caller, &function, call_data->call);
       return false;
@@ -619,7 +629,7 @@
     if (function.deoptimization_counter() >=
         FLAG_deoptimization_counter_threshold) {
       function.set_is_inlinable(false);
-      TRACE_INLINING(OS::Print("     Bailout: deoptimization threshold\n"));
+      TRACE_INLINING(ISL_Print("     Bailout: deoptimization threshold\n"));
       PRINT_INLINING_TREE("Deoptimization threshold exceeded",
           &call_data->caller, &function, call_data->call);
       return false;
@@ -628,7 +638,7 @@
     const char* kNeverInlineAnnotation = "NeverInline";
     if (FLAG_enable_inlining_annotations &&
         HasAnnotation(function, kNeverInlineAnnotation)) {
-      TRACE_INLINING(OS::Print("     Bailout: NeverInline annotation\n"));
+      TRACE_INLINING(ISL_Print("     Bailout: NeverInline annotation\n"));
       return false;
     }
 
@@ -638,7 +648,7 @@
                         function.optimized_instruction_count(),
                         function.optimized_call_site_count(),
                         constant_arguments)) {
-      TRACE_INLINING(OS::Print("     Bailout: early heuristics with "
+      TRACE_INLINING(ISL_Print("     Bailout: early heuristics with "
                                "code size:  %" Pd ", "
                                "call sites: %" Pd ", "
                                "const args: %" Pd "\n",
@@ -656,7 +666,7 @@
     volatile bool is_recursive_call = IsCallRecursive(unoptimized_code, call);
     if (is_recursive_call &&
         inlining_recursion_depth_ >= FLAG_inlining_recursion_depth_threshold) {
-      TRACE_INLINING(OS::Print("     Bailout: recursive function\n"));
+      TRACE_INLINING(ISL_Print("     Bailout: recursive function\n"));
       PRINT_INLINING_TREE("Recursive function",
           &call_data->caller, &function, call_data->call);
       return false;
@@ -716,14 +726,14 @@
       // arrays so that actual arguments are in one-to-one with the formal
       // parameters.
       if (function.HasOptionalParameters()) {
-        TRACE_INLINING(OS::Print("     adjusting for optional parameters\n"));
+        TRACE_INLINING(ISL_Print("     adjusting for optional parameters\n"));
         if (!AdjustForOptionalParameters(*parsed_function,
                                          argument_names,
                                          arguments,
                                          param_stubs,
                                          callee_graph)) {
           function.set_is_inlinable(false);
-          TRACE_INLINING(OS::Print("     Bailout: optional arg mismatch\n"));
+          TRACE_INLINING(ISL_Print("     Bailout: optional arg mismatch\n"));
           PRINT_INLINING_TREE("Optional arg mismatch",
               &call_data->caller, &function, call_data->call);
           return false;
@@ -775,7 +785,7 @@
 
       if (FLAG_trace_inlining &&
           (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) {
-        OS::Print("Callee graph for inlining %s\n",
+        ISL_Print("Callee graph for inlining %s\n",
                   function.ToFullyQualifiedCString());
         FlowGraphPrinter printer(*callee_graph);
         printer.PrintBlocks();
@@ -805,7 +815,7 @@
           function.set_is_inlinable(false);
         }
         isolate()->set_deopt_id(prev_deopt_id);
-        TRACE_INLINING(OS::Print("     Bailout: heuristics with "
+        TRACE_INLINING(ISL_Print("     Bailout: heuristics with "
                                  "code size:  %" Pd ", "
                                  "call sites: %" Pd ", "
                                  "const args: %" Pd "\n",
@@ -850,12 +860,13 @@
       // caller's list of deferred prefixes.
       caller_graph()->AddToDeferredPrefixes(callee_graph->deferred_prefixes());
 
-      FlowGraphInliner::SetInliningId(*callee_graph,
-          inliner_->NextInlineId(callee_graph->function()));
+      FlowGraphInliner::SetInliningId(callee_graph,
+          inliner_->NextInlineId(callee_graph->function(),
+                                 call_data->caller_inlining_id_));
       // We allocate a ZoneHandle for the unoptimized code so that it cannot be
       // disconnected from its function during the rest of compilation.
       Code::ZoneHandle(unoptimized_code.raw());
-      TRACE_INLINING(OS::Print("     Success\n"));
+      TRACE_INLINING(ISL_Print("     Success\n"));
       PRINT_INLINING_TREE(NULL,
           &call_data->caller, &function, call);
       return true;
@@ -864,7 +875,7 @@
       error = isolate()->object_store()->sticky_error();
       isolate()->object_store()->clear_sticky_error();
       isolate()->set_deopt_id(prev_deopt_id);
-      TRACE_INLINING(OS::Print("     Bailout: %s\n", error.ToErrorCString()));
+      TRACE_INLINING(ISL_Print("     Bailout: %s\n", error.ToErrorCString()));
       PRINT_INLINING_TREE("Bailout",
           &call_data->caller, &function, call);
       return false;
@@ -873,7 +884,7 @@
 
   void PrintInlinedInfo(const Function& top) {
     if (inlined_info_.length() > 0) {
-      OS::Print("Inlining into: '%s' growth: %f (%" Pd " -> %" Pd ")\n",
+      ISL_Print("Inlining into: '%s' growth: %f (%" Pd " -> %" Pd ")\n",
           top.ToFullyQualifiedCString(),
           GrowthFactor(),
           initial_size_,
@@ -906,9 +917,9 @@
           (info.caller->raw() == caller.raw()) &&
           !Contains(call_instructions_printed, info.call_instr->GetDeoptId())) {
         for (int t = 0; t < depth; t++) {
-          OS::Print("  ");
+          ISL_Print("  ");
         }
-        OS::Print("%" Pd " %s\n",
+        ISL_Print("%" Pd " %s\n",
             info.call_instr->GetDeoptId(),
             info.inlined->ToQualifiedCString());
         PrintInlinedInfoFor(*info.inlined, depth + 1);
@@ -926,9 +937,9 @@
           (info.caller->raw() == caller.raw()) &&
           !Contains(call_instructions_printed, info.call_instr->GetDeoptId())) {
         for (int t = 0; t < depth; t++) {
-          OS::Print("  ");
+          ISL_Print("  ");
         }
-        OS::Print("NO %" Pd " %s - %s\n",
+        ISL_Print("NO %" Pd " %s - %s\n",
             info.call_instr->GetDeoptId(),
             info.inlined->ToQualifiedCString(),
             info.bailout_reason);
@@ -1028,7 +1039,7 @@
   void InlineStaticCalls() {
     const GrowableArray<CallSites::StaticCallInfo>& call_info =
         inlining_call_sites_->static_calls();
-    TRACE_INLINING(OS::Print("  Static Calls (%" Pd ")\n", call_info.length()));
+    TRACE_INLINING(ISL_Print("  Static Calls (%" Pd ")\n", call_info.length()));
     for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) {
       StaticCallInstr* call = call_info[call_idx].call;
       if (call->function().name() == Symbols::ListFactory().raw()) {
@@ -1044,22 +1055,24 @@
         }
       }
       const Function& target = call->function();
-      if (!FlowGraphInliner::AlwaysInline(target) &&
+      if (!inliner_->AlwaysInline(target) &&
           (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) {
-        TRACE_INLINING(OS::Print(
+        TRACE_INLINING(ISL_Print(
             "  => %s (deopt count %d)\n     Bailout: cold %f\n",
             target.ToCString(),
             target.deoptimization_counter(),
             call_info[call_idx].ratio));
         PRINT_INLINING_TREE("Too cold",
-            call_info[call_idx].caller, &call->function(), call);
+            &call_info[call_idx].caller(), &call->function(), call);
         continue;
       }
       GrowableArray<Value*> arguments(call->ArgumentCount());
       for (int i = 0; i < call->ArgumentCount(); ++i) {
         arguments.Add(call->PushArgumentAt(i)->value());
       }
-      InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller);
+      InlinedCallData call_data(
+          call, &arguments, call_info[call_idx].caller(),
+          call_info[call_idx].caller_graph->inlining_id());
       if (TryInlining(call->function(), call->argument_names(), &call_data)) {
         InlineCall(&call_data);
       }
@@ -1069,7 +1082,7 @@
   void InlineClosureCalls() {
     const GrowableArray<CallSites::ClosureCallInfo>& call_info =
         inlining_call_sites_->closure_calls();
-    TRACE_INLINING(OS::Print("  Closure Calls (%" Pd ")\n",
+    TRACE_INLINING(ISL_Print("  Closure Calls (%" Pd ")\n",
         call_info.length()));
     for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) {
       ClosureCallInstr* call = call_info[call_idx].call;
@@ -1091,14 +1104,16 @@
       }
 
       if (target.IsNull()) {
-        TRACE_INLINING(OS::Print("     Bailout: non-closure operator\n"));
+        TRACE_INLINING(ISL_Print("     Bailout: non-closure operator\n"));
         continue;
       }
       GrowableArray<Value*> arguments(call->ArgumentCount());
       for (int i = 0; i < call->ArgumentCount(); ++i) {
         arguments.Add(call->PushArgumentAt(i)->value());
       }
-      InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller);
+      InlinedCallData call_data(
+          call, &arguments, call_info[call_idx].caller(),
+          call_info[call_idx].caller_graph->inlining_id());
       if (TryInlining(target,
                       call->argument_names(),
                       &call_data)) {
@@ -1110,35 +1125,39 @@
   void InlineInstanceCalls() {
     const GrowableArray<CallSites::InstanceCallInfo>& call_info =
         inlining_call_sites_->instance_calls();
-    TRACE_INLINING(OS::Print("  Polymorphic Instance Calls (%" Pd ")\n",
+    TRACE_INLINING(ISL_Print("  Polymorphic Instance Calls (%" Pd ")\n",
                              call_info.length()));
     for (intptr_t call_idx = 0; call_idx < call_info.length(); ++call_idx) {
       PolymorphicInstanceCallInstr* call = call_info[call_idx].call;
       if (call->with_checks()) {
-        const Function& cl = *call_info[call_idx].caller;
-        PolymorphicInliner inliner(this, call, cl);
+        const Function& cl = call_info[call_idx].caller();
+        intptr_t caller_inlining_id =
+            call_info[call_idx].caller_graph->inlining_id();
+        PolymorphicInliner inliner(this, call, cl, caller_inlining_id);
         inliner.Inline();
         continue;
       }
 
       const ICData& ic_data = call->ic_data();
       const Function& target = Function::ZoneHandle(ic_data.GetTargetAt(0));
-      if (!FlowGraphInliner::AlwaysInline(target) &&
+      if (!inliner_->AlwaysInline(target) &&
           (call_info[call_idx].ratio * 100) < FLAG_inlining_hotness) {
-        TRACE_INLINING(OS::Print(
+        TRACE_INLINING(ISL_Print(
             "  => %s (deopt count %d)\n     Bailout: cold %f\n",
             target.ToCString(),
             target.deoptimization_counter(),
             call_info[call_idx].ratio));
         PRINT_INLINING_TREE("Too cold",
-            call_info[call_idx].caller, &target, call);
+            &call_info[call_idx].caller(), &target, call);
         continue;
       }
       GrowableArray<Value*> arguments(call->ArgumentCount());
       for (int arg_i = 0; arg_i < call->ArgumentCount(); ++arg_i) {
         arguments.Add(call->PushArgumentAt(arg_i)->value());
       }
-      InlinedCallData call_data(call, &arguments, *call_info[call_idx].caller);
+      InlinedCallData call_data(
+          call, &arguments, call_info[call_idx].caller(),
+          call_info[call_idx].caller_graph->inlining_id());
       if (TryInlining(target,
                       call->instance_call()->argument_names(),
                       &call_data)) {
@@ -1258,7 +1277,8 @@
 
 PolymorphicInliner::PolymorphicInliner(CallSiteInliner* owner,
                                        PolymorphicInstanceCallInstr* call,
-                                       const Function& caller_function)
+                                       const Function& caller_function,
+                                       intptr_t caller_inlining_id)
     : owner_(owner),
       call_(call),
       num_variants_(call->ic_data().NumberOfChecks()),
@@ -1268,7 +1288,8 @@
       inlined_entries_(num_variants_),
       exit_collector_(new(Z)
           InlineExitCollector(owner->caller_graph(), call)),
-      caller_function_(caller_function) {
+      caller_function_(caller_function),
+      caller_inlining_id_(caller_inlining_id) {
 }
 
 
@@ -1366,7 +1387,9 @@
   for (int i = 0; i < call_->ArgumentCount(); ++i) {
     arguments.Add(call_->PushArgumentAt(i)->value());
   }
-  InlinedCallData call_data(call_, &arguments, caller_function_);
+  InlinedCallData call_data(call_, &arguments,
+                            caller_function_,
+                            caller_inlining_id_);
   if (!owner_->TryInlining(target,
                            call_->instance_call()->argument_names(),
                            &call_data)) {
@@ -1736,6 +1759,23 @@
 }
 
 
+static bool ShouldTraceInlining(FlowGraph* flow_graph) {
+  const Function& top = flow_graph->parsed_function().function();
+  return FLAG_trace_inlining && FlowGraphPrinter::ShouldPrint(top);
+}
+
+
+FlowGraphInliner::FlowGraphInliner(
+    FlowGraph* flow_graph,
+    GrowableArray<const Function*>* inline_id_to_function,
+    GrowableArray<intptr_t>* caller_inline_id)
+    : flow_graph_(flow_graph),
+      inline_id_to_function_(inline_id_to_function),
+      caller_inline_id_(caller_inline_id),
+      trace_inlining_(ShouldTraceInlining(flow_graph)) {
+}
+
+
 void FlowGraphInliner::CollectGraphInfo(FlowGraph* flow_graph, bool force) {
   const Function& function = flow_graph->function();
   if (force || (function.optimized_instruction_count() == 0)) {
@@ -1750,9 +1790,13 @@
 
 
 // TODO(srdjan): This is only needed when disassembling and/or profiling.
-void FlowGraphInliner::SetInliningId(const FlowGraph& flow_graph,
+// Sets inlining id for all instructions of this flow-graph, as well for the
+// FlowGraph itself.
+void FlowGraphInliner::SetInliningId(FlowGraph* flow_graph,
                                      intptr_t inlining_id) {
-  for (BlockIterator block_it = flow_graph.postorder_iterator();
+  ASSERT(flow_graph->inlining_id() < 0);
+  flow_graph->set_inlining_id(inlining_id);
+  for (BlockIterator block_it = flow_graph->postorder_iterator();
        !block_it.Done();
        block_it.Advance()) {
     for (ForwardInstructionIterator it(block_it.Current());
@@ -1771,7 +1815,7 @@
   const char* kAlwaysInlineAnnotation = "AlwaysInline";
   if (FLAG_enable_inlining_annotations &&
       HasAnnotation(function, kAlwaysInlineAnnotation)) {
-    TRACE_INLINING(OS::Print("AlwaysInline annotation for %s\n",
+    TRACE_INLINING(ISL_Print("AlwaysInline annotation for %s\n",
                              function.ToCString()));
     return true;
   }
@@ -1798,11 +1842,11 @@
     return;
   }
 
-  TRACE_INLINING(OS::Print("Inlining calls in %s\n", top.ToCString()));
+  TRACE_INLINING(ISL_Print("Inlining calls in %s\n", top.ToCString()));
 
-  if (FLAG_trace_inlining &&
+  if (trace_inlining() &&
       (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized)) {
-    OS::Print("Before Inlining of %s\n", flow_graph_->
+    ISL_Print("Before Inlining of %s\n", flow_graph_->
               function().ToFullyQualifiedCString());
     FlowGraphPrinter printer(*flow_graph_);
     printer.PrintBlocks();
@@ -1816,10 +1860,10 @@
 
   if (inliner.inlined()) {
     flow_graph_->DiscoverBlocks();
-    if (FLAG_trace_inlining) {
-      OS::Print("Inlining growth factor: %f\n", inliner.GrowthFactor());
+    if (trace_inlining()) {
+      ISL_Print("Inlining growth factor: %f\n", inliner.GrowthFactor());
       if (FLAG_print_flow_graph || FLAG_print_flow_graph_optimized) {
-        OS::Print("After Inlining of %s\n", flow_graph_->
+        ISL_Print("After Inlining of %s\n", flow_graph_->
                   function().ToFullyQualifiedCString());
         FlowGraphPrinter printer(*flow_graph_);
         printer.PrintBlocks();
@@ -1829,9 +1873,11 @@
 }
 
 
-intptr_t FlowGraphInliner::NextInlineId(const Function& function) {
+intptr_t FlowGraphInliner::NextInlineId(const Function& function,
+                                        intptr_t parent_id) {
   const intptr_t id = inline_id_to_function_->length();
   inline_id_to_function_->Add(&function);
+  caller_inline_id_->Add(parent_id);
   return id;
 }
 
diff --git a/runtime/vm/flow_graph_inliner.h b/runtime/vm/flow_graph_inliner.h
index 6f92aa8..114348b 100644
--- a/runtime/vm/flow_graph_inliner.h
+++ b/runtime/vm/flow_graph_inliner.h
@@ -17,27 +17,30 @@
 class FlowGraphInliner : ValueObject {
  public:
   FlowGraphInliner(FlowGraph* flow_graph,
-                   GrowableArray<const Function*>* inline_id_to_function)
-      : flow_graph_(flow_graph), inline_id_to_function_(inline_id_to_function) {
-  }
+                   GrowableArray<const Function*>* inline_id_to_function,
+                   GrowableArray<intptr_t>* caller_inline_id);
 
   // The flow graph is destructively updated upon inlining.
   void Inline();
 
   // Compute graph info if it was not already computed or if 'force' is true.
   static void CollectGraphInfo(FlowGraph* flow_graph, bool force = false);
-  static void SetInliningId(const FlowGraph& flow_graph, intptr_t inlining_id);
+  static void SetInliningId(FlowGraph* flow_graph, intptr_t inlining_id);
 
-  static bool AlwaysInline(const Function& function);
+  bool AlwaysInline(const Function& function);
 
   FlowGraph* flow_graph() const { return flow_graph_; }
-  intptr_t NextInlineId(const Function& function);
+  intptr_t NextInlineId(const Function& function, intptr_t caller_id);
+
+  bool trace_inlining() const { return trace_inlining_; }
 
  private:
   friend class CallSiteInliner;
 
   FlowGraph* flow_graph_;
   GrowableArray<const Function*>* inline_id_to_function_;
+  GrowableArray<intptr_t>* caller_inline_id_;
+  const bool trace_inlining_;
 
   DISALLOW_COPY_AND_ASSIGN(FlowGraphInliner);
 };
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 9c780bf..2a4a5db 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -565,16 +565,16 @@
     EnsureSSATempIndex(graph, current_defn, replacement_defn);
 
     if (FLAG_trace_optimization) {
-      OS::Print("Replacing v%" Pd " with v%" Pd "\n",
+      ISL_Print("Replacing v%" Pd " with v%" Pd "\n",
                 current_defn->ssa_temp_index(),
                 replacement_defn->ssa_temp_index());
     }
   } else if (FLAG_trace_optimization) {
     if (current_defn == NULL) {
-      OS::Print("Removing %s\n", current->DebugName());
+      ISL_Print("Removing %s\n", current->DebugName());
     } else {
       ASSERT(!current_defn->HasUses());
-      OS::Print("Removing v%" Pd ".\n", current_defn->ssa_temp_index());
+      ISL_Print("Removing v%" Pd ".\n", current_defn->ssa_temp_index());
     }
   }
   iterator->RemoveCurrentFromGraph();
@@ -4439,7 +4439,7 @@
                    getter.usage_counter());
     if (!result) {
       if (FLAG_trace_optimization) {
-        OS::Print("Disabling unboxing of %s\n", field.ToCString());
+        ISL_Print("Disabling unboxing of %s\n", field.ToCString());
       }
       field.set_is_unboxing_candidate(false);
       field.DeoptimizeDependentCode();
@@ -4674,7 +4674,7 @@
     }
 
     if (FLAG_trace_smi_widening) {
-      OS::Print("analysing candidate: %s\n", op->ToCString());
+      ISL_Print("analysing candidate: %s\n", op->ToCString());
     }
     worklist.Clear();
     worklist.Add(op);
@@ -4686,14 +4686,14 @@
       Definition* defn = worklist.definitions()[j];
 
       if (FLAG_trace_smi_widening) {
-        OS::Print("> %s\n", defn->ToCString());
+        ISL_Print("> %s\n", defn->ToCString());
       }
 
       if (defn->IsBinarySmiOp() &&
           BenefitsFromWidening(defn->AsBinarySmiOp())) {
         gain++;
         if (FLAG_trace_smi_widening) {
-          OS::Print("^ [%" Pd "] (o) %s\n", gain, defn->ToCString());
+          ISL_Print("^ [%" Pd "] (o) %s\n", gain, defn->ToCString());
         }
       }
 
@@ -4711,7 +4711,7 @@
           // Mint operation produces untagged result. We avoid tagging.
           gain++;
           if (FLAG_trace_smi_widening) {
-            OS::Print("^ [%" Pd "] (i) %s\n", gain, input->ToCString());
+            ISL_Print("^ [%" Pd "] (i) %s\n", gain, input->ToCString());
           }
         } else if (defn_loop == loops[input->GetBlock()->preorder_number()] &&
                    (input->Type()->ToCid() == kSmiCid)) {
@@ -4722,7 +4722,7 @@
           // coalesced with untagging. Start coalescing them.
           gain--;
           if (FLAG_trace_smi_widening) {
-            OS::Print("v [%" Pd "] (i) %s\n", gain, input->ToCString());
+            ISL_Print("v [%" Pd "] (i) %s\n", gain, input->ToCString());
           }
         }
       }
@@ -4739,7 +4739,7 @@
           if (!instr->IsReturn() && !instr->IsPushArgument()) {
             gain--;
             if (FLAG_trace_smi_widening) {
-              OS::Print("v [%" Pd "] (u) %s\n",
+              ISL_Print("v [%" Pd "] (u) %s\n",
                         gain,
                         use->instruction()->ToCString());
             }
@@ -4757,14 +4757,14 @@
           // sign extension operation.
           gain++;
           if (FLAG_trace_smi_widening) {
-            OS::Print("^ [%" Pd "] (u) %s\n",
+            ISL_Print("^ [%" Pd "] (u) %s\n",
                       gain,
                       use->instruction()->ToCString());
           }
         } else if (defn_loop == loops[instr->GetBlock()->preorder_number()]) {
           gain--;
           if (FLAG_trace_smi_widening) {
-            OS::Print("v [%" Pd "] (u) %s\n",
+            ISL_Print("v [%" Pd "] (u) %s\n",
                       gain,
                       use->instruction()->ToCString());
           }
@@ -4775,7 +4775,7 @@
     processed->AddAll(worklist.contains_vector());
 
     if (FLAG_trace_smi_widening) {
-      OS::Print("~ %s gain %" Pd "\n", op->ToCString(), gain);
+      ISL_Print("~ %s gain %" Pd "\n", op->ToCString(), gain);
     }
 
     if (gain > 0) {
@@ -4903,7 +4903,7 @@
     current->AsCheckArrayBound()->set_licm_hoisted(true);
   }
   if (FLAG_trace_optimization) {
-    OS::Print("Hoisting instruction %s:%" Pd " from B%" Pd " to B%" Pd "\n",
+    ISL_Print("Hoisting instruction %s:%" Pd " from B%" Pd " to B%" Pd "\n",
               current->DebugName(),
               current->GetDeoptId(),
               current->GetBlock()->block_id(),
@@ -5720,9 +5720,9 @@
          !it.Done();
          it.Advance()) {
       if (comma) {
-        OS::Print(", ");
+        ISL_Print(", ");
       }
-      OS::Print("%s", places_[it.Current()]->ToCString());
+      ISL_Print("%s", places_[it.Current()]->ToCString());
       comma = true;
     }
   }
@@ -5817,16 +5817,16 @@
     }
 
     if (FLAG_trace_load_optimization) {
-      OS::Print("Aliases KILL sets:\n");
+      ISL_Print("Aliases KILL sets:\n");
       for (intptr_t i = 0; i < aliases_.length(); ++i) {
         const Place* alias = aliases_[i];
         BitVector* kill = GetKilledSet(alias->id());
 
-        OS::Print("%s: ", alias->ToCString());
+        ISL_Print("%s: ", alias->ToCString());
         if (kill != NULL) {
           PrintSet(kill);
         }
-        OS::Print("\n");
+        ISL_Print("\n");
       }
     }
   }
@@ -6214,7 +6214,7 @@
       BlockEntryInstr* block = phi->GetBlock();
 
       if (FLAG_trace_optimization) {
-        OS::Print("phi dependent place %s\n", place->ToCString());
+        ISL_Print("phi dependent place %s\n", place->ToCString());
       }
 
       Place input_place(*place);
@@ -6227,7 +6227,7 @@
           map->Insert(result);
           places->Add(result);
           if (FLAG_trace_optimization) {
-            OS::Print("  adding place %s as %" Pd "\n",
+            ISL_Print("  adding place %s as %" Pd "\n",
                       result->ToCString(),
                       result->id());
           }
@@ -6283,7 +6283,7 @@
         places->Add(result);
 
         if (FLAG_trace_optimization) {
-          OS::Print("numbering %s as %" Pd "\n",
+          ISL_Print("numbering %s as %" Pd "\n",
                     result->ToCString(),
                     result->id());
         }
@@ -6552,7 +6552,7 @@
           Definition* replacement = (*out_values)[place_id];
           EnsureSSATempIndex(graph_, defn, replacement);
           if (FLAG_trace_optimization) {
-            OS::Print("Replacing load v%" Pd " with v%" Pd "\n",
+            ISL_Print("Replacing load v%" Pd " with v%" Pd "\n",
                       defn->ssa_temp_index(),
                       replacement->ssa_temp_index());
           }
@@ -6760,18 +6760,18 @@
       }
 
       if (FLAG_trace_load_optimization) {
-        OS::Print("B%" Pd "\n", block->block_id());
-        OS::Print("  IN: ");
+        ISL_Print("B%" Pd "\n", block->block_id());
+        ISL_Print("  IN: ");
         aliased_set_->PrintSet(in_[preorder_number]);
-        OS::Print("\n");
+        ISL_Print("\n");
 
-        OS::Print("  KILL: ");
+        ISL_Print("  KILL: ");
         aliased_set_->PrintSet(kill_[preorder_number]);
-        OS::Print("\n");
+        ISL_Print("\n");
 
-        OS::Print("  OUT: ");
+        ISL_Print("  OUT: ");
         aliased_set_->PrintSet(out_[preorder_number]);
-        OS::Print("\n");
+        ISL_Print("\n");
       }
     }
 
@@ -6824,7 +6824,7 @@
 
       if (FLAG_trace_optimization) {
         for (BitVector::Iterator it(loop_gen); !it.Done(); it.Advance()) {
-          OS::Print("place %s is loop invariant for B%" Pd "\n",
+          ISL_Print("place %s is loop invariant for B%" Pd "\n",
                     aliased_set_->places()[it.Current()]->ToCString(),
                     header->block_id());
         }
@@ -6895,7 +6895,7 @@
     phis_.Add(phi);  // Postpone phi insertion until after load forwarding.
 
     if (FLAG_trace_load_optimization) {
-      OS::Print("created pending phi %s for %s at B%" Pd "\n",
+      ISL_Print("created pending phi %s for %s at B%" Pd "\n",
                 phi->ToCString(),
                 aliased_set_->places()[place_id]->ToCString(),
                 block->block_id());
@@ -6934,7 +6934,7 @@
           EnsureSSATempIndex(graph_, load, replacement);
 
           if (FLAG_trace_optimization) {
-            OS::Print("Replacing load v%" Pd " with v%" Pd "\n",
+            ISL_Print("Replacing load v%" Pd " with v%" Pd "\n",
                       load->ssa_temp_index(),
                       replacement->ssa_temp_index());
           }
@@ -7123,7 +7123,7 @@
       }
 
       if (FLAG_trace_load_optimization) {
-        OS::Print("Replacing %s with congruent %s\n",
+        ISL_Print("Replacing %s with congruent %s\n",
                   a->ToCString(),
                   b->ToCString());
       }
@@ -7329,7 +7329,7 @@
             if (!live_in->Contains(instr->place_id()) &&
                 CanEliminateStore(instr)) {
               if (FLAG_trace_optimization) {
-                OS::Print(
+                ISL_Print(
                     "Removing dead store to place %" Pd " in block B%" Pd "\n",
                     instr->place_id(), block->block_id());
               }
@@ -7382,7 +7382,7 @@
     }
     if (FLAG_trace_load_optimization) {
       Dump();
-      OS::Print("---\n");
+      ISL_Print("---\n");
     }
   }
 
@@ -7416,7 +7416,7 @@
         if (!live_out->Contains(instr->place_id()) &&
             CanEliminateStore(instr)) {
           if (FLAG_trace_optimization) {
-            OS::Print("Removing dead store to place %" Pd " block B%" Pd "\n",
+            ISL_Print("Removing dead store to place %" Pd " block B%" Pd "\n",
                       instr->place_id(), block->block_id());
           }
           instr->RemoveFromGraph(/* ignored */ false);
@@ -7510,14 +7510,14 @@
             phi->UnuseAllInputs();
             (*join->phis_)[i] = NULL;
             if (FLAG_trace_optimization) {
-              OS::Print("Removing dead phi v%" Pd "\n", phi->ssa_temp_index());
+              ISL_Print("Removing dead phi v%" Pd "\n", phi->ssa_temp_index());
             }
           } else if (phi->IsRedundant()) {
             phi->ReplaceUsesWith(phi->InputAt(0)->definition());
             phi->UnuseAllInputs();
             (*join->phis_)[i] = NULL;
             if (FLAG_trace_optimization) {
-              OS::Print("Removing redundant phi v%" Pd "\n",
+              ISL_Print("Removing redundant phi v%" Pd "\n",
                          phi->ssa_temp_index());
             }
           } else {
@@ -8060,7 +8060,7 @@
        use = use->next_use()) {
     if (!IsSafeUse(use, check_type)) {
       if (FLAG_trace_optimization) {
-        OS::Print("use of %s at %s is unsafe for allocation sinking\n",
+        ISL_Print("use of %s at %s is unsafe for allocation sinking\n",
                   alloc->ToCString(),
                   use->instruction()->ToCString());
       }
@@ -8090,7 +8090,7 @@
   ASSERT(IsAllocationSinkingCandidate(alloc, kStrictCheck));
 
   if (FLAG_trace_optimization) {
-    OS::Print("removing allocation from the graph: v%" Pd "\n",
+    ISL_Print("removing allocation from the graph: v%" Pd "\n",
               alloc->ssa_temp_index());
   }
 
@@ -8171,7 +8171,7 @@
     Definition* alloc = candidates_[i];
     if (alloc->Identity().IsAllocationSinkingCandidate()) {
       if (FLAG_trace_optimization) {
-        OS::Print("discovered allocation sinking candidate: v%" Pd "\n",
+        ISL_Print("discovered allocation sinking candidate: v%" Pd "\n",
                   alloc->ssa_temp_index());
       }
 
@@ -8266,7 +8266,7 @@
     Definition* alloc = candidates_[i];
     if (!alloc->Identity().IsAllocationSinkingCandidate()) {
       if (FLAG_trace_optimization) {
-        OS::Print("allocation v%" Pd " can't be eliminated\n",
+        ISL_Print("allocation v%" Pd " can't be eliminated\n",
                   alloc->ssa_temp_index());
       }
 
diff --git a/runtime/vm/flow_graph_range_analysis.cc b/runtime/vm/flow_graph_range_analysis.cc
index ab54a1f..738c294 100644
--- a/runtime/vm/flow_graph_range_analysis.cc
+++ b/runtime/vm/flow_graph_range_analysis.cc
@@ -183,7 +183,7 @@
         InductionVariableInfo* info = DetectSimpleInductionVariable(current);
         if (info != NULL) {
           if (FLAG_trace_range_analysis) {
-            OS::Print("Simple loop variable: %s bound <%s>\n",
+            ISL_Print("Simple loop variable: %s bound <%s>\n",
                        current->ToCString(),
                        info->limit() != NULL ?
                            info->limit()->ToCString() : "?");
@@ -695,7 +695,7 @@
 
     if (!range.Equals(defn->range())) {
       if (FLAG_trace_range_analysis) {
-        OS::Print("%c [%" Pd "] %s:  %s => %s\n",
+        ISL_Print("%c [%" Pd "] %s:  %s => %s\n",
                   OpPrefix(op),
                   iteration,
                   defn->ToCString(),
@@ -998,7 +998,7 @@
     if (upper_bound == UnwrapConstraint(check->index()->definition())) {
       // Unable to construct upper bound for the index.
       if (FLAG_trace_range_analysis) {
-        OS::Print("Failed to construct upper bound for %s index\n",
+        ISL_Print("Failed to construct upper bound for %s index\n",
                   check->ToCString());
       }
       return;
@@ -1009,7 +1009,7 @@
     // upper bound through scheduler.
     if (!Simplify(&upper_bound, NULL)) {
       if (FLAG_trace_range_analysis) {
-        OS::Print("Failed to simplify upper bound for %s index\n",
+        ISL_Print("Failed to simplify upper bound for %s index\n",
                   check->ToCString());
       }
       return;
@@ -1024,7 +1024,7 @@
     GrowableArray<Definition*> non_positive_symbols;
     if (!FindNonPositiveSymbols(&non_positive_symbols, upper_bound)) {
       if (FLAG_trace_range_analysis) {
-        OS::Print("Failed to generalize %s index to %s"
+        ISL_Print("Failed to generalize %s index to %s"
                   " (can't ensure positivity)\n",
                   check->ToCString(),
                   IndexBoundToCString(upper_bound));
@@ -1058,7 +1058,7 @@
       // Can't prove that lower bound is positive even with additional checks
       // against potentially non-positive symbols. Give up.
       if (FLAG_trace_range_analysis) {
-        OS::Print("Failed to generalize %s index to %s"
+        ISL_Print("Failed to generalize %s index to %s"
                   " (lower bound is not positive)\n",
                   check->ToCString(),
                   IndexBoundToCString(upper_bound));
@@ -1067,7 +1067,7 @@
     }
 
     if (FLAG_trace_range_analysis) {
-      OS::Print("For %s computed index bounds [%s, %s]\n",
+      ISL_Print("For %s computed index bounds [%s, %s]\n",
                 check->ToCString(),
                 IndexBoundToCString(lower_bound),
                 IndexBoundToCString(upper_bound));
@@ -1088,7 +1088,7 @@
       precondition = scheduler_.Emit(precondition, check);
       if (precondition == NULL) {
         if (FLAG_trace_range_analysis) {
-          OS::Print("  => failed to insert positivity constraint\n");
+          ISL_Print("  => failed to insert positivity constraint\n");
         }
         scheduler_.Rollback();
         return;
@@ -1102,7 +1102,7 @@
     new_check->mark_generalized();
     if (new_check->IsRedundant(array_length)) {
       if (FLAG_trace_range_analysis) {
-        OS::Print("  => generalized check is redundant\n");
+        ISL_Print("  => generalized check is redundant\n");
       }
       RemoveGeneralizedCheck(check);
       return;
@@ -1111,13 +1111,13 @@
     new_check = scheduler_.Emit(new_check, check);
     if (new_check != NULL) {
       if (FLAG_trace_range_analysis) {
-        OS::Print("  => generalized check was hoisted into B%" Pd "\n",
+        ISL_Print("  => generalized check was hoisted into B%" Pd "\n",
                   new_check->GetBlock()->block_id());
       }
       RemoveGeneralizedCheck(check);
     } else {
       if (FLAG_trace_range_analysis) {
-        OS::Print("  => generalized check can't be hoisted\n");
+        ISL_Print("  => generalized check can't be hoisted\n");
       }
       scheduler_.Rollback();
     }
@@ -1567,7 +1567,7 @@
       if (target == branch->true_successor()) {
         // True unreachable.
         if (FLAG_trace_constant_propagation) {
-          OS::Print("Range analysis: True unreachable (B%" Pd ")\n",
+          ISL_Print("Range analysis: True unreachable (B%" Pd ")\n",
                     branch->true_successor()->block_id());
         }
         branch->set_constant_target(branch->false_successor());
@@ -1575,7 +1575,7 @@
         ASSERT(target == branch->false_successor());
         // False unreachable.
         if (FLAG_trace_constant_propagation) {
-          OS::Print("Range analysis: False unreachable (B%" Pd ")\n",
+          ISL_Print("Range analysis: False unreachable (B%" Pd ")\n",
                     branch->false_successor()->block_id());
         }
         branch->set_constant_target(branch->true_successor());
@@ -1666,14 +1666,14 @@
 
 void IntegerInstructionSelector::Select() {
   if (FLAG_trace_integer_ir_selection) {
-    OS::Print("---- starting integer ir selection -------\n");
+    ISL_Print("---- starting integer ir selection -------\n");
   }
   FindPotentialUint32Definitions();
   FindUint32NarrowingDefinitions();
   Propagate();
   ReplaceInstructions();
   if (FLAG_trace_integer_ir_selection) {
-    OS::Print("---- after integer ir selection -------\n");
+    ISL_Print("---- after integer ir selection -------\n");
     FlowGraphPrinter printer(*flow_graph_);
     printer.PrintBlocks();
   }
@@ -1694,7 +1694,7 @@
 
 void IntegerInstructionSelector::FindPotentialUint32Definitions() {
   if (FLAG_trace_integer_ir_selection) {
-    OS::Print("++++ Finding potential Uint32 definitions:\n");
+    ISL_Print("++++ Finding potential Uint32 definitions:\n");
   }
 
   for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator();
@@ -1710,7 +1710,7 @@
       if ((defn != NULL) && defn->HasSSATemp()) {
         if (IsPotentialUint32Definition(defn)) {
           if (FLAG_trace_integer_ir_selection) {
-           OS::Print("Adding %s\n", current->ToCString());
+           ISL_Print("Adding %s\n", current->ToCString());
           }
           potential_uint32_defs_.Add(defn);
         }
@@ -1744,14 +1744,14 @@
 void IntegerInstructionSelector::FindUint32NarrowingDefinitions() {
   ASSERT(selected_uint32_defs_ != NULL);
   if (FLAG_trace_integer_ir_selection) {
-    OS::Print("++++ Selecting Uint32 definitions:\n");
-    OS::Print("++++ Initial set:\n");
+    ISL_Print("++++ Selecting Uint32 definitions:\n");
+    ISL_Print("++++ Initial set:\n");
   }
   for (intptr_t i = 0; i < potential_uint32_defs_.length(); i++) {
     Definition* defn = potential_uint32_defs_[i];
     if (IsUint32NarrowingDefinition(defn)) {
       if (FLAG_trace_integer_ir_selection) {
-        OS::Print("Adding %s\n", defn->ToCString());
+        ISL_Print("Adding %s\n", defn->ToCString());
       }
       selected_uint32_defs_->Add(defn->ssa_temp_index());
     }
@@ -1811,7 +1811,7 @@
   intptr_t iteration = 0;
   while (changed) {
     if (FLAG_trace_integer_ir_selection) {
-      OS::Print("+++ Iteration: %" Pd "\n", iteration++);
+      ISL_Print("+++ Iteration: %" Pd "\n", iteration++);
     }
     changed = false;
     for (intptr_t i = 0; i < potential_uint32_defs_.length(); i++) {
@@ -1826,7 +1826,7 @@
       }
       if (CanBecomeUint32(defn)) {
         if (FLAG_trace_integer_ir_selection) {
-          OS::Print("Adding %s\n", defn->ToCString());
+          ISL_Print("Adding %s\n", defn->ToCString());
         }
         // Found a new candidate.
         selected_uint32_defs_->Add(defn->ssa_temp_index());
@@ -1836,7 +1836,7 @@
     }
   }
   if (FLAG_trace_integer_ir_selection) {
-    OS::Print("Reached fixed point\n");
+    ISL_Print("Reached fixed point\n");
   }
 }
 
@@ -1883,7 +1883,7 @@
 
 void IntegerInstructionSelector::ReplaceInstructions() {
   if (FLAG_trace_integer_ir_selection) {
-    OS::Print("++++ Replacing instructions:\n");
+    ISL_Print("++++ Replacing instructions:\n");
   }
   for (intptr_t i = 0; i < potential_uint32_defs_.length(); i++) {
     Definition* defn = potential_uint32_defs_[i];
@@ -1894,7 +1894,7 @@
     Definition* replacement = ConstructReplacementFor(defn);
     ASSERT(replacement != NULL);
     if (FLAG_trace_integer_ir_selection) {
-      OS::Print("Replacing %s with %s\n", defn->ToCString(),
+      ISL_Print("Replacing %s with %s\n", defn->ToCString(),
                                           replacement->ToCString());
     }
     if (!Range::IsUnknown(defn->range())) {
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index c550f0d..bc052f6 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -74,13 +74,13 @@
   while (!worklist_.is_empty()) {
     Definition* def = RemoveLastFromWorklist();
     if (FLAG_trace_type_propagation) {
-      OS::Print("recomputing type of v%" Pd ": %s\n",
+      ISL_Print("recomputing type of v%" Pd ": %s\n",
                 def->ssa_temp_index(),
                 def->Type()->ToCString());
     }
     if (def->RecomputeType()) {
       if (FLAG_trace_type_propagation) {
-        OS::Print("  ... new type %s\n", def->Type()->ToCString());
+        ISL_Print("  ... new type %s\n", def->Type()->ToCString());
       }
       for (Value::Iterator it(def->input_use_list());
            !it.Done();
@@ -260,7 +260,7 @@
   value->SetReachingType(type);
 
   if (FLAG_trace_type_propagation) {
-    OS::Print("reaching type to v%" Pd " for v%" Pd " is %s\n",
+    ISL_Print("reaching type to v%" Pd " for v%" Pd " is %s\n",
               value->instruction()->IsDefinition() ?
                   value->instruction()->AsDefinition()->ssa_temp_index() : -1,
               value->definition()->ssa_temp_index(),
@@ -680,7 +680,7 @@
   CompileType result = CompileType::None();
   for (intptr_t i = 0; i < InputCount(); i++) {
     if (FLAG_trace_type_propagation) {
-      OS::Print("  phi %" Pd " input %" Pd ": v%" Pd " has reaching type %s\n",
+      ISL_Print("  phi %" Pd " input %" Pd ": v%" Pd " has reaching type %s\n",
                 ssa_temp_index(),
                 i,
                 InputAt(i)->definition()->ssa_temp_index(),
@@ -750,13 +750,13 @@
   const AbstractType& type = scope->VariableAt(index())->type();
 
   // Parameter is the constructor phase.
-  if ((index() == 1) && function.IsConstructor()) {
+  if ((index() == 1) && function.IsGenerativeConstructor()) {
     return CompileType::FromAbstractType(type, CompileType::kNonNullable);
   }
 
   // Parameter is the receiver.
   if ((index() == 0) &&
-      (function.IsDynamicFunction() || function.IsConstructor())) {
+      (function.IsDynamicFunction() || function.IsGenerativeConstructor())) {
     if (type.IsObjectType() || type.IsNullType()) {
       // Receiver can be null.
       return CompileType::FromAbstractType(type, CompileType::kNullable);
diff --git a/runtime/vm/gc_marker.cc b/runtime/vm/gc_marker.cc
index 2587622..b68fdee 100644
--- a/runtime/vm/gc_marker.cc
+++ b/runtime/vm/gc_marker.cc
@@ -501,19 +501,23 @@
                            bool invoke_api_callbacks,
                            bool collect_code) {
   const bool visit_function_code = !collect_code;
-  MarkingStack marking_stack;
   Prologue(isolate, invoke_api_callbacks);
-  MarkingVisitor mark(
-      isolate, heap_, page_space, &marking_stack, visit_function_code);
-  IterateRoots(isolate, &mark, !invoke_api_callbacks);
-  DrainMarkingStack(isolate, &mark);
-  IterateWeakReferences(isolate, &mark);
-  MarkingWeakVisitor mark_weak;
-  IterateWeakRoots(isolate, &mark_weak, invoke_api_callbacks);
-  mark.Finalize();
-  ProcessWeakTables(page_space);
-  ProcessObjectIdTable(isolate);
-
+  // The API prologue/epilogue may create/destroy zones, so we must not
+  // depend on zone allocations surviving beyond the epilogue callback.
+  {
+    StackZone zone(isolate);
+    MarkingStack marking_stack;
+    MarkingVisitor mark(
+        isolate, heap_, page_space, &marking_stack, visit_function_code);
+    IterateRoots(isolate, &mark, !invoke_api_callbacks);
+    DrainMarkingStack(isolate, &mark);
+    IterateWeakReferences(isolate, &mark);
+    MarkingWeakVisitor mark_weak;
+    IterateWeakRoots(isolate, &mark_weak, invoke_api_callbacks);
+    mark.Finalize();
+    ProcessWeakTables(page_space);
+    ProcessObjectIdTable(isolate);
+  }
   Epilogue(isolate, invoke_api_callbacks);
 }
 
diff --git a/runtime/vm/il_printer.cc b/runtime/vm/il_printer.cc
index c81ac93..c94497a 100644
--- a/runtime/vm/il_printer.cc
+++ b/runtime/vm/il_printer.cc
@@ -12,7 +12,8 @@
 namespace dart {
 
 DEFINE_FLAG(bool, print_environments, false, "Print SSA environments.");
-
+DEFINE_FLAG(charp, print_flow_graph_filter, NULL,
+    "Print only IR of functions with matching names");
 
 void BufferFormatter::Print(const char* format, ...) {
   va_list args;
@@ -33,11 +34,58 @@
 }
 
 
+// Checks whether function's name matches the given filter, which is
+// a comma-separated list of strings.
+bool FlowGraphPrinter::PassesFilter(const char* filter,
+                                    const Function& function) {
+  if (filter == NULL) {
+    return true;
+  }
+
+  char* save_ptr;  // Needed for strtok_r.
+  const char* function_name = function.ToFullyQualifiedCString();
+  intptr_t function_name_len = strlen(function_name);
+
+  intptr_t len = strlen(filter) + 1;  // Length with \0.
+  char* filter_buffer = new char[len];
+  strncpy(filter_buffer, filter, len);  // strtok modifies arg 1.
+  char* token = strtok_r(filter_buffer, ",", &save_ptr);
+  bool found = false;
+  while (token != NULL) {
+    if (strstr(function_name, token) != NULL) {
+      found = true;
+      break;
+    }
+    const intptr_t token_len = strlen(token);
+    if (token[token_len - 1] == '%') {
+      if (function_name_len > token_len) {
+        const char* suffix = function_name +
+            (function_name_len - token_len + 1);
+        if (strncmp(suffix, token, token_len - 1) == 0) {
+          found = true;
+          break;
+        }
+      }
+    }
+    token = strtok_r(NULL, ",", &save_ptr);
+  }
+  delete[] filter_buffer;
+
+  return found;
+}
+
+
+bool FlowGraphPrinter::ShouldPrint(const Function& function) {
+  return PassesFilter(FLAG_print_flow_graph_filter, function);
+}
+
+
 void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) {
-  OS::Print("*** BEGIN CFG\n%s\n", phase);
+  LogBlock lb(Isolate::Current());
+  ISL_Print("*** BEGIN CFG\n%s\n", phase);
   FlowGraphPrinter printer(*flow_graph);
   printer.PrintBlocks();
-  OS::Print("*** END CFG\n");
+  ISL_Print("*** END CFG\n");
   fflush(stdout);
 }
 
@@ -46,19 +94,19 @@
                                   bool print_locations) {
   // Print the block entry.
   PrintOneInstruction(block, print_locations);
-  OS::Print("\n");
+  ISL_Print("\n");
   // And all the successors in the block.
   for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
     Instruction* current = it.Current();
     PrintOneInstruction(current, print_locations);
-    OS::Print("\n");
+    ISL_Print("\n");
   }
 }
 
 
 void FlowGraphPrinter::PrintBlocks() {
   if (!function_.IsNull()) {
-    OS::Print("==== %s\n", function_.ToFullyQualifiedCString());
+    ISL_Print("==== %s\n", function_.ToFullyQualifiedCString());
   }
 
   for (intptr_t i = 0; i < block_order_.length(); ++i) {
@@ -84,10 +132,10 @@
     instr->locs()->PrintTo(&f);
   }
   if (instr->lifetime_position() != -1) {
-    OS::Print("%3" Pd ": ", instr->lifetime_position());
+    ISL_Print("%3" Pd ": ", instr->lifetime_position());
   }
-  if (!instr->IsBlockEntry()) OS::Print("    ");
-  OS::Print("%s", str);
+  if (!instr->IsBlockEntry()) ISL_Print("    ");
+  ISL_Print("%s", str);
 }
 
 
@@ -101,7 +149,7 @@
     if (value != NULL && value->reaching_type_ != NULL) {
       compile_type_name = value->reaching_type_->ToCString();
     }
-    OS::Print("%s type check: compile type %s is %s specific than "
+    ISL_Print("%s type check: compile type %s is %s specific than "
               "type '%s' of '%s'.\n",
                          eliminated ? "Eliminated" : "Generated",
                          compile_type_name,
@@ -175,9 +223,9 @@
   char buffer[1024];
   BufferFormatter f(buffer, sizeof(buffer));
   PrintICDataHelper(&f, ic_data);
-  OS::Print("%s ", buffer);
+  ISL_Print("%s ", buffer);
   const Array& a = Array::Handle(ic_data.arguments_descriptor());
-  OS::Print(" arg-desc %" Pd "\n", a.Length());
+  ISL_Print(" arg-desc %" Pd "\n", a.Length());
 }
 
 
diff --git a/runtime/vm/il_printer.h b/runtime/vm/il_printer.h
index f311357..c1d036f 100644
--- a/runtime/vm/il_printer.h
+++ b/runtime/vm/il_printer.h
@@ -60,6 +60,10 @@
   // Debugging helper function.
   static void PrintICData(const ICData& ic_data);
 
+  static bool ShouldPrint(const Function& function);
+
+  static bool PassesFilter(const char* filter, const Function& function);
+
  private:
   const Function& function_;
   const GrowableArray<BlockEntryInstr*>& block_order_;
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 9d39bfa..4fad850 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -7967,6 +7967,16 @@
     return fixed_parameter_count_;
   }
 
+  intptr_t CountArgsPushed() {
+    intptr_t count = 0;
+    for (Environment::DeepIterator it(this); !it.Done(); it.Advance()) {
+      if (it.CurrentValue()->definition()->IsPushArgument()) {
+        count++;
+      }
+    }
+    return count;
+  }
+
   const Code& code() const { return parsed_function_.code(); }
 
   Environment* DeepCopy(Isolate* isolate) const {
diff --git a/runtime/vm/intrinsifier.cc b/runtime/vm/intrinsifier.cc
index 519ef63..56850d63 100644
--- a/runtime/vm/intrinsifier.cc
+++ b/runtime/vm/intrinsifier.cc
@@ -154,7 +154,7 @@
 #undef EMIT_CASE
   }
 
-  if (FLAG_print_flow_graph) {
+  if (FLAG_print_flow_graph && FlowGraphPrinter::ShouldPrint(function)) {
     OS::Print("Intrinsic graph before\n");
     FlowGraphPrinter printer(*graph);
     printer.PrintBlocks();
@@ -164,7 +164,7 @@
   FlowGraphAllocator allocator(*graph, true);  // Intrinsic mode.
   allocator.AllocateRegisters();
 
-  if (FLAG_print_flow_graph) {
+  if (FLAG_print_flow_graph && FlowGraphPrinter::ShouldPrint(function)) {
     OS::Print("Intrinsic graph after\n");
     FlowGraphPrinter printer(*graph);
     printer.PrintBlocks();
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 54a8692..b5b3ccf 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -381,8 +381,9 @@
 //   - left > 0 && left < right
 // R1: Tagged left (dividend).
 // R0: Tagged right (divisor).
-// Returns with result in R0, OR:
-//   R1: Untagged result (remainder).
+// Returns:
+//   R1: Untagged fallthrough result (remainder to be adjusted), or
+//   R0: Tagged return result (remainder).
 static void EmitRemainderOperation(Assembler* assembler) {
   Label modulo;
   const Register left = R1;
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index c61db05..f85b09b 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -366,8 +366,9 @@
 //   - left > 0 && left < right
 // R1: Tagged left (dividend).
 // R0: Tagged right (divisor).
-// Returns with result in R0, OR:
-//   R1: Untagged result (remainder).
+// Returns:
+//   R1: Untagged fallthrough result (remainder to be adjusted), or
+//   R0: Tagged return result (remainder).
 static void EmitRemainderOperation(Assembler* assembler) {
   Label return_zero, modulo;
   const Register left = R1;
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index 4fb2397..50d4d13 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -378,7 +378,9 @@
 //   - left > 0 && left < right
 // EAX: Tagged left (dividend).
 // EBX: Tagged right (divisor).
-// EDX: Untagged result (remainder).
+// Returns:
+//   EDX: Untagged fallthrough result (remainder to be adjusted), or
+//   EAX: Tagged return result (remainder).
 static void EmitRemainderOperation(Assembler* assembler) {
   Label return_zero, modulo;
   // Check for quick zero results.
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index a6bff54..61d2799 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -364,7 +364,9 @@
 //   - left > 0 && left < right
 // T1: Tagged left (dividend).
 // T0: Tagged right (divisor).
-// V0: Untagged result.
+// Returns:
+//   V0: Untagged fallthrough result (remainder to be adjusted), or
+//   V0: Tagged return result (remainder).
 static void EmitRemainderOperation(Assembler* assembler) {
   Label return_zero, modulo;
   const Register left = T1;
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index a3f40bf..c1d8171 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -336,7 +336,9 @@
 //   - left > 0 && left < right
 // RAX: Tagged left (dividend).
 // RCX: Tagged right (divisor).
-// RAX: Untagged result (remainder).
+// Returns:
+//   RAX: Untagged fallthrough result (remainder to be adjusted), or
+//   RAX: Tagged return result (remainder).
 static void EmitRemainderOperation(Assembler* assembler) {
   Label return_zero, try_modulo, not_32bit, done;
   // Check for quick zero results.
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index 31b8c9e..88c3726 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -16,6 +16,7 @@
 #include "vm/deopt_instructions.h"
 #include "vm/heap.h"
 #include "vm/lockers.h"
+#include "vm/log.h"
 #include "vm/message_handler.h"
 #include "vm/object_id_ring.h"
 #include "vm/object_store.h"
@@ -46,7 +47,9 @@
 DEFINE_FLAG(bool, break_at_isolate_spawn, false,
             "Insert a one-time breakpoint at the entrypoint for all spawned "
             "isolates");
-
+DEFINE_FLAG(charp, isolate_log_filter, NULL,
+            "Log isolates whose name include the filter. "
+            "Default: service isolate log messages are suppressed.");
 
 // Quick access to the locally defined isolate() method.
 #define I (isolate())
@@ -475,6 +478,8 @@
       gc_epilogue_callback_(NULL),
       defer_finalization_count_(0),
       deopt_context_(NULL),
+      is_service_isolate_(false),
+      log_(new class Log()),
       stacktrace_(NULL),
       stack_frame_index_(-1),
       last_allocationprofile_accumulator_reset_timestamp_(0),
@@ -534,6 +539,8 @@
       gc_epilogue_callback_(NULL),
       defer_finalization_count_(0),
       deopt_context_(NULL),
+      is_service_isolate_(false),
+      log_(new class Log()),
       stacktrace_(NULL),
       stack_frame_index_(-1),
       last_allocationprofile_accumulator_reset_timestamp_(0),
@@ -571,6 +578,8 @@
   message_handler_ = NULL;  // Fail fast if we send messages to a dead isolate.
   ASSERT(deopt_context_ == NULL);  // No deopt in progress when isolate deleted.
   delete spawn_state_;
+  delete log_;
+  log_ = NULL;
 }
 
 
@@ -624,11 +633,6 @@
   ISOLATE_METRIC_LIST(ISOLATE_METRIC_INIT);
 #undef ISOLATE_METRIC_INIT
 
-
-  // Add to isolate list.
-  AddIsolateTolist(result);
-
-
   // TODO(5411455): For now just set the recently created isolate as
   // the current isolate.
   SetCurrent(result);
@@ -675,6 +679,9 @@
     }
   }
 
+  // Add to isolate list.
+  AddIsolateTolist(result);
+
   return result;
 }
 
@@ -695,6 +702,23 @@
 }
 
 
+Log* Isolate::Log() const {
+  if (FLAG_isolate_log_filter == NULL) {
+    if (is_service_isolate_) {
+      // By default, do not log for the service isolate.
+      return Log::NoOpLog();
+    }
+    return log_;
+  }
+  ASSERT(name_ != NULL);
+  if (strstr(name_, FLAG_isolate_log_filter) == NULL) {
+    // Filter does not match, do not log for this isolate.
+    return Log::NoOpLog();
+  }
+  return log_;
+}
+
+
 // TODO(5411455): Use flag to override default value and Validate the
 // stack size by querying OS.
 uword Isolate::GetSpecifiedStackSize() {
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 765f88b..1b92b93 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -52,6 +52,7 @@
 class IsolateSpawnState;
 class InterruptableThreadState;
 class Library;
+class Log;
 class LongJumpScope;
 class MessageHandler;
 class Mutex;
@@ -167,6 +168,7 @@
   }
 
   const char* name() const { return name_; }
+  class Log* Log() const;
 
   int64_t start_time() const { return start_time_; }
 
@@ -693,6 +695,10 @@
   intptr_t defer_finalization_count_;
   DeoptContext* deopt_context_;
 
+  // Log.
+  bool is_service_isolate_;
+  class Log* log_;
+
   // Status support.
   char* stacktrace_;
   intptr_t stack_frame_index_;
@@ -769,6 +775,8 @@
 REUSABLE_HANDLE_LIST(REUSABLE_FRIEND_DECLARATION)
 #undef REUSABLE_FRIEND_DECLARATION
 
+  friend class Service;
+
   DISALLOW_COPY_AND_ASSIGN(Isolate);
 };
 
diff --git a/runtime/vm/locations.cc b/runtime/vm/locations.cc
index dd8cae5..69fc7cc 100644
--- a/runtime/vm/locations.cc
+++ b/runtime/vm/locations.cc
@@ -8,6 +8,7 @@
 #include "vm/il_printer.h"
 #include "vm/intermediate_language.h"
 #include "vm/flow_graph_compiler.h"
+#include "vm/log.h"
 #include "vm/stack_frame.h"
 
 namespace dart {
@@ -224,9 +225,9 @@
 
 void Location::Print() const {
   if (kind() == kStackSlot) {
-    OS::Print("S%+" Pd "", stack_index());
+    ISL_Print("S%+" Pd "", stack_index());
   } else {
-    OS::Print("%s", Name());
+    ISL_Print("%s", Name());
   }
 }
 
diff --git a/runtime/vm/locations.h b/runtime/vm/locations.h
index 8a653ebc..d57b2fd 100644
--- a/runtime/vm/locations.h
+++ b/runtime/vm/locations.h
@@ -8,6 +8,7 @@
 #include "vm/allocation.h"
 #include "vm/assembler.h"
 #include "vm/bitfield.h"
+#include "vm/log.h"
 
 namespace dart {
 
@@ -516,7 +517,7 @@
     for (intptr_t i = 0; i < kNumberOfCpuRegisters; i++) {
       Register r = static_cast<Register>(i);
       if (ContainsRegister(r)) {
-        OS::Print("%s %s\n", Assembler::RegisterName(r),
+        ISL_Print("%s %s\n", Assembler::RegisterName(r),
                            IsTagged(r) ? "tagged" : "untagged");
       }
     }
@@ -524,7 +525,7 @@
     for (intptr_t i = 0; i < kNumberOfFpuRegisters; i++) {
       FpuRegister r = static_cast<FpuRegister>(i);
       if (ContainsFpuRegister(r)) {
-        OS::Print("%s\n", Assembler::FpuRegisterName(r));
+        ISL_Print("%s\n", Assembler::FpuRegisterName(r));
       }
     }
   }
diff --git a/runtime/vm/log.cc b/runtime/vm/log.cc
new file mode 100644
index 0000000..c62d8e5
--- /dev/null
+++ b/runtime/vm/log.cc
@@ -0,0 +1,141 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "vm/log.h"
+
+#include "vm/flags.h"
+#include "vm/thread.h"
+
+namespace dart {
+
+DEFINE_FLAG(bool, force_log_flush, false, "Always flush log messages.");
+
+Log::Log(LogPrinter printer)
+    : printer_(printer),
+      manual_flush_(0),
+      buffer_(0) {
+}
+
+
+void Log::Print(const char* format, ...) {
+  if (this == NoOpLog()) {
+    return;
+  }
+
+  va_list args;
+  va_start(args, format);
+  VPrint(format, args);
+  va_end(args);
+}
+
+
+void Log::VPrint(const char* format, va_list args) {
+  if (this == NoOpLog()) {
+    return;
+  }
+
+  // Measure.
+  va_list measure_args;
+  va_copy(measure_args, args);
+  intptr_t len = OS::VSNPrint(NULL, 0, format, measure_args);
+  va_end(measure_args);
+
+  // Print.
+  char* buffer = reinterpret_cast<char*>(malloc(len + 1));
+  va_list print_args;
+  va_copy(print_args, args);
+  OS::VSNPrint(buffer, (len + 1), format, print_args);
+  va_end(print_args);
+
+  // Append.
+  // NOTE: does not append the '\0' character.
+  for (intptr_t i = 0; i < len; i++) {
+    buffer_.Add(buffer[i]);
+  }
+  free(buffer);
+
+  if ((manual_flush_ == 0) || FLAG_force_log_flush) {
+    Flush();
+  }
+}
+
+
+void Log::Flush(const intptr_t cursor) {
+  if (this == NoOpLog()) {
+    return;
+  }
+  if (buffer_.is_empty()) {
+    return;
+  }
+  if (buffer_.length() <= cursor) {
+    return;
+  }
+  TerminateString();
+  const char* str = &buffer_[cursor];
+  ASSERT(str != NULL);
+  printer_("%s", str);
+  buffer_.TruncateTo(cursor);
+}
+
+
+void Log::Clear() {
+  if (this == NoOpLog()) {
+    return;
+  }
+  buffer_.TruncateTo(0);
+}
+
+
+intptr_t Log::cursor() const {
+  return buffer_.length();
+}
+
+
+Log Log::noop_log_;
+Log* Log::NoOpLog() {
+  return &noop_log_;
+}
+
+
+void Log::TerminateString() {
+  buffer_.Add('\0');
+}
+
+
+void Log::EnableManualFlush() {
+  manual_flush_++;
+}
+
+
+void Log::DisableManualFlush() {
+  manual_flush_--;
+  ASSERT(manual_flush_ >= 0);
+  if (manual_flush_ == 0) {
+    Flush();
+  }
+}
+
+
+LogBlock::LogBlock(Thread* thread, Log* log)
+    : StackResource(thread->isolate()),
+      log_(log), cursor_(log->cursor()) {
+  CommonConstructor();
+}
+
+
+LogBlock::LogBlock(Isolate* isolate)
+    : StackResource(isolate),
+      log_(isolate->Log()), cursor_(isolate->Log()->cursor()) {
+  CommonConstructor();
+}
+
+
+LogBlock::LogBlock(Thread* thread)
+    : StackResource(thread->isolate()),
+      log_(thread->isolate()->Log()),
+      cursor_(thread->isolate()->Log()->cursor()) {
+  CommonConstructor();
+}
+
+}  // namespace dart
diff --git a/runtime/vm/log.h b/runtime/vm/log.h
new file mode 100644
index 0000000..f4c58e9
--- /dev/null
+++ b/runtime/vm/log.h
@@ -0,0 +1,103 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef VM_LOG_H_
+#define VM_LOG_H_
+
+#include "vm/allocation.h"
+#include "vm/growable_array.h"
+#include "vm/os.h"
+
+namespace dart {
+
+class Isolate;
+class LogBlock;
+class Thread;
+
+#if defined(_MSC_VER)
+#define ISL_Print(format, ...) \
+    Isolate::Current()->Log()->Print(format, __VA_ARGS__)
+#else
+#define ISL_Print(format, ...) \
+    Isolate::Current()->Log()->Print(format, ##__VA_ARGS__)
+#endif
+
+#define ISL_VPrint(format, args) \
+    Isolate::Current()->Log()->VPrint(format, args)
+
+typedef void (*LogPrinter)(const char* str, ...);
+
+class Log {
+ public:
+  explicit Log(LogPrinter printer = OS::Print);
+
+  // Append a formatted string to the log.
+  void Print(const char* format, ...) PRINTF_ATTRIBUTE(2, 3);
+
+  void VPrint(const char* format, va_list args);
+
+  // Flush and truncate the log. The log is flushed starting at cursor
+  // and truncated to cursor afterwards.
+  void Flush(const intptr_t cursor = 0);
+
+  // Clears the log.
+  void Clear();
+
+  // Current cursor.
+  intptr_t cursor() const;
+
+  // A logger that does nothing.
+  static Log* NoOpLog();
+
+ private:
+  void TerminateString();
+  void EnableManualFlush();
+  void DisableManualFlush();
+
+  static Log noop_log_;
+  LogPrinter printer_;
+  intptr_t manual_flush_;
+  MallocGrowableArray<char> buffer_;
+
+  friend class LogBlock;
+  friend class LogTestHelper;
+  DISALLOW_COPY_AND_ASSIGN(Log);
+};
+
+
+// Causes all log messages to be buffered until destructor is called.
+// Can be nested.
+class LogBlock : public StackResource {
+ public:
+  LogBlock(Isolate* isolate, Log* log)
+      : StackResource(isolate),
+        log_(log), cursor_(log->cursor()) {
+    CommonConstructor();
+  }
+
+  explicit LogBlock(Isolate* isolate);
+  explicit LogBlock(Thread* thread);
+
+  LogBlock(Thread* thread, Log* log);
+
+  ~LogBlock() {
+    CommonDestructor();
+  }
+
+ private:
+  void CommonConstructor() {
+    log_->EnableManualFlush();
+  }
+
+  void CommonDestructor() {
+    log_->Flush(cursor_);
+    log_->DisableManualFlush();
+  }
+  Log* log_;
+  const intptr_t cursor_;
+};
+
+}  // namespace dart
+
+#endif  // VM_LOG_H_
diff --git a/runtime/vm/log_test.cc b/runtime/vm/log_test.cc
new file mode 100644
index 0000000..eaaa7ad
--- /dev/null
+++ b/runtime/vm/log_test.cc
@@ -0,0 +1,92 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "platform/globals.h"
+
+#include "include/dart_debugger_api.h"
+#include "vm/dart_api_impl.h"
+#include "vm/dart_entry.h"
+#include "vm/debugger.h"
+#include "vm/globals.h"
+#include "vm/isolate.h"
+#include "vm/log.h"
+#include "vm/message_handler.h"
+#include "vm/unit_test.h"
+
+namespace dart {
+
+static const char* test_output_;
+static void TestPrinter(const char* format, ...) {
+  // Measure.
+  va_list args;
+  va_start(args, format);
+  intptr_t len = OS::VSNPrint(NULL, 0, format, args);
+  va_end(args);
+
+  // Print string to buffer.
+  char* buffer = reinterpret_cast<char*>(malloc(len + 1));
+  va_list args2;
+  va_start(args2, format);
+  OS::VSNPrint(buffer, (len + 1), format, args2);
+  va_end(args2);
+
+  // Leaks buffer.
+  test_output_ = buffer;
+}
+
+class LogTestHelper : public AllStatic {
+ public:
+  static void SetPrinter(Log* log, LogPrinter printer) {
+    ASSERT(log != NULL);
+    ASSERT(printer != NULL);
+    log->printer_ = printer;
+  }
+};
+
+
+TEST_CASE(Log_Macro) {
+  test_output_ = NULL;
+  Isolate* isolate = Isolate::Current();
+  Log* log = isolate->Log();
+  LogTestHelper::SetPrinter(log, TestPrinter);
+
+  ISL_Print("Hello %s", "World");
+  EXPECT_STREQ("Hello World", test_output_);
+  ISL_Print("SingleArgument");
+  EXPECT_STREQ("SingleArgument", test_output_);
+}
+
+
+TEST_CASE(Log_Basic) {
+  test_output_ = NULL;
+  Log* log = new Log(TestPrinter);
+
+  EXPECT_EQ(reinterpret_cast<const char*>(NULL), test_output_);
+  log->Print("Hello %s", "World");
+  EXPECT_STREQ("Hello World", test_output_);
+}
+
+
+TEST_CASE(Log_Block) {
+  test_output_ = NULL;
+  Log* log = new Log(TestPrinter);
+
+  Isolate* isolate = Isolate::Current();
+
+  EXPECT_EQ(reinterpret_cast<const char*>(NULL), test_output_);
+  {
+    LogBlock ba(isolate, log);
+    log->Print("APPLE");
+    EXPECT_EQ(reinterpret_cast<const char*>(NULL), test_output_);
+    {
+      LogBlock ba(isolate, log);
+      log->Print("BANANA");
+      EXPECT_EQ(reinterpret_cast<const char*>(NULL), test_output_);
+    }
+    EXPECT_STREQ("BANANA", test_output_);
+  }
+  EXPECT_STREQ("APPLE", test_output_);
+}
+
+}  // namespace dart
diff --git a/runtime/vm/native_arguments.h b/runtime/vm/native_arguments.h
index 938ad80..26fcb5c 100644
--- a/runtime/vm/native_arguments.h
+++ b/runtime/vm/native_arguments.h
@@ -143,7 +143,7 @@
 
   static intptr_t ParameterCountForResolution(const Function& function) {
     ASSERT(function.is_native());
-    ASSERT(!function.IsConstructor());  // Not supported.
+    ASSERT(!function.IsGenerativeConstructor());  // Not supported.
     intptr_t count = function.NumParameters();
     if (function.is_static() && function.IsClosureFunction()) {
       // The closure object is hidden and not accessible from native code.
@@ -157,7 +157,7 @@
 
   static int ComputeArgcTag(const Function& function) {
     ASSERT(function.is_native());
-    ASSERT(!function.IsConstructor());  // Not supported.
+    ASSERT(!function.IsGenerativeConstructor());  // Not supported.
     int tag = ArgcBits::encode(function.NumParameters());
     int function_bits = 0;
     if (!function.is_static()) {
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index e774599..f51342d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -2721,7 +2721,7 @@
   }
   for (intptr_t i = 0; i < patch_len; i++) {
     func ^= patch_list.At(i);
-    if (func.IsConstructor() || func.IsFactory()) {
+    if (func.IsGenerativeConstructor() || func.IsFactory()) {
       // Do not preserve the original implicit constructor, if any.
       orig_implicit_ctor = Function::null();
     }
@@ -3840,7 +3840,7 @@
       return func.raw();
     }
   } else if (kind == kConstructor) {
-    if (func.IsConstructor()) {
+    if (func.IsGenerativeConstructor()) {
       ASSERT(!func.is_static());
       return func.raw();
     }
@@ -5601,7 +5601,7 @@
       ASSERT(IsFactory());
       return 1;  // Type arguments.
     } else {
-      ASSERT(IsConstructor());
+      ASSERT(IsGenerativeConstructor());
       return 2;  // Instance, phase.
     }
   }
@@ -6063,7 +6063,7 @@
 // does not contain an explicit constructor or factory. The implicit
 // constructor has the same token position as the owner class.
 bool Function::IsImplicitConstructor() const {
-  return IsConstructor() && (token_pos() == end_token_pos());
+  return IsGenerativeConstructor() && (token_pos() == end_token_pos());
 }
 
 
@@ -6139,7 +6139,7 @@
 
 
 RawFunction* Function::Clone(const Class& new_owner) const {
-  ASSERT(!IsConstructor());
+  ASSERT(!IsGenerativeConstructor());
   Function& clone = Function::Handle();
   clone ^= Object::Clone(*this, Heap::kOld);
   const Class& origin = Class::Handle(this->origin());
@@ -12173,6 +12173,12 @@
 }
 
 
+void Code::set_inlined_id_to_function(const Array& value) const {
+  ASSERT(value.IsOld());
+  StorePointer(&raw_ptr()->inlined_id_to_function_, value.raw());
+}
+
+
 RawCode* Code::New(intptr_t pointer_offsets_length) {
   if (pointer_offsets_length < 0 || pointer_offsets_length > kMaxElements) {
     // This should be caught before we reach here.
@@ -12526,6 +12532,22 @@
 }
 
 
+intptr_t Code::GetCallerId(intptr_t inlined_id) const {
+  if (inlined_id < 0) return -1;
+  const Array& intervals = Array::Handle(inlined_intervals());
+  Smi& temp_smi = Smi::Handle();
+  for (intptr_t i = 0; i < intervals.Length() - Code::kInlIntNumEntries;
+       i += Code::kInlIntNumEntries) {
+    temp_smi ^= intervals.At(i + Code::kInlIntInliningId);
+    if (temp_smi.Value() == inlined_id) {
+      temp_smi ^= intervals.At(i + Code::kInlIntCallerId);
+      return temp_smi.Value();
+    }
+  }
+  return -1;
+}
+
+
 void Code::GetInlinedFunctionsAt(
     intptr_t offset, GrowableArray<Function*>* fs) const {
   fs->Clear();
@@ -12534,19 +12556,38 @@
     // E.g., for code stubs.
     return;
   }
+  // First find the right interval. TODO(srdjan): use binary search since
+  // intervals are sorted.
   Smi& start = Smi::Handle();
   Smi& end = Smi::Handle();
-  Function& function = Function::Handle();
-  for (intptr_t i = 0; i < intervals.Length(); i += Code::kInlIntNumEntries) {
+  intptr_t found_interval_ix = intervals.Length() - Code::kInlIntNumEntries;
+  for (intptr_t i = 0; i < intervals.Length() - Code::kInlIntNumEntries;
+       i += Code::kInlIntNumEntries) {
     start ^= intervals.At(i + Code::kInlIntStart);
     if (!start.IsNull()) {
-      end ^= intervals.At(i + Code::kInlIntEnd);
+      end ^= intervals.At(i + Code::kInlIntNumEntries + Code::kInlIntStart);
       if ((start.Value() <= offset) && (offset < end.Value())) {
-        function ^= intervals.At(i + Code::kInlIntFunction);
-        fs->Add(&Function::ZoneHandle(function.raw()));
+        found_interval_ix = i;
+        break;
       }
     }
   }
+
+  // Find all functions.
+  const Array& id_map = Array::Handle(inlined_id_to_function());
+  Smi& temp_smi = Smi::Handle();
+  temp_smi ^= intervals.At(found_interval_ix + Code::kInlIntInliningId);
+  intptr_t inlining_id = temp_smi.Value();
+  ASSERT(inlining_id >= 0);
+  temp_smi ^= intervals.At(found_interval_ix + Code::kInlIntCallerId);
+  intptr_t caller_id = temp_smi.Value();
+  while (inlining_id >= 0) {
+    Function& function = Function::ZoneHandle();
+    function  ^= id_map.At(inlining_id);
+    fs->Add(&function);
+    inlining_id = caller_id;
+    caller_id = GetCallerId(inlining_id);
+  }
 }
 
 
@@ -12554,15 +12595,24 @@
   OS::Print("Inlined intervals:\n");
   const Array& intervals = Array::Handle(inlined_intervals());
   Smi& start = Smi::Handle();
-  Smi& end = Smi::Handle();
-  Function& function = Function::Handle();
+  Smi& inlining_id = Smi::Handle();
+  Smi& caller_id = Smi::Handle();
   for (intptr_t i = 0; i < intervals.Length(); i += Code::kInlIntNumEntries) {
     start ^= intervals.At(i + Code::kInlIntStart);
-    if (!start.IsNull()) {
-      end ^= intervals.At(i + Code::kInlIntEnd);
-      function ^= intervals.At(i + Code::kInlIntFunction);
-      OS::Print("%" Pd " .. %" Pd " %s\n",
-          start.Value(), end.Value(), function.ToQualifiedCString());
+    ASSERT(!start.IsNull());
+    if (start.IsNull()) continue;
+    inlining_id ^= intervals.At(i + Code::kInlIntInliningId);
+    caller_id ^= intervals.At(i + Code::kInlIntCallerId);
+    OS::Print("  %" Px " id: %" Pd " caller-id: %" Pd " \n",
+        start.Value(), inlining_id.Value(), caller_id.Value());
+  }
+  OS::Print("Inlined ids:\n");
+  const Array& id_map = Array::Handle(inlined_id_to_function());
+  Function& function = Function::Handle();
+  for (intptr_t i = 0; i < id_map.Length(); i++) {
+    function ^= id_map.At(i);
+    if (!function.IsNull()) {
+      OS::Print("  %" Pd ": %s\n", i, function.ToQualifiedCString());
     }
   }
 }
@@ -17063,8 +17113,6 @@
 
 
 const char* Bigint::ToHexCString(uword (*allocator)(intptr_t size)) const {
-  NoGCScope no_gc;  // TODO(regis): Is this necessary?
-
   const intptr_t used = Used();
   if (used == 0) {
     const char* zero = "0x0";
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 47d6afd..254ec19 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1876,7 +1876,7 @@
 
   static const char* KindToCString(RawFunction::Kind kind);
 
-  bool IsConstructor() const {
+  bool IsGenerativeConstructor() const {
     return (kind() == RawFunction::kConstructor) && !is_static();
   }
   bool IsImplicitConstructor() const;
@@ -4043,9 +4043,9 @@
 
   enum InlinedIntervalEntries {
     kInlIntStart = 0,
-    kInlIntEnd = 1,
-    kInlIntFunction = 2,
-    kInlIntNumEntries = 3
+    kInlIntInliningId = 1,
+    kInlIntCallerId = 2,
+    kInlIntNumEntries = 3,
   };
 
   RawArray* inlined_intervals() const {
@@ -4053,6 +4053,11 @@
   }
   void set_inlined_intervals(const Array& value) const;
 
+  RawArray* inlined_id_to_function() const {
+    return raw_ptr()->inlined_id_to_function_;
+  }
+  void set_inlined_id_to_function(const Array& value) const;
+
   void GetInlinedFunctionsAt(
       intptr_t offset, GrowableArray<Function*>* fs) const;
 
@@ -4233,6 +4238,9 @@
     *PointerOffsetAddrAt(index) = offset_in_instructions;
   }
 
+  // Currently slow, as it searches linearly through inlined_intervals().
+  intptr_t GetCallerId(intptr_t inlined_id) const;
+
   intptr_t BinarySearchInSCallTable(uword pc) const;
   static RawCode* LookupCodeInIsolate(Isolate* isolate, uword pc);
 
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index c9b3345..a1c8aa7 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -2606,7 +2606,7 @@
 
 
 SequenceNode* Parser::MakeImplicitConstructor(const Function& func) {
-  ASSERT(func.IsConstructor());
+  ASSERT(func.IsGenerativeConstructor());
   ASSERT(func.Owner() == current_class().raw());
   const intptr_t ctor_pos = TokenPos();
   OpenFunctionBlock(func);
@@ -2707,7 +2707,7 @@
 SequenceNode* Parser::ParseConstructor(const Function& func,
                                        Array* default_parameter_values) {
   TRACE_PARSER("ParseConstructor");
-  ASSERT(func.IsConstructor());
+  ASSERT(func.IsGenerativeConstructor());
   ASSERT(!func.IsFactory());
   ASSERT(!func.is_static());
   ASSERT(!func.IsLocalFunction());
@@ -2881,7 +2881,7 @@
     if (init_statements->NodeAt(i)->IsStaticCallNode()) {
       StaticCallNode* static_call =
       init_statements->NodeAt(i)->AsStaticCallNode();
-      if (static_call->function().IsConstructor()) {
+      if (static_call->function().IsGenerativeConstructor()) {
         super_call = static_call;
         break;
       }
@@ -3034,14 +3034,14 @@
   parsed_function()->reset_saved_try_ctx_vars();
   LocalScope* saved_async_temp_scope = async_temp_scope_;
 
-  if (func.IsConstructor()) {
+  if (func.IsGenerativeConstructor()) {
     SequenceNode* statements = ParseConstructor(func, default_parameter_values);
     innermost_function_ = saved_innermost_function.raw();
     last_used_try_index_ = saved_try_index;
     return statements;
   }
 
-  ASSERT(!func.IsConstructor());
+  ASSERT(!func.IsGenerativeConstructor());
   OpenFunctionBlock(func);  // Build local scope for function.
 
   ParamList params;
@@ -3171,7 +3171,7 @@
   } else if (func.IsSyncGenClosure()) {
     // The closure containing the body of a sync generator is debuggable.
     ASSERT(func.is_debuggable());
-    // Nothing special to do.
+    async_temp_scope_ = current_block_->scope;
   }
 
   BoolScope allow_await(&this->await_is_keyword_,
@@ -5975,21 +5975,20 @@
   }
 
   ASSERT(try_blocks_list_ != NULL);
-  if (innermost_function().IsAsyncClosure() ||
-      innermost_function().IsAsyncFunction()) {
-    if ((try_blocks_list_->outer_try_block() != NULL) &&
-        (try_blocks_list_->outer_try_block()->try_block()
-            ->scope->function_level() ==
-         current_block_->scope->function_level())) {
-      // We need to unchain three scope levels: catch clause, catch
-      // parameters, and the general try block.
-      RestoreSavedTryContext(
-          current_block_->scope->parent()->parent()->parent(),
-          try_blocks_list_->outer_try_block()->try_index(),
-          current_block_->statements);
-    } else {
-      parsed_function()->reset_saved_try_ctx_vars();
-    }
+  ASSERT(innermost_function().IsAsyncClosure() ||
+         innermost_function().IsAsyncFunction());
+  if ((try_blocks_list_->outer_try_block() != NULL) &&
+      (try_blocks_list_->outer_try_block()->try_block()
+          ->scope->function_level() ==
+       current_block_->scope->function_level())) {
+    // We need to unchain three scope levels: catch clause, catch
+    // parameters, and the general try block.
+    RestoreSavedTryContext(
+        current_block_->scope->parent()->parent()->parent(),
+        try_blocks_list_->outer_try_block()->try_index(),
+        current_block_->statements);
+  } else {
+    parsed_function()->reset_saved_try_ctx_vars();
   }
 
   // Complete the async future with an error.
@@ -6081,7 +6080,9 @@
   PushTryBlock(current_block_);
 
   if (innermost_function().IsAsyncClosure() ||
-      innermost_function().IsAsyncFunction()) {
+      innermost_function().IsAsyncFunction() ||
+      innermost_function().IsSyncGenClosure() ||
+      innermost_function().IsSyncGenerator()) {
     SetupSavedTryContext(context_var);
   }
 }
@@ -6164,10 +6165,7 @@
   OpenFunctionBlock(body);
   AddFormalParamsToScope(&closure_params, current_block_->scope);
   OpenBlock();
-
-  /*
-  async_temp_scope_ = current_block_->scope; // Is this needed?
-  */
+  async_temp_scope_ = current_block_->scope;
   return body.raw();
 }
 
@@ -8196,7 +8194,9 @@
   // outer try block (if it exists).  The current try block has already been
   // removed from the stack of try blocks.
   if ((innermost_function().IsAsyncClosure() ||
-       innermost_function().IsAsyncFunction()) &&
+       innermost_function().IsAsyncFunction() ||
+       innermost_function().IsSyncGenClosure() ||
+       innermost_function().IsSyncGenerator()) &&
       (try_blocks_list_ != NULL)) {
     // We need two unchain two scopes: finally clause, and the try block level.
     RestoreSavedTryContext(current_block_->scope->parent()->parent(),
@@ -8356,7 +8356,9 @@
     // outer try block (if it exists).
     ASSERT(try_blocks_list_ != NULL);
     if (innermost_function().IsAsyncClosure() ||
-        innermost_function().IsAsyncFunction()) {
+        innermost_function().IsAsyncFunction() ||
+        innermost_function().IsSyncGenClosure() ||
+        innermost_function().IsSyncGenerator()) {
       if ((try_blocks_list_->outer_try_block() != NULL) &&
           (try_blocks_list_->outer_try_block()->try_block()
               ->scope->function_level() ==
@@ -8464,7 +8466,9 @@
     // outer try block (if it exists).
     ASSERT(try_blocks_list_ != NULL);
     if (innermost_function().IsAsyncClosure() ||
-        innermost_function().IsAsyncFunction()) {
+        innermost_function().IsAsyncFunction() ||
+        innermost_function().IsSyncGenClosure() ||
+        innermost_function().IsSyncGenerator()) {
       if ((try_blocks_list_->outer_try_block() != NULL) &&
           (try_blocks_list_->outer_try_block()->try_block()
               ->scope->function_level() ==
@@ -8495,6 +8499,7 @@
       Scanner::kNoSourcePos,
       async_saved_try_ctx_name,
       Type::ZoneHandle(Z, Type::DynamicType()));
+  ASSERT(async_temp_scope_ != NULL);
   async_temp_scope_->AddVariable(async_saved_try_ctx);
   async_saved_try_ctx->set_is_captured();
   async_saved_try_ctx = current_block_->scope->LookupVariable(
@@ -8600,7 +8605,9 @@
   ExpectToken(Token::kLBRACE);
 
   if (innermost_function().IsAsyncClosure() ||
-      innermost_function().IsAsyncFunction()) {
+      innermost_function().IsAsyncFunction() ||
+      innermost_function().IsSyncGenClosure() ||
+      innermost_function().IsSyncGenerator()) {
     SetupSavedTryContext(context_var);
   }
 
@@ -8779,7 +8786,7 @@
     ConsumeToken();
     if (CurrentToken() != Token::kSEMICOLON) {
       const intptr_t expr_pos = TokenPos();
-      if (current_function().IsConstructor() &&
+      if (current_function().IsGenerativeConstructor() &&
           (current_block_->scope->function_level() == 0)) {
         ReportError(expr_pos,
                     "return of a value is not allowed in constructors");
@@ -8842,6 +8849,22 @@
         new(Z) LiteralNode(TokenPos(), Bool::True()));
     return_true->set_return_type(ReturnNode::kContinuationTarget);
     yield->AddNode(return_true);
+
+    // If this expression is part of a try block, also append the code for
+    // restoring the saved try context that lives on the stack.
+    const String& async_saved_try_ctx_name =
+        String::Handle(Z, parsed_function()->async_saved_try_ctx_name());
+    if (!async_saved_try_ctx_name.IsNull()) {
+      LocalVariable* async_saved_try_ctx =
+          current_block_->scope->LookupVariable(async_saved_try_ctx_name,
+                                                false);
+      ASSERT(async_saved_try_ctx != NULL);
+      yield->AddNode(new (Z) StoreLocalNode(
+          Scanner::kNoSourcePos,
+          parsed_function()->saved_try_ctx(),
+          new (Z) LoadLocalNode(Scanner::kNoSourcePos, async_saved_try_ctx)));
+    }
+
     statement = yield;
     ExpectSemicolon();
   } else if (token == Token::kIF) {
diff --git a/runtime/vm/profiler.cc b/runtime/vm/profiler.cc
index 335d105..b518cb6 100644
--- a/runtime/vm/profiler.cc
+++ b/runtime/vm/profiler.cc
@@ -173,1373 +173,6 @@
 }
 
 
-class ScopeStopwatch : public ValueObject {
- public:
-  explicit ScopeStopwatch(const char* name) : name_(name) {
-    start_ = FLAG_trace_profiler ? OS::GetCurrentTimeMillis() : 0;
-  }
-
-  int64_t GetElapsed() const {
-    int64_t end = OS::GetCurrentTimeMillis();
-    ASSERT(end >= start_);
-    return end - start_;
-  }
-
-  ~ScopeStopwatch() {
-    if (FLAG_trace_profiler) {
-      int64_t elapsed = GetElapsed();
-      OS::Print("%s took %" Pd64 " millis.\n", name_, elapsed);
-    }
-  }
-
- private:
-  const char* name_;
-  int64_t start_;
-};
-
-
-struct AddressEntry {
-  uword pc;
-  intptr_t exclusive_ticks;
-  intptr_t inclusive_ticks;
-
-  void tick(bool exclusive) {
-    if (exclusive) {
-      exclusive_ticks++;
-    } else {
-      inclusive_ticks++;
-    }
-  }
-};
-
-
-struct CallEntry {
-  intptr_t code_table_index;
-  intptr_t count;
-};
-
-
-typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end);
-
-
-class CodeRegionTrieNode : public ZoneAllocated {
- public:
-  explicit CodeRegionTrieNode(intptr_t code_region_index)
-      : code_region_index_(code_region_index),
-        count_(0),
-        children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) {
-  }
-
-  void Tick() {
-    ASSERT(code_region_index_ >= 0);
-    count_++;
-  }
-
-  intptr_t count() const {
-    ASSERT(code_region_index_ >= 0);
-    return count_;
-  }
-
-  intptr_t code_region_index() const {
-    return code_region_index_;
-  }
-
-  ZoneGrowableArray<CodeRegionTrieNode*>& children() const {
-    return *children_;
-  }
-
-  CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) {
-    const intptr_t length = children_->length();
-    intptr_t i = 0;
-    while (i < length) {
-      CodeRegionTrieNode* child = (*children_)[i];
-      if (child->code_region_index() == child_code_region_index) {
-        return child;
-      }
-      if (child->code_region_index() > child_code_region_index) {
-        break;
-      }
-      i++;
-    }
-    // Add new CodeRegion, sorted by CodeRegionTable index.
-    CodeRegionTrieNode* child = new CodeRegionTrieNode(child_code_region_index);
-    if (i < length) {
-      // Insert at i.
-      children_->InsertAt(i, child);
-    } else {
-      // Add to end.
-      children_->Add(child);
-    }
-    return child;
-  }
-
-  // Sort this's children and (recursively) all descendants by count.
-  // This should only be called after the trie is completely built.
-  void SortByCount() {
-    children_->Sort(CodeRegionTrieNodeCompare);
-    ZoneGrowableArray<CodeRegionTrieNode*>& kids = children();
-    intptr_t child_count = kids.length();
-    // Recurse.
-    for (intptr_t i = 0; i < child_count; i++) {
-      kids[i]->SortByCount();
-    }
-  }
-
-  void PrintToJSONArray(JSONArray* array) const {
-    ASSERT(array != NULL);
-    // Write CodeRegion index.
-    array->AddValue(code_region_index_);
-    // Write count.
-    array->AddValue(count_);
-    // Write number of children.
-    ZoneGrowableArray<CodeRegionTrieNode*>& kids = children();
-    intptr_t child_count = kids.length();
-    array->AddValue(child_count);
-    // Recurse.
-    for (intptr_t i = 0; i < child_count; i++) {
-      kids[i]->PrintToJSONArray(array);
-    }
-  }
-
- private:
-  static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a,
-                                       CodeRegionTrieNode* const* b) {
-    ASSERT(a != NULL);
-    ASSERT(b != NULL);
-    return (*b)->count() - (*a)->count();
-  }
-
-  const intptr_t code_region_index_;
-  intptr_t count_;
-  ZoneGrowableArray<CodeRegionTrieNode*>* children_;
-};
-
-
-// A contiguous address region that holds code. Each CodeRegion has a "kind"
-// which describes the type of code contained inside the region. Each
-// region covers the following interval: [start, end).
-class CodeRegion : public ZoneAllocated {
- public:
-  enum Kind {
-    kDartCode,       // Live Dart code.
-    kCollectedCode,  // Dead Dart code.
-    kNativeCode,     // Native code.
-    kReusedCode,     // Dead Dart code that has been reused by new kDartCode.
-    kTagCode,        // A special kind of code representing a tag.
-  };
-
-  CodeRegion(Kind kind, uword start, uword end, int64_t timestamp)
-      : kind_(kind),
-        start_(start),
-        end_(end),
-        inclusive_ticks_(0),
-        exclusive_ticks_(0),
-        inclusive_tick_serial_(0),
-        name_(NULL),
-        compile_timestamp_(timestamp),
-        creation_serial_(0),
-        address_table_(new ZoneGrowableArray<AddressEntry>()),
-        callers_table_(new ZoneGrowableArray<CallEntry>()),
-        callees_table_(new ZoneGrowableArray<CallEntry>()) {
-    ASSERT(start_ < end_);
-  }
-
-
-  uword start() const { return start_; }
-  void set_start(uword start) {
-    start_ = start;
-  }
-
-  uword end() const { return end_; }
-  void set_end(uword end) {
-    end_ = end;
-  }
-
-  void AdjustExtent(uword start, uword end) {
-    if (start < start_) {
-      start_ = start;
-    }
-    if (end > end_) {
-      end_ = end;
-    }
-    ASSERT(start_ < end_);
-  }
-
-  bool contains(uword pc) const {
-    return (pc >= start_) && (pc < end_);
-  }
-
-  bool overlaps(const CodeRegion* other) const {
-    ASSERT(other != NULL);
-    return other->contains(start_)   ||
-           other->contains(end_ - 1) ||
-           contains(other->start())  ||
-           contains(other->end() - 1);
-  }
-
-  intptr_t creation_serial() const { return creation_serial_; }
-  void set_creation_serial(intptr_t serial) {
-    creation_serial_ = serial;
-  }
-  int64_t compile_timestamp() const { return compile_timestamp_; }
-  void set_compile_timestamp(int64_t timestamp) {
-    compile_timestamp_ = timestamp;
-  }
-
-  intptr_t inclusive_ticks() const { return inclusive_ticks_; }
-  void set_inclusive_ticks(intptr_t inclusive_ticks) {
-    inclusive_ticks_ = inclusive_ticks;
-  }
-
-  intptr_t exclusive_ticks() const { return exclusive_ticks_; }
-  void set_exclusive_ticks(intptr_t exclusive_ticks) {
-    exclusive_ticks_ = exclusive_ticks;
-  }
-
-  const char* name() const { return name_; }
-  void SetName(const char* name) {
-    if (name == NULL) {
-      name_ = NULL;
-    }
-    intptr_t len = strlen(name);
-    name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1);
-    strncpy(const_cast<char*>(name_), name, len);
-    const_cast<char*>(name_)[len] = '\0';
-  }
-
-  Kind kind() const { return kind_; }
-
-  static const char* KindToCString(Kind kind) {
-    switch (kind) {
-      case kDartCode:
-        return "Dart";
-      case kCollectedCode:
-        return "Collected";
-      case kNativeCode:
-        return "Native";
-      case kReusedCode:
-        return "Overwritten";
-      case kTagCode:
-        return "Tag";
-    }
-    UNREACHABLE();
-    return NULL;
-  }
-
-  void DebugPrint() const {
-    OS::Print("%s [%" Px ", %" Px ") %" Pd " %" Pd64 "\n",
-              KindToCString(kind_),
-              start(),
-              end(),
-              creation_serial_,
-              compile_timestamp_);
-  }
-
-  void Tick(uword pc, bool exclusive, intptr_t serial) {
-    // Assert that exclusive ticks are never passed a valid serial number.
-    ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1)));
-    if (!exclusive && (inclusive_tick_serial_ == serial)) {
-      // We've already given this code object an inclusive tick for this sample.
-      return;
-    }
-    // Tick the code object.
-    if (exclusive) {
-      exclusive_ticks_++;
-    } else {
-      inclusive_ticks_++;
-      // Mark the last serial we ticked the inclusive count.
-      inclusive_tick_serial_ = serial;
-    }
-    TickAddress(pc, exclusive);
-  }
-
-  void AddCaller(intptr_t index, intptr_t count) {
-    AddCallEntry(callers_table_, index, count);
-  }
-
-  void AddCallee(intptr_t index, intptr_t count) {
-    AddCallEntry(callees_table_, index, count);
-  }
-
-  void PrintNativeCode(JSONObject* profile_code_obj) {
-    ASSERT(kind() == kNativeCode);
-    JSONObject obj(profile_code_obj, "code");
-    obj.AddProperty("type", "@Code");
-    obj.AddProperty("kind", "Native");
-    obj.AddProperty("name", name());
-    obj.AddPropertyF("start", "%" Px "", start());
-    obj.AddPropertyF("end", "%" Px "", end());
-    obj.AddPropertyF("id", "code/native-%" Px "", start());
-    {
-      // Generate a fake function entry.
-      JSONObject func(&obj, "function");
-      func.AddProperty("type", "@Function");
-      func.AddPropertyF("id", "functions/native-%" Px "", start());
-      func.AddProperty("name", name());
-      func.AddProperty("kind", "Native");
-    }
-  }
-
-  void PrintCollectedCode(JSONObject* profile_code_obj) {
-    ASSERT(kind() == kCollectedCode);
-    JSONObject obj(profile_code_obj, "code");
-    obj.AddProperty("type", "@Code");
-    obj.AddProperty("kind", "Collected");
-    obj.AddProperty("name", name());
-    obj.AddPropertyF("start", "%" Px "", start());
-    obj.AddPropertyF("end", "%" Px "", end());
-    obj.AddPropertyF("id", "code/collected-%" Px "", start());
-    {
-      // Generate a fake function entry.
-      JSONObject func(&obj, "function");
-      func.AddProperty("type", "@Function");
-      obj.AddPropertyF("id", "functions/collected-%" Px "", start());
-      func.AddProperty("name", name());
-      func.AddProperty("kind", "Collected");
-    }
-  }
-
-  void PrintOverwrittenCode(JSONObject* profile_code_obj) {
-    ASSERT(kind() == kReusedCode);
-    JSONObject obj(profile_code_obj, "code");
-    obj.AddProperty("type", "@Code");
-    obj.AddProperty("kind", "Reused");
-    obj.AddProperty("name", name());
-    obj.AddPropertyF("start", "%" Px "", start());
-    obj.AddPropertyF("end", "%" Px "", end());
-    obj.AddPropertyF("id", "code/reused-%" Px "", start());
-    {
-      // Generate a fake function entry.
-      JSONObject func(&obj, "function");
-      func.AddProperty("type", "@Function");
-      obj.AddPropertyF("id", "functions/reused-%" Px "", start());
-      func.AddProperty("name", name());
-      func.AddProperty("kind", "Reused");
-    }
-  }
-
-  void  PrintTagCode(JSONObject* profile_code_obj) {
-    ASSERT(kind() == kTagCode);
-    JSONObject obj(profile_code_obj, "code");
-    obj.AddProperty("type", "@Code");
-    obj.AddProperty("kind", "Tag");
-    obj.AddPropertyF("id", "code/tag-%" Px "", start());
-    obj.AddProperty("name", name());
-    obj.AddPropertyF("start", "%" Px "", start());
-    obj.AddPropertyF("end", "%" Px "", end());
-    {
-      // Generate a fake function entry.
-      JSONObject func(&obj, "function");
-      func.AddProperty("type", "@Function");
-      func.AddProperty("kind", "Tag");
-      obj.AddPropertyF("id", "functions/tag-%" Px "", start());
-      func.AddProperty("name", name());
-    }
-  }
-
-  void PrintToJSONArray(Isolate* isolate, JSONArray* events, bool full) {
-    JSONObject obj(events);
-    obj.AddProperty("kind", KindToCString(kind()));
-    obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks());
-    obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks());
-    if (kind() == kDartCode) {
-      // Look up code in Dart heap.
-      Code& code = Code::Handle(isolate);
-      code ^= Code::LookupCode(start());
-      if (code.IsNull()) {
-        // Code is a stub in the Vm isolate.
-        code ^= Code::LookupCodeInVmIsolate(start());
-      }
-      ASSERT(!code.IsNull());
-      obj.AddProperty("code", code, !full);
-    } else if (kind() == kCollectedCode) {
-      if (name() == NULL) {
-        // Lazily set generated name.
-        GenerateAndSetSymbolName("[Collected]");
-      }
-      PrintCollectedCode(&obj);
-    } else if (kind() == kReusedCode) {
-      if (name() == NULL) {
-        // Lazily set generated name.
-        GenerateAndSetSymbolName("[Reused]");
-      }
-      PrintOverwrittenCode(&obj);
-    } else if (kind() == kTagCode) {
-      if (name() == NULL) {
-        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 {
-      ASSERT(kind() == kNativeCode);
-      if (name() == NULL) {
-        // Lazily set generated name.
-        GenerateAndSetSymbolName("[Native]");
-      }
-      PrintNativeCode(&obj);
-    }
-    {
-      JSONArray ticks(&obj, "ticks");
-      for (intptr_t i = 0; i < address_table_->length(); i++) {
-        const AddressEntry& entry = (*address_table_)[i];
-        ticks.AddValueF("%" Px "", entry.pc);
-        ticks.AddValueF("%" Pd "", entry.exclusive_ticks);
-        ticks.AddValueF("%" Pd "", entry.inclusive_ticks);
-      }
-    }
-    {
-      JSONArray callers(&obj, "callers");
-      for (intptr_t i = 0; i < callers_table_->length(); i++) {
-        const CallEntry& entry = (*callers_table_)[i];
-        callers.AddValueF("%" Pd "", entry.code_table_index);
-        callers.AddValueF("%" Pd "", entry.count);
-      }
-    }
-    {
-      JSONArray callees(&obj, "callees");
-      for (intptr_t i = 0; i < callees_table_->length(); i++) {
-        const CallEntry& entry = (*callees_table_)[i];
-        callees.AddValueF("%" Pd "", entry.code_table_index);
-        callees.AddValueF("%" Pd "", entry.count);
-      }
-    }
-  }
-
- private:
-  void TickAddress(uword pc, bool exclusive) {
-    const intptr_t length = address_table_->length();
-    intptr_t i = 0;
-    for (; i < length; i++) {
-      AddressEntry& entry = (*address_table_)[i];
-      if (entry.pc == pc) {
-        // Tick the address entry.
-        entry.tick(exclusive);
-        return;
-      }
-      if (entry.pc > pc) {
-        break;
-      }
-    }
-    // New address, add entry.
-    AddressEntry entry;
-    entry.pc = pc;
-    entry.exclusive_ticks = 0;
-    entry.inclusive_ticks = 0;
-    entry.tick(exclusive);
-    if (i < length) {
-      // Insert at i.
-      address_table_->InsertAt(i, entry);
-    } else {
-      // Add to end.
-      address_table_->Add(entry);
-    }
-  }
-
-
-  void AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index,
-                    intptr_t count) {
-    const intptr_t length = table->length();
-    intptr_t i = 0;
-    for (; i < length; i++) {
-      CallEntry& entry = (*table)[i];
-      if (entry.code_table_index == index) {
-        entry.count += count;
-        return;
-      }
-      if (entry.code_table_index > index) {
-        break;
-      }
-    }
-    CallEntry entry;
-    entry.code_table_index = index;
-    entry.count = count;
-    if (i < length) {
-      table->InsertAt(i, entry);
-    } else {
-      table->Add(entry);
-    }
-  }
-
-  void GenerateAndSetSymbolName(const char* prefix) {
-    const intptr_t kBuffSize = 512;
-    char buff[kBuffSize];
-    OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")",
-                prefix, start(), end());
-    SetName(buff);
-  }
-
-  // CodeRegion kind.
-  const Kind kind_;
-  // CodeRegion start address.
-  uword start_;
-  // CodeRegion end address.
-  uword end_;
-  // Inclusive ticks.
-  intptr_t inclusive_ticks_;
-  // Exclusive ticks.
-  intptr_t exclusive_ticks_;
-  // Inclusive tick serial number, ensures that each CodeRegion is only given
-  // a single inclusive tick per sample.
-  intptr_t inclusive_tick_serial_;
-  // Name of code region.
-  const char* name_;
-  // The compilation timestamp associated with this code region.
-  int64_t compile_timestamp_;
-  // Serial number at which this CodeRegion was created.
-  intptr_t creation_serial_;
-  ZoneGrowableArray<AddressEntry>* address_table_;
-  ZoneGrowableArray<CallEntry>* callers_table_;
-  ZoneGrowableArray<CallEntry>* callees_table_;
-  DISALLOW_COPY_AND_ASSIGN(CodeRegion);
-};
-
-
-// A sorted table of CodeRegions. Does not allow for overlap.
-class CodeRegionTable : public ValueObject {
- public:
-  enum TickResult {
-    kTicked = 0,     // CodeRegion found and ticked.
-    kNotFound = -1,   // No CodeRegion found.
-    kNewerCode = -2,  // CodeRegion found but it was compiled after sample.
-  };
-
-  CodeRegionTable() :
-      code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) {
-  }
-
-  // Ticks the CodeRegion containing pc if it is alive at timestamp.
-  TickResult Tick(uword pc, bool exclusive, intptr_t serial,
-                  int64_t timestamp) {
-    intptr_t index = FindIndex(pc);
-    if (index < 0) {
-      // Not found.
-      return kNotFound;
-    }
-    ASSERT(index < code_region_table_->length());
-    CodeRegion* region = At(index);
-    if (region->compile_timestamp() > timestamp) {
-      // Compiled after tick.
-      return kNewerCode;
-    }
-    region->Tick(pc, exclusive, serial);
-    return kTicked;
-  }
-
-  // Table length.
-  intptr_t Length() const { return code_region_table_->length(); }
-
-  // Get the CodeRegion at index.
-  CodeRegion* At(intptr_t index) const {
-    return (*code_region_table_)[index];
-  }
-
-  // Find the table index to the CodeRegion containing pc.
-  // Returns < 0 if not found.
-  intptr_t FindIndex(uword pc) const {
-    intptr_t index = FindRegionIndex(pc, &CompareLowerBound);
-    const CodeRegion* code_region = NULL;
-    if (index == code_region_table_->length()) {
-      // Not present.
-      return -1;
-    }
-    code_region = At(index);
-    if (code_region->contains(pc)) {
-      // Found at index.
-      return index;
-    }
-    return -2;
-  }
-
-  // Insert code_region into the table. Returns the table index where the
-  // CodeRegion was inserted. Will merge with an overlapping CodeRegion if
-  // one is present.
-  intptr_t InsertCodeRegion(CodeRegion* code_region) {
-    const uword start = code_region->start();
-    const uword end = code_region->end();
-    const intptr_t length = code_region_table_->length();
-    if (length == 0) {
-      code_region_table_->Add(code_region);
-      return length;
-    }
-    // Determine the correct place to insert or merge code_region into table.
-    intptr_t lo = FindRegionIndex(start, &CompareLowerBound);
-    intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound);
-    // TODO(johnmccutchan): Simplify below logic.
-    if ((lo == length) && (hi == length)) {
-      lo = length - 1;
-    }
-    if (lo == length) {
-      CodeRegion* region = At(hi);
-      if (region->overlaps(code_region)) {
-        HandleOverlap(region, code_region, start, end);
-        return hi;
-      }
-      code_region_table_->Add(code_region);
-      return length;
-    } else if (hi == length) {
-      CodeRegion* region = At(lo);
-      if (region->overlaps(code_region)) {
-        HandleOverlap(region, code_region, start, end);
-        return lo;
-      }
-      code_region_table_->Add(code_region);
-      return length;
-    } else if (lo == hi) {
-      CodeRegion* region = At(lo);
-      if (region->overlaps(code_region)) {
-        HandleOverlap(region, code_region, start, end);
-        return lo;
-      }
-      code_region_table_->InsertAt(lo, code_region);
-      return lo;
-    } else {
-      CodeRegion* region = At(lo);
-      if (region->overlaps(code_region)) {
-        HandleOverlap(region, code_region, start, end);
-        return lo;
-      }
-      region = At(hi);
-      if (region->overlaps(code_region)) {
-        HandleOverlap(region, code_region, start, end);
-        return hi;
-      }
-      code_region_table_->InsertAt(hi, code_region);
-      return hi;
-    }
-    UNREACHABLE();
-  }
-
-#if defined(DEBUG)
-  void Verify() {
-    VerifyOrder();
-    VerifyOverlap();
-  }
-#endif
-
-  void DebugPrint() {
-    OS::Print("Dumping CodeRegionTable:\n");
-    for (intptr_t i = 0; i < code_region_table_->length(); i++) {
-      CodeRegion* region = At(i);
-      region->DebugPrint();
-    }
-  }
-
- private:
-  intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const {
-    ASSERT(comparator != NULL);
-    intptr_t count = code_region_table_->length();
-    intptr_t first = 0;
-    while (count > 0) {
-      intptr_t it = first;
-      intptr_t step = count / 2;
-      it += step;
-      const CodeRegion* code_region = At(it);
-      if (comparator(pc, code_region->start(), code_region->end())) {
-        first = ++it;
-        count -= (step + 1);
-      } else {
-        count = step;
-      }
-    }
-    return first;
-  }
-
-  static bool CompareUpperBound(uword pc, uword start, uword end) {
-    return pc >= end;
-  }
-
-  static bool CompareLowerBound(uword pc, uword start, uword end) {
-    return end <= pc;
-  }
-
-  void HandleOverlap(CodeRegion* region, CodeRegion* code_region,
-                     uword start, uword end) {
-    // We should never see overlapping Dart code regions.
-    ASSERT(region->kind() != CodeRegion::kDartCode);
-    // We should never see overlapping Tag code regions.
-    ASSERT(region->kind() != CodeRegion::kTagCode);
-    // When code regions overlap, they should be of the same kind.
-    ASSERT(region->kind() == code_region->kind());
-    region->AdjustExtent(start, end);
-  }
-
-#if defined(DEBUG)
-  void VerifyOrder() {
-    const intptr_t length = code_region_table_->length();
-    if (length == 0) {
-      return;
-    }
-    uword last = (*code_region_table_)[0]->end();
-    for (intptr_t i = 1; i < length; i++) {
-      CodeRegion* a = (*code_region_table_)[i];
-      ASSERT(last <= a->start());
-      last = a->end();
-    }
-  }
-
-  void VerifyOverlap() {
-    const intptr_t length = code_region_table_->length();
-    for (intptr_t i = 0; i < length; i++) {
-      CodeRegion* a = (*code_region_table_)[i];
-      for (intptr_t j = i+1; j < length; j++) {
-        CodeRegion* b = (*code_region_table_)[j];
-        ASSERT(!a->contains(b->start()) &&
-               !a->contains(b->end() - 1) &&
-               !b->contains(a->start()) &&
-               !b->contains(a->end() - 1));
-      }
-    }
-  }
-#endif
-
-  ZoneGrowableArray<CodeRegion*>* code_region_table_;
-};
-
-
-class FixTopFrameVisitor : public SampleVisitor {
- public:
-  explicit FixTopFrameVisitor(Isolate* isolate)
-      : SampleVisitor(isolate),
-        vm_isolate_(Dart::vm_isolate()) {
-  }
-
-  void VisitSample(Sample* sample) {
-    if (sample->processed()) {
-      // Already processed.
-      return;
-    }
-    REUSABLE_CODE_HANDLESCOPE(isolate());
-    // Mark that we've processed this sample.
-    sample->set_processed(true);
-    // Lookup code object for leaf frame.
-    Code& code = reused_code_handle.Handle();
-    code = FindCodeForPC(sample->At(0));
-    sample->set_leaf_frame_is_dart(!code.IsNull());
-    if (sample->pc_marker() == 0) {
-      // No pc marker. Nothing to do.
-      return;
-    }
-    if (!code.IsNull() && (code.compile_timestamp() > sample->timestamp())) {
-      // Code compiled after sample. Ignore.
-      return;
-    }
-    if (sample->leaf_frame_is_dart()) {
-      CheckForMissingDartFrame(code, sample);
-    }
-  }
-
- private:
-  void CheckForMissingDartFrame(const Code& code, Sample* sample) const {
-    // Some stubs (and intrinsics) do not push a frame onto the stack leaving
-    // the frame pointer in the caller.
-    //
-    // PC -> STUB
-    // FP -> DART3  <-+
-    //       DART2  <-|  <- TOP FRAME RETURN ADDRESS.
-    //       DART1  <-|
-    //       .....
-    //
-    // In this case, traversing the linked stack frames will not collect a PC
-    // inside DART3. The stack will incorrectly be: STUB, DART2, DART1.
-    // In Dart code, after pushing the FP onto the stack, an IP in the current
-    // function is pushed onto the stack as well. This stack slot is called
-    // the PC marker. We can use the PC marker to insert DART3 into the stack
-    // so that it will correctly be: STUB, DART3, DART2, DART1. Note the
-    // inserted PC may not accurately reflect the true return address from STUB.
-    ASSERT(!code.IsNull());
-    if (sample->sp() == sample->fp()) {
-      // Haven't pushed pc marker yet.
-      return;
-    }
-    uword pc_marker = sample->pc_marker();
-    if (code.ContainsInstructionAt(pc_marker)) {
-      // PC marker is in the same code as pc, no missing frame.
-      return;
-    }
-    if (!ContainedInDartCodeHeaps(pc_marker)) {
-      // Not a valid PC marker.
-      return;
-    }
-    sample->InsertCallerForTopFrame(pc_marker);
-  }
-
-  bool ContainedInDartCodeHeaps(uword pc) const {
-    return isolate()->heap()->CodeContains(pc) ||
-           vm_isolate()->heap()->CodeContains(pc);
-  }
-
-  Isolate* vm_isolate() const {
-    return vm_isolate_;
-  }
-
-  RawCode* FindCodeForPC(uword pc) const {
-    // Check current isolate for pc.
-    if (isolate()->heap()->CodeContains(pc)) {
-      return Code::LookupCode(pc);
-    }
-    // Check VM isolate for pc.
-    if (vm_isolate()->heap()->CodeContains(pc)) {
-      return Code::LookupCodeInVmIsolate(pc);
-    }
-    return Code::null();
-  }
-
-  Isolate* vm_isolate_;
-};
-
-
-class CodeRegionTableBuilder : public SampleVisitor {
- public:
-  CodeRegionTableBuilder(Isolate* isolate,
-                         CodeRegionTable* live_code_table,
-                         CodeRegionTable* dead_code_table,
-                         CodeRegionTable* tag_code_table)
-      : SampleVisitor(isolate),
-        live_code_table_(live_code_table),
-        dead_code_table_(dead_code_table),
-        tag_code_table_(tag_code_table),
-        isolate_(isolate),
-        vm_isolate_(Dart::vm_isolate()) {
-    ASSERT(live_code_table_ != NULL);
-    ASSERT(dead_code_table_ != NULL);
-    ASSERT(tag_code_table_ != NULL);
-    frames_ = 0;
-    min_time_ = kMaxInt64;
-    max_time_ = 0;
-    ASSERT(isolate_ != NULL);
-    ASSERT(vm_isolate_ != NULL);
-  }
-
-  void VisitSample(Sample* sample) {
-    int64_t timestamp = sample->timestamp();
-    if (timestamp > max_time_) {
-      max_time_ = timestamp;
-    }
-    if (timestamp < min_time_) {
-      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 if we aren't sampled from an exit frame.
-    if (!sample->exit_frame_sample()) {
-      Tick(sample->At(0), true, timestamp);
-    }
-    // Inclusive tick for all frames.
-    for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
-      if (sample->At(i) == 0) {
-        break;
-      }
-      frames_++;
-      Tick(sample->At(i), false, timestamp);
-    }
-  }
-
-  intptr_t frames() const { return frames_; }
-
-  intptr_t  TimeDeltaMicros() const {
-    return static_cast<intptr_t>(max_time_ - min_time_);
-  }
-  int64_t  max_time() const { return max_time_; }
-
- private:
-  void CreateTag(uword tag) {
-    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 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();
-    r = live_code_table_->Tick(pc, exclusive, serial, timestamp);
-    if (r == CodeRegionTable::kTicked) {
-      // Live code found and ticked.
-      return;
-    }
-    if (r == CodeRegionTable::kNewerCode) {
-      // Code has been overwritten by newer code.
-      // Update shadow table of dead code regions.
-      r = dead_code_table_->Tick(pc, exclusive, serial, timestamp);
-      ASSERT(r != CodeRegionTable::kNewerCode);
-      if (r == CodeRegionTable::kTicked) {
-        // Dead code found and ticked.
-        return;
-      }
-      ASSERT(r == CodeRegionTable::kNotFound);
-      CreateAndTickDeadCodeRegion(pc, exclusive, serial);
-      return;
-    }
-    // Create new live CodeRegion.
-    ASSERT(r == CodeRegionTable::kNotFound);
-    CodeRegion* region = CreateCodeRegion(pc);
-    region->set_creation_serial(visited());
-    intptr_t index = live_code_table_->InsertCodeRegion(region);
-    ASSERT(index >= 0);
-    region = live_code_table_->At(index);
-    if (region->compile_timestamp() <= timestamp) {
-      region->Tick(pc, exclusive, serial);
-      return;
-    }
-    // We have created a new code region but it's for a CodeRegion
-    // compiled after the sample.
-    ASSERT(region->kind() == CodeRegion::kDartCode);
-    CreateAndTickDeadCodeRegion(pc, exclusive, serial);
-  }
-
-  void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) {
-    // Need to create dead code.
-    CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode,
-                                        pc,
-                                        pc + 1,
-                                        0);
-    intptr_t index = dead_code_table_->InsertCodeRegion(region);
-    region->set_creation_serial(visited());
-    ASSERT(index >= 0);
-    dead_code_table_->At(index)->Tick(pc, exclusive, serial);
-  }
-
-  CodeRegion* CreateCodeRegion(uword pc) {
-    const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment();
-    const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1);
-    Code& code = Code::Handle(isolate_);
-    // Check current isolate for pc.
-    if (isolate_->heap()->CodeContains(pc)) {
-      code ^= Code::LookupCode(pc);
-      if (!code.IsNull()) {
-        return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
-                              code.EntryPoint() + code.Size(),
-                              code.compile_timestamp());
-      }
-      return new CodeRegion(CodeRegion::kCollectedCode, pc,
-                            (pc & kDartCodeAlignmentMask) + kDartCodeAlignment,
-                            0);
-    }
-    // Check VM isolate for pc.
-    if (vm_isolate_->heap()->CodeContains(pc)) {
-      code ^= Code::LookupCodeInVmIsolate(pc);
-      if (!code.IsNull()) {
-        return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
-                              code.EntryPoint() + code.Size(),
-                              code.compile_timestamp());
-      }
-      return new CodeRegion(CodeRegion::kCollectedCode, pc,
-                            (pc & kDartCodeAlignmentMask) + kDartCodeAlignment,
-                            0);
-    }
-    // Check NativeSymbolResolver for pc.
-    uintptr_t native_start = 0;
-    char* native_name = NativeSymbolResolver::LookupSymbolName(pc,
-                                                               &native_start);
-    if (native_name == NULL) {
-      // No native name found.
-      return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1, 0);
-    }
-    ASSERT(pc >= native_start);
-    CodeRegion* code_region =
-        new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1, 0);
-    code_region->SetName(native_name);
-    free(native_name);
-    return code_region;
-  }
-
-  intptr_t frames_;
-  int64_t min_time_;
-  int64_t max_time_;
-  CodeRegionTable* live_code_table_;
-  CodeRegionTable* dead_code_table_;
-  CodeRegionTable* tag_code_table_;
-  Isolate* isolate_;
-  Isolate* vm_isolate_;
-};
-
-
-class CodeRegionExclusiveTrieBuilder : public SampleVisitor {
- public:
-  CodeRegionExclusiveTrieBuilder(Isolate* isolate,
-                                 CodeRegionTable* live_code_table,
-                                 CodeRegionTable* dead_code_table,
-                                 CodeRegionTable* tag_code_table)
-      : SampleVisitor(isolate),
-        live_code_table_(live_code_table),
-        dead_code_table_(dead_code_table),
-        tag_code_table_(tag_code_table) {
-    ASSERT(live_code_table_ != NULL);
-    ASSERT(dead_code_table_ != NULL);
-    ASSERT(tag_code_table_ != NULL);
-    dead_code_table_offset_ = live_code_table_->Length();
-    tag_code_table_offset_ = dead_code_table_offset_ +
-                             dead_code_table_->Length();
-    intptr_t root_index = tag_code_table_->FindIndex(0);
-    // Verify that the "0" tag does not exist.
-    ASSERT(root_index < 0);
-    // Insert the dummy tag CodeRegion that is used for the Trie root.
-    CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, 0, 1, 0);
-    root_index = tag_code_table_->InsertCodeRegion(region);
-    ASSERT(root_index >= 0);
-    region->set_creation_serial(0);
-    root_ = new CodeRegionTrieNode(tag_code_table_offset_ + root_index);
-    set_tag_order(Profiler::kUserVM);
-  }
-
-  void VisitSample(Sample* sample) {
-    // Give the root a tick.
-    root_->Tick();
-    CodeRegionTrieNode* current = root_;
-    current = ProcessTags(sample, current);
-    // Walk the sampled PCs.
-    for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
-      if (sample->At(i) == 0) {
-        break;
-      }
-      intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp());
-      current = current->GetChild(index);
-      current->Tick();
-    }
-  }
-
-  CodeRegionTrieNode* root() const {
-    return root_;
-  }
-
-  Profiler::TagOrder tag_order() const {
-    return tag_order_;
-  }
-
-  void set_tag_order(Profiler::TagOrder tag_order) {
-    tag_order_ = tag_order;
-  }
-
- private:
-  CodeRegionTrieNode* ProcessUserTags(Sample* sample,
-                                      CodeRegionTrieNode* current) {
-    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();
-    }
-    return current;
-  }
-
-  CodeRegionTrieNode* ProcessVMTags(Sample* sample,
-                                    CodeRegionTrieNode* current) {
-    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.
-    current->Tick();
-    return current;
-  }
-
-  CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) {
-    // None.
-    if (tag_order() == Profiler::kNoTags) {
-      return current;
-    }
-    // User first.
-    if ((tag_order() == Profiler::kUserVM) ||
-        (tag_order() == Profiler::kUser)) {
-      current = ProcessUserTags(sample, current);
-      // Only user.
-      if (tag_order() == Profiler::kUser) {
-        return current;
-      }
-      return ProcessVMTags(sample, current);
-    }
-    // VM first.
-    ASSERT((tag_order() == Profiler::kVMUser) ||
-           (tag_order() == Profiler::kVM));
-    current = ProcessVMTags(sample, current);
-    // Only VM.
-    if (tag_order() == Profiler::kVM) {
-      return current;
-    }
-    return ProcessUserTags(sample, current);
-  }
-
-  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;
-  }
-
-  intptr_t FindFinalIndex(uword pc, int64_t timestamp) const {
-    intptr_t index = live_code_table_->FindIndex(pc);
-    ASSERT(index >= 0);
-    CodeRegion* region = live_code_table_->At(index);
-    ASSERT(region->contains(pc));
-    if (region->compile_timestamp() > timestamp) {
-      // Overwritten code, find in dead code table.
-      index = dead_code_table_->FindIndex(pc);
-      ASSERT(index >= 0);
-      region = dead_code_table_->At(index);
-      ASSERT(region->contains(pc));
-      ASSERT(region->compile_timestamp() <= timestamp);
-      return index + dead_code_table_offset_;
-    }
-    ASSERT(region->compile_timestamp() <= timestamp);
-    return index;
-  }
-
-  Profiler::TagOrder tag_order_;
-  CodeRegionTrieNode* root_;
-  CodeRegionTable* live_code_table_;
-  CodeRegionTable* dead_code_table_;
-  CodeRegionTable* tag_code_table_;
-  intptr_t dead_code_table_offset_;
-  intptr_t tag_code_table_offset_;
-};
-
-
-class CodeRegionTableCallersBuilder {
- public:
-  CodeRegionTableCallersBuilder(CodeRegionTrieNode* exclusive_root,
-                                CodeRegionTable* live_code_table,
-                                CodeRegionTable* dead_code_table,
-                                CodeRegionTable* tag_code_table)
-      : exclusive_root_(exclusive_root),
-        live_code_table_(live_code_table),
-        dead_code_table_(dead_code_table),
-        tag_code_table_(tag_code_table) {
-    ASSERT(exclusive_root_ != NULL);
-    ASSERT(live_code_table_ != NULL);
-    ASSERT(dead_code_table_ != NULL);
-    ASSERT(tag_code_table_ != NULL);
-    dead_code_table_offset_ = live_code_table_->Length();
-    tag_code_table_offset_ = dead_code_table_offset_ +
-                             dead_code_table_->Length();
-  }
-
-  void Build() {
-    ProcessNode(exclusive_root_);
-  }
-
- private:
-  void ProcessNode(CodeRegionTrieNode* parent) {
-    const ZoneGrowableArray<CodeRegionTrieNode*>& children = parent->children();
-    intptr_t parent_index = parent->code_region_index();
-    ASSERT(parent_index >= 0);
-    CodeRegion* parent_region = At(parent_index);
-    ASSERT(parent_region != NULL);
-    for (intptr_t i = 0; i < children.length(); i++) {
-      CodeRegionTrieNode* node = children[i];
-      ProcessNode(node);
-      intptr_t index = node->code_region_index();
-      ASSERT(index >= 0);
-      CodeRegion* region = At(index);
-      ASSERT(region != NULL);
-      region->AddCallee(parent_index, node->count());
-      parent_region->AddCaller(index, node->count());
-    }
-  }
-
-  CodeRegion* At(intptr_t final_index) {
-    ASSERT(final_index >= 0);
-    if (final_index < dead_code_table_offset_) {
-      return live_code_table_->At(final_index);
-    } else if (final_index < tag_code_table_offset_) {
-      return dead_code_table_->At(final_index - dead_code_table_offset_);
-    } else {
-      return tag_code_table_->At(final_index - tag_code_table_offset_);
-    }
-  }
-
-  CodeRegionTrieNode* exclusive_root_;
-  CodeRegionTable* live_code_table_;
-  CodeRegionTable* dead_code_table_;
-  CodeRegionTable* tag_code_table_;
-  intptr_t dead_code_table_offset_;
-  intptr_t tag_code_table_offset_;
-};
-
-
-void Profiler::PrintJSON(Isolate* isolate, JSONStream* stream,
-                         bool full, TagOrder tag_order) {
-  ASSERT(isolate == Isolate::Current());
-  // Disable profile interrupts while processing the buffer.
-  EndExecution(isolate);
-  MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
-  IsolateProfilerData* profiler_data = isolate->profiler_data();
-  if (profiler_data == NULL) {
-    JSONObject error(stream);
-    error.AddProperty("type", "Error");
-    error.AddProperty("text", "Isolate does not have profiling enabled.");
-    return;
-  }
-  SampleBuffer* sample_buffer = profiler_data->sample_buffer();
-  ASSERT(sample_buffer != NULL);
-  {
-    StackZone zone(isolate);
-    {
-      // Live code holds Dart, Native, and Collected CodeRegions.
-      CodeRegionTable live_code_table;
-      // Dead code holds Overwritten CodeRegions.
-      CodeRegionTable dead_code_table;
-      // Tag code holds Tag CodeRegions.
-      CodeRegionTable tag_code_table;
-      CodeRegionTableBuilder builder(isolate,
-                                     &live_code_table,
-                                     &dead_code_table,
-                                     &tag_code_table);
-      {
-        ScopeStopwatch sw("FixTopFrame");
-        // Preprocess samples and fix the caller when the top PC is in a
-        // stub or intrinsic without a frame.
-        FixTopFrameVisitor fixTopFrame(isolate);
-        sample_buffer->VisitSamples(&fixTopFrame);
-      }
-      {
-        // Build CodeRegion tables.
-        ScopeStopwatch sw("CodeRegionTableBuilder");
-        sample_buffer->VisitSamples(&builder);
-      }
-      intptr_t samples = builder.visited();
-      intptr_t frames = builder.frames();
-      if (FLAG_trace_profiler) {
-        intptr_t total_live_code_objects = live_code_table.Length();
-        intptr_t total_dead_code_objects = dead_code_table.Length();
-        intptr_t total_tag_code_objects = tag_code_table.Length();
-        OS::Print("Processed %" Pd " frames\n", frames);
-        OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n",
-                  total_live_code_objects,
-                  total_dead_code_objects,
-                  total_tag_code_objects);
-      }
-#if defined(DEBUG)
-      live_code_table.Verify();
-      dead_code_table.Verify();
-      tag_code_table.Verify();
-      if (FLAG_trace_profiler) {
-        OS::Print("CodeRegionTables verified to be ordered and not overlap.\n");
-      }
-#endif
-      CodeRegionExclusiveTrieBuilder build_trie(isolate,
-                                                &live_code_table,
-                                                &dead_code_table,
-                                                &tag_code_table);
-      build_trie.set_tag_order(tag_order);
-      {
-        // Build CodeRegion trie.
-        ScopeStopwatch sw("CodeRegionExclusiveTrieBuilder");
-        sample_buffer->VisitSamples(&build_trie);
-        build_trie.root()->SortByCount();
-      }
-      CodeRegionTableCallersBuilder build_callers(build_trie.root(),
-                                                  &live_code_table,
-                                                  &dead_code_table,
-                                                  &tag_code_table);
-      {
-        // Build CodeRegion callers.
-        ScopeStopwatch sw("CodeRegionTableCallersBuilder");
-        build_callers.Build();
-      }
-      {
-        ScopeStopwatch sw("CodeTableStream");
-        // Serialize to JSON.
-        JSONObject obj(stream);
-        obj.AddProperty("type", "CpuProfile");
-        obj.AddProperty("id", "profile");
-        obj.AddProperty("samples", samples);
-        obj.AddProperty("depth", static_cast<intptr_t>(FLAG_profile_depth));
-        obj.AddProperty("period", static_cast<intptr_t>(FLAG_profile_period));
-        obj.AddProperty("timeSpan",
-                        MicrosecondsToSeconds(builder.TimeDeltaMicros()));
-        {
-          JSONArray exclusive_trie(&obj, "exclusive_trie");
-          CodeRegionTrieNode* root = build_trie.root();
-          ASSERT(root != NULL);
-          root->PrintToJSONArray(&exclusive_trie);
-        }
-        JSONArray codes(&obj, "codes");
-        for (intptr_t i = 0; i < live_code_table.Length(); i++) {
-          CodeRegion* region = live_code_table.At(i);
-          ASSERT(region != NULL);
-          region->PrintToJSONArray(isolate, &codes, full);
-        }
-        for (intptr_t i = 0; i < dead_code_table.Length(); i++) {
-          CodeRegion* region = dead_code_table.At(i);
-          ASSERT(region != NULL);
-          region->PrintToJSONArray(isolate, &codes, full);
-        }
-        for (intptr_t i = 0; i < tag_code_table.Length(); i++) {
-          CodeRegion* region = tag_code_table.At(i);
-          ASSERT(region != NULL);
-          region->PrintToJSONArray(isolate, &codes, full);
-        }
-      }
-    }
-  }
-  // Enable profile interrupts.
-  BeginExecution(isolate);
-}
-
-
 IsolateProfilerData::IsolateProfilerData(SampleBuffer* sample_buffer,
                                          bool own_sample_buffer) {
   ASSERT(sample_buffer != NULL);
@@ -1775,22 +408,37 @@
 
   uword InitialReturnAddress() const {
     ASSERT(sp_ != NULL);
+    // MSan/ASan are unaware of frames initialized by generated code.
+    MSAN_UNPOISON(sp_, kWordSize);
+    ASAN_UNPOISON(sp_, kWordSize);
     return *(sp_);
   }
 
   uword* CallerPC() const {
     ASSERT(fp_ != NULL);
-    return reinterpret_cast<uword*>(*(fp_ + kSavedCallerPcSlotFromFp));
+    uword* caller_pc_ptr = fp_ + kSavedCallerPcSlotFromFp;
+    // MSan/ASan are unaware of frames initialized by generated code.
+    MSAN_UNPOISON(caller_pc_ptr, kWordSize);
+    ASAN_UNPOISON(caller_pc_ptr, kWordSize);
+    return reinterpret_cast<uword*>(*caller_pc_ptr);
   }
 
   uword* CallerFP() const {
     ASSERT(fp_ != NULL);
-    return reinterpret_cast<uword*>(*(fp_ + kSavedCallerFpSlotFromFp));
+    uword* caller_fp_ptr = fp_ + kSavedCallerFpSlotFromFp;
+    // MSan/ASan are unaware of frames initialized by generated code.
+    MSAN_UNPOISON(caller_fp_ptr, kWordSize);
+    ASAN_UNPOISON(caller_fp_ptr, kWordSize);
+    return reinterpret_cast<uword*>(*caller_fp_ptr);
   }
 
   uword* ExitLink() const {
     ASSERT(fp_ != NULL);
-    return reinterpret_cast<uword*>(*(fp_ + kExitLinkSlotFromEntryFp));
+    uword* exit_link_ptr = fp_ + kExitLinkSlotFromEntryFp;
+    // MSan/ASan are unaware of frames initialized by generated code.
+    MSAN_UNPOISON(exit_link_ptr, kWordSize);
+    ASAN_UNPOISON(exit_link_ptr, kWordSize);
+    return reinterpret_cast<uword*>(*exit_link_ptr);
   }
 
   bool ValidFramePointer() const {
diff --git a/runtime/vm/profiler.h b/runtime/vm/profiler.h
index 0c0bb0a..d93d648 100644
--- a/runtime/vm/profiler.h
+++ b/runtime/vm/profiler.h
@@ -6,31 +6,24 @@
 #define VM_PROFILER_H_
 
 #include "vm/allocation.h"
+#include "vm/bitfield.h"
 #include "vm/code_observers.h"
 #include "vm/globals.h"
 #include "vm/tags.h"
 #include "vm/thread_interrupter.h"
 
+// Profiler sampling and stack walking support.
+// NOTE: For service related code, see profile_service.h.
+
 namespace dart {
 
 // Forward declarations.
-class JSONArray;
-class JSONStream;
-class ProfilerCodeRegionTable;
 class Sample;
 class SampleBuffer;
 
-// Profiler
+
 class Profiler : public AllStatic {
  public:
-  enum TagOrder {
-    kNoTags,
-    kUser,
-    kUserVM,
-    kVM,
-    kVMUser
-  };
-
   static void InitOnce();
   static void Shutdown();
 
@@ -44,9 +37,6 @@
   static void BeginExecution(Isolate* isolate);
   static void EndExecution(Isolate* isolate);
 
-  static void PrintJSON(Isolate* isolate, JSONStream* stream,
-                        bool full, TagOrder tag_order);
-
   static SampleBuffer* sample_buffer() {
     return sample_buffer_;
   }
diff --git a/runtime/vm/profiler_service.cc b/runtime/vm/profiler_service.cc
new file mode 100644
index 0000000..a6457d6
--- /dev/null
+++ b/runtime/vm/profiler_service.cc
@@ -0,0 +1,1361 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#include "vm/profiler_service.h"
+
+#include "vm/growable_array.h"
+#include "vm/native_symbol.h"
+#include "vm/object.h"
+#include "vm/os.h"
+#include "vm/profiler.h"
+#include "vm/reusable_handles.h"
+#include "vm/scope_timer.h"
+
+namespace dart {
+
+DECLARE_FLAG(int, profile_depth);
+DECLARE_FLAG(bool, trace_profiler);
+DECLARE_FLAG(int, profile_period);
+
+struct AddressEntry {
+  uword pc;
+  intptr_t exclusive_ticks;
+  intptr_t inclusive_ticks;
+
+  void tick(bool exclusive) {
+    if (exclusive) {
+      exclusive_ticks++;
+    } else {
+      inclusive_ticks++;
+    }
+  }
+};
+
+
+struct CallEntry {
+  intptr_t code_table_index;
+  intptr_t count;
+};
+
+
+typedef bool (*RegionCompare)(uword pc, uword region_start, uword region_end);
+
+
+class CodeRegionTrieNode : public ZoneAllocated {
+ public:
+  explicit CodeRegionTrieNode(intptr_t code_region_index)
+      : code_region_index_(code_region_index),
+        count_(0),
+        children_(new ZoneGrowableArray<CodeRegionTrieNode*>()) {
+  }
+
+  void Tick() {
+    ASSERT(code_region_index_ >= 0);
+    count_++;
+  }
+
+  intptr_t count() const {
+    ASSERT(code_region_index_ >= 0);
+    return count_;
+  }
+
+  intptr_t code_region_index() const {
+    return code_region_index_;
+  }
+
+  ZoneGrowableArray<CodeRegionTrieNode*>& children() const {
+    return *children_;
+  }
+
+  CodeRegionTrieNode* GetChild(intptr_t child_code_region_index) {
+    const intptr_t length = children_->length();
+    intptr_t i = 0;
+    while (i < length) {
+      CodeRegionTrieNode* child = (*children_)[i];
+      if (child->code_region_index() == child_code_region_index) {
+        return child;
+      }
+      if (child->code_region_index() > child_code_region_index) {
+        break;
+      }
+      i++;
+    }
+    // Add new CodeRegion, sorted by CodeRegionTable index.
+    CodeRegionTrieNode* child = new CodeRegionTrieNode(child_code_region_index);
+    if (i < length) {
+      // Insert at i.
+      children_->InsertAt(i, child);
+    } else {
+      // Add to end.
+      children_->Add(child);
+    }
+    return child;
+  }
+
+  // Sort this's children and (recursively) all descendants by count.
+  // This should only be called after the trie is completely built.
+  void SortByCount() {
+    children_->Sort(CodeRegionTrieNodeCompare);
+    ZoneGrowableArray<CodeRegionTrieNode*>& kids = children();
+    intptr_t child_count = kids.length();
+    // Recurse.
+    for (intptr_t i = 0; i < child_count; i++) {
+      kids[i]->SortByCount();
+    }
+  }
+
+  void PrintToJSONArray(JSONArray* array) const {
+    ASSERT(array != NULL);
+    // Write CodeRegion index.
+    array->AddValue(code_region_index_);
+    // Write count.
+    array->AddValue(count_);
+    // Write number of children.
+    ZoneGrowableArray<CodeRegionTrieNode*>& kids = children();
+    intptr_t child_count = kids.length();
+    array->AddValue(child_count);
+    // Recurse.
+    for (intptr_t i = 0; i < child_count; i++) {
+      kids[i]->PrintToJSONArray(array);
+    }
+  }
+
+ private:
+  static int CodeRegionTrieNodeCompare(CodeRegionTrieNode* const* a,
+                                       CodeRegionTrieNode* const* b) {
+    ASSERT(a != NULL);
+    ASSERT(b != NULL);
+    return (*b)->count() - (*a)->count();
+  }
+
+  const intptr_t code_region_index_;
+  intptr_t count_;
+  ZoneGrowableArray<CodeRegionTrieNode*>* children_;
+};
+
+
+// A contiguous address region that holds code. Each CodeRegion has a "kind"
+// which describes the type of code contained inside the region. Each
+// region covers the following interval: [start, end).
+class CodeRegion : public ZoneAllocated {
+ public:
+  enum Kind {
+    kDartCode,       // Live Dart code.
+    kCollectedCode,  // Dead Dart code.
+    kNativeCode,     // Native code.
+    kReusedCode,     // Dead Dart code that has been reused by new kDartCode.
+    kTagCode,        // A special kind of code representing a tag.
+  };
+
+  CodeRegion(Kind kind, uword start, uword end, int64_t timestamp)
+      : kind_(kind),
+        start_(start),
+        end_(end),
+        inclusive_ticks_(0),
+        exclusive_ticks_(0),
+        inclusive_tick_serial_(0),
+        name_(NULL),
+        compile_timestamp_(timestamp),
+        creation_serial_(0),
+        address_table_(new ZoneGrowableArray<AddressEntry>()),
+        callers_table_(new ZoneGrowableArray<CallEntry>()),
+        callees_table_(new ZoneGrowableArray<CallEntry>()) {
+    ASSERT(start_ < end_);
+  }
+
+
+  uword start() const { return start_; }
+  void set_start(uword start) {
+    start_ = start;
+  }
+
+  uword end() const { return end_; }
+  void set_end(uword end) {
+    end_ = end;
+  }
+
+  void AdjustExtent(uword start, uword end) {
+    if (start < start_) {
+      start_ = start;
+    }
+    if (end > end_) {
+      end_ = end;
+    }
+    ASSERT(start_ < end_);
+  }
+
+  bool contains(uword pc) const {
+    return (pc >= start_) && (pc < end_);
+  }
+
+  bool overlaps(const CodeRegion* other) const {
+    ASSERT(other != NULL);
+    return other->contains(start_)   ||
+           other->contains(end_ - 1) ||
+           contains(other->start())  ||
+           contains(other->end() - 1);
+  }
+
+  intptr_t creation_serial() const { return creation_serial_; }
+  void set_creation_serial(intptr_t serial) {
+    creation_serial_ = serial;
+  }
+  int64_t compile_timestamp() const { return compile_timestamp_; }
+  void set_compile_timestamp(int64_t timestamp) {
+    compile_timestamp_ = timestamp;
+  }
+
+  intptr_t inclusive_ticks() const { return inclusive_ticks_; }
+  void set_inclusive_ticks(intptr_t inclusive_ticks) {
+    inclusive_ticks_ = inclusive_ticks;
+  }
+
+  intptr_t exclusive_ticks() const { return exclusive_ticks_; }
+  void set_exclusive_ticks(intptr_t exclusive_ticks) {
+    exclusive_ticks_ = exclusive_ticks;
+  }
+
+  const char* name() const { return name_; }
+  void SetName(const char* name) {
+    if (name == NULL) {
+      name_ = NULL;
+    }
+    intptr_t len = strlen(name);
+    name_ = Isolate::Current()->current_zone()->Alloc<const char>(len + 1);
+    strncpy(const_cast<char*>(name_), name, len);
+    const_cast<char*>(name_)[len] = '\0';
+  }
+
+  Kind kind() const { return kind_; }
+
+  static const char* KindToCString(Kind kind) {
+    switch (kind) {
+      case kDartCode:
+        return "Dart";
+      case kCollectedCode:
+        return "Collected";
+      case kNativeCode:
+        return "Native";
+      case kReusedCode:
+        return "Overwritten";
+      case kTagCode:
+        return "Tag";
+    }
+    UNREACHABLE();
+    return NULL;
+  }
+
+  void DebugPrint() const {
+    OS::Print("%s [%" Px ", %" Px ") %" Pd " %" Pd64 "\n",
+              KindToCString(kind_),
+              start(),
+              end(),
+              creation_serial_,
+              compile_timestamp_);
+  }
+
+  void Tick(uword pc, bool exclusive, intptr_t serial) {
+    // Assert that exclusive ticks are never passed a valid serial number.
+    ASSERT((exclusive && (serial == -1)) || (!exclusive && (serial != -1)));
+    if (!exclusive && (inclusive_tick_serial_ == serial)) {
+      // We've already given this code object an inclusive tick for this sample.
+      return;
+    }
+    // Tick the code object.
+    if (exclusive) {
+      exclusive_ticks_++;
+    } else {
+      inclusive_ticks_++;
+      // Mark the last serial we ticked the inclusive count.
+      inclusive_tick_serial_ = serial;
+    }
+    TickAddress(pc, exclusive);
+  }
+
+  void AddCaller(intptr_t index, intptr_t count) {
+    AddCallEntry(callers_table_, index, count);
+  }
+
+  void AddCallee(intptr_t index, intptr_t count) {
+    AddCallEntry(callees_table_, index, count);
+  }
+
+  void PrintNativeCode(JSONObject* profile_code_obj) {
+    ASSERT(kind() == kNativeCode);
+    JSONObject obj(profile_code_obj, "code");
+    obj.AddProperty("type", "@Code");
+    obj.AddProperty("kind", "Native");
+    obj.AddProperty("name", name());
+    obj.AddPropertyF("start", "%" Px "", start());
+    obj.AddPropertyF("end", "%" Px "", end());
+    obj.AddPropertyF("id", "code/native-%" Px "", start());
+    {
+      // Generate a fake function entry.
+      JSONObject func(&obj, "function");
+      func.AddProperty("type", "@Function");
+      func.AddPropertyF("id", "functions/native-%" Px "", start());
+      func.AddProperty("name", name());
+      func.AddProperty("kind", "Native");
+    }
+  }
+
+  void PrintCollectedCode(JSONObject* profile_code_obj) {
+    ASSERT(kind() == kCollectedCode);
+    JSONObject obj(profile_code_obj, "code");
+    obj.AddProperty("type", "@Code");
+    obj.AddProperty("kind", "Collected");
+    obj.AddProperty("name", name());
+    obj.AddPropertyF("start", "%" Px "", start());
+    obj.AddPropertyF("end", "%" Px "", end());
+    obj.AddPropertyF("id", "code/collected-%" Px "", start());
+    {
+      // Generate a fake function entry.
+      JSONObject func(&obj, "function");
+      func.AddProperty("type", "@Function");
+      obj.AddPropertyF("id", "functions/collected-%" Px "", start());
+      func.AddProperty("name", name());
+      func.AddProperty("kind", "Collected");
+    }
+  }
+
+  void PrintOverwrittenCode(JSONObject* profile_code_obj) {
+    ASSERT(kind() == kReusedCode);
+    JSONObject obj(profile_code_obj, "code");
+    obj.AddProperty("type", "@Code");
+    obj.AddProperty("kind", "Reused");
+    obj.AddProperty("name", name());
+    obj.AddPropertyF("start", "%" Px "", start());
+    obj.AddPropertyF("end", "%" Px "", end());
+    obj.AddPropertyF("id", "code/reused-%" Px "", start());
+    {
+      // Generate a fake function entry.
+      JSONObject func(&obj, "function");
+      func.AddProperty("type", "@Function");
+      obj.AddPropertyF("id", "functions/reused-%" Px "", start());
+      func.AddProperty("name", name());
+      func.AddProperty("kind", "Reused");
+    }
+  }
+
+  void  PrintTagCode(JSONObject* profile_code_obj) {
+    ASSERT(kind() == kTagCode);
+    JSONObject obj(profile_code_obj, "code");
+    obj.AddProperty("type", "@Code");
+    obj.AddProperty("kind", "Tag");
+    obj.AddPropertyF("id", "code/tag-%" Px "", start());
+    obj.AddProperty("name", name());
+    obj.AddPropertyF("start", "%" Px "", start());
+    obj.AddPropertyF("end", "%" Px "", end());
+    {
+      // Generate a fake function entry.
+      JSONObject func(&obj, "function");
+      func.AddProperty("type", "@Function");
+      func.AddProperty("kind", "Tag");
+      obj.AddPropertyF("id", "functions/tag-%" Px "", start());
+      func.AddProperty("name", name());
+    }
+  }
+
+  void PrintToJSONArray(Isolate* isolate, JSONArray* events) {
+    JSONObject obj(events);
+    obj.AddProperty("kind", KindToCString(kind()));
+    obj.AddPropertyF("inclusive_ticks", "%" Pd "", inclusive_ticks());
+    obj.AddPropertyF("exclusive_ticks", "%" Pd "", exclusive_ticks());
+    if (kind() == kDartCode) {
+      // Look up code in Dart heap.
+      Code& code = Code::Handle(isolate);
+      code ^= Code::LookupCode(start());
+      if (code.IsNull()) {
+        // Code is a stub in the Vm isolate.
+        code ^= Code::LookupCodeInVmIsolate(start());
+      }
+      ASSERT(!code.IsNull());
+      obj.AddProperty("code", code);
+    } else if (kind() == kCollectedCode) {
+      if (name() == NULL) {
+        // Lazily set generated name.
+        GenerateAndSetSymbolName("[Collected]");
+      }
+      PrintCollectedCode(&obj);
+    } else if (kind() == kReusedCode) {
+      if (name() == NULL) {
+        // Lazily set generated name.
+        GenerateAndSetSymbolName("[Reused]");
+      }
+      PrintOverwrittenCode(&obj);
+    } else if (kind() == kTagCode) {
+      if (name() == NULL) {
+        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 {
+      ASSERT(kind() == kNativeCode);
+      if (name() == NULL) {
+        // Lazily set generated name.
+        GenerateAndSetSymbolName("[Native]");
+      }
+      PrintNativeCode(&obj);
+    }
+    {
+      JSONArray ticks(&obj, "ticks");
+      for (intptr_t i = 0; i < address_table_->length(); i++) {
+        const AddressEntry& entry = (*address_table_)[i];
+        ticks.AddValueF("%" Px "", entry.pc);
+        ticks.AddValueF("%" Pd "", entry.exclusive_ticks);
+        ticks.AddValueF("%" Pd "", entry.inclusive_ticks);
+      }
+    }
+    {
+      JSONArray callers(&obj, "callers");
+      for (intptr_t i = 0; i < callers_table_->length(); i++) {
+        const CallEntry& entry = (*callers_table_)[i];
+        callers.AddValueF("%" Pd "", entry.code_table_index);
+        callers.AddValueF("%" Pd "", entry.count);
+      }
+    }
+    {
+      JSONArray callees(&obj, "callees");
+      for (intptr_t i = 0; i < callees_table_->length(); i++) {
+        const CallEntry& entry = (*callees_table_)[i];
+        callees.AddValueF("%" Pd "", entry.code_table_index);
+        callees.AddValueF("%" Pd "", entry.count);
+      }
+    }
+  }
+
+ private:
+  void TickAddress(uword pc, bool exclusive) {
+    const intptr_t length = address_table_->length();
+    intptr_t i = 0;
+    for (; i < length; i++) {
+      AddressEntry& entry = (*address_table_)[i];
+      if (entry.pc == pc) {
+        // Tick the address entry.
+        entry.tick(exclusive);
+        return;
+      }
+      if (entry.pc > pc) {
+        break;
+      }
+    }
+    // New address, add entry.
+    AddressEntry entry;
+    entry.pc = pc;
+    entry.exclusive_ticks = 0;
+    entry.inclusive_ticks = 0;
+    entry.tick(exclusive);
+    if (i < length) {
+      // Insert at i.
+      address_table_->InsertAt(i, entry);
+    } else {
+      // Add to end.
+      address_table_->Add(entry);
+    }
+  }
+
+
+  void AddCallEntry(ZoneGrowableArray<CallEntry>* table, intptr_t index,
+                    intptr_t count) {
+    const intptr_t length = table->length();
+    intptr_t i = 0;
+    for (; i < length; i++) {
+      CallEntry& entry = (*table)[i];
+      if (entry.code_table_index == index) {
+        entry.count += count;
+        return;
+      }
+      if (entry.code_table_index > index) {
+        break;
+      }
+    }
+    CallEntry entry;
+    entry.code_table_index = index;
+    entry.count = count;
+    if (i < length) {
+      table->InsertAt(i, entry);
+    } else {
+      table->Add(entry);
+    }
+  }
+
+  void GenerateAndSetSymbolName(const char* prefix) {
+    const intptr_t kBuffSize = 512;
+    char buff[kBuffSize];
+    OS::SNPrint(&buff[0], kBuffSize-1, "%s [%" Px ", %" Px ")",
+                prefix, start(), end());
+    SetName(buff);
+  }
+
+  // CodeRegion kind.
+  const Kind kind_;
+  // CodeRegion start address.
+  uword start_;
+  // CodeRegion end address.
+  uword end_;
+  // Inclusive ticks.
+  intptr_t inclusive_ticks_;
+  // Exclusive ticks.
+  intptr_t exclusive_ticks_;
+  // Inclusive tick serial number, ensures that each CodeRegion is only given
+  // a single inclusive tick per sample.
+  intptr_t inclusive_tick_serial_;
+  // Name of code region.
+  const char* name_;
+  // The compilation timestamp associated with this code region.
+  int64_t compile_timestamp_;
+  // Serial number at which this CodeRegion was created.
+  intptr_t creation_serial_;
+  ZoneGrowableArray<AddressEntry>* address_table_;
+  ZoneGrowableArray<CallEntry>* callers_table_;
+  ZoneGrowableArray<CallEntry>* callees_table_;
+  DISALLOW_COPY_AND_ASSIGN(CodeRegion);
+};
+
+
+// A sorted table of CodeRegions. Does not allow for overlap.
+class CodeRegionTable : public ValueObject {
+ public:
+  enum TickResult {
+    kTicked = 0,     // CodeRegion found and ticked.
+    kNotFound = -1,   // No CodeRegion found.
+    kNewerCode = -2,  // CodeRegion found but it was compiled after sample.
+  };
+
+  CodeRegionTable() :
+      code_region_table_(new ZoneGrowableArray<CodeRegion*>(64)) {
+  }
+
+  // Ticks the CodeRegion containing pc if it is alive at timestamp.
+  TickResult Tick(uword pc, bool exclusive, intptr_t serial,
+                  int64_t timestamp) {
+    intptr_t index = FindIndex(pc);
+    if (index < 0) {
+      // Not found.
+      return kNotFound;
+    }
+    ASSERT(index < code_region_table_->length());
+    CodeRegion* region = At(index);
+    if (region->compile_timestamp() > timestamp) {
+      // Compiled after tick.
+      return kNewerCode;
+    }
+    region->Tick(pc, exclusive, serial);
+    return kTicked;
+  }
+
+  // Table length.
+  intptr_t Length() const { return code_region_table_->length(); }
+
+  // Get the CodeRegion at index.
+  CodeRegion* At(intptr_t index) const {
+    return (*code_region_table_)[index];
+  }
+
+  // Find the table index to the CodeRegion containing pc.
+  // Returns < 0 if not found.
+  intptr_t FindIndex(uword pc) const {
+    intptr_t index = FindRegionIndex(pc, &CompareLowerBound);
+    const CodeRegion* code_region = NULL;
+    if (index == code_region_table_->length()) {
+      // Not present.
+      return -1;
+    }
+    code_region = At(index);
+    if (code_region->contains(pc)) {
+      // Found at index.
+      return index;
+    }
+    return -2;
+  }
+
+  // Insert code_region into the table. Returns the table index where the
+  // CodeRegion was inserted. Will merge with an overlapping CodeRegion if
+  // one is present.
+  intptr_t InsertCodeRegion(CodeRegion* code_region) {
+    const uword start = code_region->start();
+    const uword end = code_region->end();
+    const intptr_t length = code_region_table_->length();
+    if (length == 0) {
+      code_region_table_->Add(code_region);
+      return length;
+    }
+    // Determine the correct place to insert or merge code_region into table.
+    intptr_t lo = FindRegionIndex(start, &CompareLowerBound);
+    intptr_t hi = FindRegionIndex(end - 1, &CompareUpperBound);
+    // TODO(johnmccutchan): Simplify below logic.
+    if ((lo == length) && (hi == length)) {
+      lo = length - 1;
+    }
+    if (lo == length) {
+      CodeRegion* region = At(hi);
+      if (region->overlaps(code_region)) {
+        HandleOverlap(region, code_region, start, end);
+        return hi;
+      }
+      code_region_table_->Add(code_region);
+      return length;
+    } else if (hi == length) {
+      CodeRegion* region = At(lo);
+      if (region->overlaps(code_region)) {
+        HandleOverlap(region, code_region, start, end);
+        return lo;
+      }
+      code_region_table_->Add(code_region);
+      return length;
+    } else if (lo == hi) {
+      CodeRegion* region = At(lo);
+      if (region->overlaps(code_region)) {
+        HandleOverlap(region, code_region, start, end);
+        return lo;
+      }
+      code_region_table_->InsertAt(lo, code_region);
+      return lo;
+    } else {
+      CodeRegion* region = At(lo);
+      if (region->overlaps(code_region)) {
+        HandleOverlap(region, code_region, start, end);
+        return lo;
+      }
+      region = At(hi);
+      if (region->overlaps(code_region)) {
+        HandleOverlap(region, code_region, start, end);
+        return hi;
+      }
+      code_region_table_->InsertAt(hi, code_region);
+      return hi;
+    }
+    UNREACHABLE();
+  }
+
+#if defined(DEBUG)
+  void Verify() {
+    VerifyOrder();
+    VerifyOverlap();
+  }
+#endif
+
+  void DebugPrint() {
+    OS::Print("Dumping CodeRegionTable:\n");
+    for (intptr_t i = 0; i < code_region_table_->length(); i++) {
+      CodeRegion* region = At(i);
+      region->DebugPrint();
+    }
+  }
+
+ private:
+  intptr_t FindRegionIndex(uword pc, RegionCompare comparator) const {
+    ASSERT(comparator != NULL);
+    intptr_t count = code_region_table_->length();
+    intptr_t first = 0;
+    while (count > 0) {
+      intptr_t it = first;
+      intptr_t step = count / 2;
+      it += step;
+      const CodeRegion* code_region = At(it);
+      if (comparator(pc, code_region->start(), code_region->end())) {
+        first = ++it;
+        count -= (step + 1);
+      } else {
+        count = step;
+      }
+    }
+    return first;
+  }
+
+  static bool CompareUpperBound(uword pc, uword start, uword end) {
+    return pc >= end;
+  }
+
+  static bool CompareLowerBound(uword pc, uword start, uword end) {
+    return end <= pc;
+  }
+
+  void HandleOverlap(CodeRegion* region, CodeRegion* code_region,
+                     uword start, uword end) {
+    // We should never see overlapping Dart code regions.
+    ASSERT(region->kind() != CodeRegion::kDartCode);
+    // We should never see overlapping Tag code regions.
+    ASSERT(region->kind() != CodeRegion::kTagCode);
+    // When code regions overlap, they should be of the same kind.
+    ASSERT(region->kind() == code_region->kind());
+    region->AdjustExtent(start, end);
+  }
+
+#if defined(DEBUG)
+  void VerifyOrder() {
+    const intptr_t length = code_region_table_->length();
+    if (length == 0) {
+      return;
+    }
+    uword last = (*code_region_table_)[0]->end();
+    for (intptr_t i = 1; i < length; i++) {
+      CodeRegion* a = (*code_region_table_)[i];
+      ASSERT(last <= a->start());
+      last = a->end();
+    }
+  }
+
+  void VerifyOverlap() {
+    const intptr_t length = code_region_table_->length();
+    for (intptr_t i = 0; i < length; i++) {
+      CodeRegion* a = (*code_region_table_)[i];
+      for (intptr_t j = i+1; j < length; j++) {
+        CodeRegion* b = (*code_region_table_)[j];
+        ASSERT(!a->contains(b->start()) &&
+               !a->contains(b->end() - 1) &&
+               !b->contains(a->start()) &&
+               !b->contains(a->end() - 1));
+      }
+    }
+  }
+#endif
+
+  ZoneGrowableArray<CodeRegion*>* code_region_table_;
+};
+
+
+class FixTopFrameVisitor : public SampleVisitor {
+ public:
+  explicit FixTopFrameVisitor(Isolate* isolate)
+      : SampleVisitor(isolate),
+        vm_isolate_(Dart::vm_isolate()) {
+  }
+
+  void VisitSample(Sample* sample) {
+    if (sample->processed()) {
+      // Already processed.
+      return;
+    }
+    REUSABLE_CODE_HANDLESCOPE(isolate());
+    // Mark that we've processed this sample.
+    sample->set_processed(true);
+    // Lookup code object for leaf frame.
+    Code& code = reused_code_handle.Handle();
+    code = FindCodeForPC(sample->At(0));
+    sample->set_leaf_frame_is_dart(!code.IsNull());
+    if (sample->pc_marker() == 0) {
+      // No pc marker. Nothing to do.
+      return;
+    }
+    if (!code.IsNull() && (code.compile_timestamp() > sample->timestamp())) {
+      // Code compiled after sample. Ignore.
+      return;
+    }
+    if (sample->leaf_frame_is_dart()) {
+      CheckForMissingDartFrame(code, sample);
+    }
+  }
+
+ private:
+  void CheckForMissingDartFrame(const Code& code, Sample* sample) const {
+    // Some stubs (and intrinsics) do not push a frame onto the stack leaving
+    // the frame pointer in the caller.
+    //
+    // PC -> STUB
+    // FP -> DART3  <-+
+    //       DART2  <-|  <- TOP FRAME RETURN ADDRESS.
+    //       DART1  <-|
+    //       .....
+    //
+    // In this case, traversing the linked stack frames will not collect a PC
+    // inside DART3. The stack will incorrectly be: STUB, DART2, DART1.
+    // In Dart code, after pushing the FP onto the stack, an IP in the current
+    // function is pushed onto the stack as well. This stack slot is called
+    // the PC marker. We can use the PC marker to insert DART3 into the stack
+    // so that it will correctly be: STUB, DART3, DART2, DART1. Note the
+    // inserted PC may not accurately reflect the true return address from STUB.
+    ASSERT(!code.IsNull());
+    if (sample->sp() == sample->fp()) {
+      // Haven't pushed pc marker yet.
+      return;
+    }
+    uword pc_marker = sample->pc_marker();
+    if (code.ContainsInstructionAt(pc_marker)) {
+      // PC marker is in the same code as pc, no missing frame.
+      return;
+    }
+    if (!ContainedInDartCodeHeaps(pc_marker)) {
+      // Not a valid PC marker.
+      return;
+    }
+    sample->InsertCallerForTopFrame(pc_marker);
+  }
+
+  bool ContainedInDartCodeHeaps(uword pc) const {
+    return isolate()->heap()->CodeContains(pc) ||
+           vm_isolate()->heap()->CodeContains(pc);
+  }
+
+  Isolate* vm_isolate() const {
+    return vm_isolate_;
+  }
+
+  RawCode* FindCodeForPC(uword pc) const {
+    // Check current isolate for pc.
+    if (isolate()->heap()->CodeContains(pc)) {
+      return Code::LookupCode(pc);
+    }
+    // Check VM isolate for pc.
+    if (vm_isolate()->heap()->CodeContains(pc)) {
+      return Code::LookupCodeInVmIsolate(pc);
+    }
+    return Code::null();
+  }
+
+  Isolate* vm_isolate_;
+};
+
+
+class CodeRegionTableBuilder : public SampleVisitor {
+ public:
+  CodeRegionTableBuilder(Isolate* isolate,
+                         CodeRegionTable* live_code_table,
+                         CodeRegionTable* dead_code_table,
+                         CodeRegionTable* tag_code_table)
+      : SampleVisitor(isolate),
+        live_code_table_(live_code_table),
+        dead_code_table_(dead_code_table),
+        tag_code_table_(tag_code_table),
+        isolate_(isolate),
+        vm_isolate_(Dart::vm_isolate()) {
+    ASSERT(live_code_table_ != NULL);
+    ASSERT(dead_code_table_ != NULL);
+    ASSERT(tag_code_table_ != NULL);
+    frames_ = 0;
+    min_time_ = kMaxInt64;
+    max_time_ = 0;
+    ASSERT(isolate_ != NULL);
+    ASSERT(vm_isolate_ != NULL);
+  }
+
+  void VisitSample(Sample* sample) {
+    int64_t timestamp = sample->timestamp();
+    if (timestamp > max_time_) {
+      max_time_ = timestamp;
+    }
+    if (timestamp < min_time_) {
+      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 if we aren't sampled from an exit frame.
+    if (!sample->exit_frame_sample()) {
+      Tick(sample->At(0), true, timestamp);
+    }
+    // Inclusive tick for all frames.
+    for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
+      if (sample->At(i) == 0) {
+        break;
+      }
+      frames_++;
+      Tick(sample->At(i), false, timestamp);
+    }
+  }
+
+  intptr_t frames() const { return frames_; }
+
+  intptr_t  TimeDeltaMicros() const {
+    return static_cast<intptr_t>(max_time_ - min_time_);
+  }
+  int64_t  max_time() const { return max_time_; }
+
+ private:
+  void CreateTag(uword tag) {
+    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 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();
+    r = live_code_table_->Tick(pc, exclusive, serial, timestamp);
+    if (r == CodeRegionTable::kTicked) {
+      // Live code found and ticked.
+      return;
+    }
+    if (r == CodeRegionTable::kNewerCode) {
+      // Code has been overwritten by newer code.
+      // Update shadow table of dead code regions.
+      r = dead_code_table_->Tick(pc, exclusive, serial, timestamp);
+      ASSERT(r != CodeRegionTable::kNewerCode);
+      if (r == CodeRegionTable::kTicked) {
+        // Dead code found and ticked.
+        return;
+      }
+      ASSERT(r == CodeRegionTable::kNotFound);
+      CreateAndTickDeadCodeRegion(pc, exclusive, serial);
+      return;
+    }
+    // Create new live CodeRegion.
+    ASSERT(r == CodeRegionTable::kNotFound);
+    CodeRegion* region = CreateCodeRegion(pc);
+    region->set_creation_serial(visited());
+    intptr_t index = live_code_table_->InsertCodeRegion(region);
+    ASSERT(index >= 0);
+    region = live_code_table_->At(index);
+    if (region->compile_timestamp() <= timestamp) {
+      region->Tick(pc, exclusive, serial);
+      return;
+    }
+    // We have created a new code region but it's for a CodeRegion
+    // compiled after the sample.
+    ASSERT(region->kind() == CodeRegion::kDartCode);
+    CreateAndTickDeadCodeRegion(pc, exclusive, serial);
+  }
+
+  void CreateAndTickDeadCodeRegion(uword pc, bool exclusive, intptr_t serial) {
+    // Need to create dead code.
+    CodeRegion* region = new CodeRegion(CodeRegion::kReusedCode,
+                                        pc,
+                                        pc + 1,
+                                        0);
+    intptr_t index = dead_code_table_->InsertCodeRegion(region);
+    region->set_creation_serial(visited());
+    ASSERT(index >= 0);
+    dead_code_table_->At(index)->Tick(pc, exclusive, serial);
+  }
+
+  CodeRegion* CreateCodeRegion(uword pc) {
+    const intptr_t kDartCodeAlignment = OS::PreferredCodeAlignment();
+    const intptr_t kDartCodeAlignmentMask = ~(kDartCodeAlignment - 1);
+    Code& code = Code::Handle(isolate_);
+    // Check current isolate for pc.
+    if (isolate_->heap()->CodeContains(pc)) {
+      code ^= Code::LookupCode(pc);
+      if (!code.IsNull()) {
+        return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
+                              code.EntryPoint() + code.Size(),
+                              code.compile_timestamp());
+      }
+      return new CodeRegion(CodeRegion::kCollectedCode, pc,
+                            (pc & kDartCodeAlignmentMask) + kDartCodeAlignment,
+                            0);
+    }
+    // Check VM isolate for pc.
+    if (vm_isolate_->heap()->CodeContains(pc)) {
+      code ^= Code::LookupCodeInVmIsolate(pc);
+      if (!code.IsNull()) {
+        return new CodeRegion(CodeRegion::kDartCode, code.EntryPoint(),
+                              code.EntryPoint() + code.Size(),
+                              code.compile_timestamp());
+      }
+      return new CodeRegion(CodeRegion::kCollectedCode, pc,
+                            (pc & kDartCodeAlignmentMask) + kDartCodeAlignment,
+                            0);
+    }
+    // Check NativeSymbolResolver for pc.
+    uintptr_t native_start = 0;
+    char* native_name = NativeSymbolResolver::LookupSymbolName(pc,
+                                                               &native_start);
+    if (native_name == NULL) {
+      // No native name found.
+      return new CodeRegion(CodeRegion::kNativeCode, pc, pc + 1, 0);
+    }
+    ASSERT(pc >= native_start);
+    CodeRegion* code_region =
+        new CodeRegion(CodeRegion::kNativeCode, native_start, pc + 1, 0);
+    code_region->SetName(native_name);
+    free(native_name);
+    return code_region;
+  }
+
+  intptr_t frames_;
+  int64_t min_time_;
+  int64_t max_time_;
+  CodeRegionTable* live_code_table_;
+  CodeRegionTable* dead_code_table_;
+  CodeRegionTable* tag_code_table_;
+  Isolate* isolate_;
+  Isolate* vm_isolate_;
+};
+
+
+class CodeRegionExclusiveTrieBuilder : public SampleVisitor {
+ public:
+  CodeRegionExclusiveTrieBuilder(Isolate* isolate,
+                                 CodeRegionTable* live_code_table,
+                                 CodeRegionTable* dead_code_table,
+                                 CodeRegionTable* tag_code_table)
+      : SampleVisitor(isolate),
+        live_code_table_(live_code_table),
+        dead_code_table_(dead_code_table),
+        tag_code_table_(tag_code_table) {
+    ASSERT(live_code_table_ != NULL);
+    ASSERT(dead_code_table_ != NULL);
+    ASSERT(tag_code_table_ != NULL);
+    dead_code_table_offset_ = live_code_table_->Length();
+    tag_code_table_offset_ = dead_code_table_offset_ +
+                             dead_code_table_->Length();
+    intptr_t root_index = tag_code_table_->FindIndex(0);
+    // Verify that the "0" tag does not exist.
+    ASSERT(root_index < 0);
+    // Insert the dummy tag CodeRegion that is used for the Trie root.
+    CodeRegion* region = new CodeRegion(CodeRegion::kTagCode, 0, 1, 0);
+    root_index = tag_code_table_->InsertCodeRegion(region);
+    ASSERT(root_index >= 0);
+    region->set_creation_serial(0);
+    root_ = new CodeRegionTrieNode(tag_code_table_offset_ + root_index);
+    set_tag_order(ProfilerService::kUserVM);
+  }
+
+  void VisitSample(Sample* sample) {
+    // Give the root a tick.
+    root_->Tick();
+    CodeRegionTrieNode* current = root_;
+    current = ProcessTags(sample, current);
+    // Walk the sampled PCs.
+    for (intptr_t i = 0; i < FLAG_profile_depth; i++) {
+      if (sample->At(i) == 0) {
+        break;
+      }
+      intptr_t index = FindFinalIndex(sample->At(i), sample->timestamp());
+      current = current->GetChild(index);
+      current->Tick();
+    }
+  }
+
+  CodeRegionTrieNode* root() const {
+    return root_;
+  }
+
+  ProfilerService::TagOrder tag_order() const {
+    return tag_order_;
+  }
+
+  void set_tag_order(ProfilerService::TagOrder tag_order) {
+    tag_order_ = tag_order;
+  }
+
+ private:
+  CodeRegionTrieNode* ProcessUserTags(Sample* sample,
+                                      CodeRegionTrieNode* current) {
+    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();
+    }
+    return current;
+  }
+
+  CodeRegionTrieNode* ProcessVMTags(Sample* sample,
+                                    CodeRegionTrieNode* current) {
+    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.
+    current->Tick();
+    return current;
+  }
+
+  CodeRegionTrieNode* ProcessTags(Sample* sample, CodeRegionTrieNode* current) {
+    // None.
+    if (tag_order() == ProfilerService::kNoTags) {
+      return current;
+    }
+    // User first.
+    if ((tag_order() == ProfilerService::kUserVM) ||
+        (tag_order() == ProfilerService::kUser)) {
+      current = ProcessUserTags(sample, current);
+      // Only user.
+      if (tag_order() == ProfilerService::kUser) {
+        return current;
+      }
+      return ProcessVMTags(sample, current);
+    }
+    // VM first.
+    ASSERT((tag_order() == ProfilerService::kVMUser) ||
+           (tag_order() == ProfilerService::kVM));
+    current = ProcessVMTags(sample, current);
+    // Only VM.
+    if (tag_order() == ProfilerService::kVM) {
+      return current;
+    }
+    return ProcessUserTags(sample, current);
+  }
+
+  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;
+  }
+
+  intptr_t FindFinalIndex(uword pc, int64_t timestamp) const {
+    intptr_t index = live_code_table_->FindIndex(pc);
+    ASSERT(index >= 0);
+    CodeRegion* region = live_code_table_->At(index);
+    ASSERT(region->contains(pc));
+    if (region->compile_timestamp() > timestamp) {
+      // Overwritten code, find in dead code table.
+      index = dead_code_table_->FindIndex(pc);
+      ASSERT(index >= 0);
+      region = dead_code_table_->At(index);
+      ASSERT(region->contains(pc));
+      ASSERT(region->compile_timestamp() <= timestamp);
+      return index + dead_code_table_offset_;
+    }
+    ASSERT(region->compile_timestamp() <= timestamp);
+    return index;
+  }
+
+  ProfilerService::TagOrder tag_order_;
+  CodeRegionTrieNode* root_;
+  CodeRegionTable* live_code_table_;
+  CodeRegionTable* dead_code_table_;
+  CodeRegionTable* tag_code_table_;
+  intptr_t dead_code_table_offset_;
+  intptr_t tag_code_table_offset_;
+};
+
+
+class CodeRegionTableCallersBuilder {
+ public:
+  CodeRegionTableCallersBuilder(CodeRegionTrieNode* exclusive_root,
+                                CodeRegionTable* live_code_table,
+                                CodeRegionTable* dead_code_table,
+                                CodeRegionTable* tag_code_table)
+      : exclusive_root_(exclusive_root),
+        live_code_table_(live_code_table),
+        dead_code_table_(dead_code_table),
+        tag_code_table_(tag_code_table) {
+    ASSERT(exclusive_root_ != NULL);
+    ASSERT(live_code_table_ != NULL);
+    ASSERT(dead_code_table_ != NULL);
+    ASSERT(tag_code_table_ != NULL);
+    dead_code_table_offset_ = live_code_table_->Length();
+    tag_code_table_offset_ = dead_code_table_offset_ +
+                             dead_code_table_->Length();
+  }
+
+  void Build() {
+    ProcessNode(exclusive_root_);
+  }
+
+ private:
+  void ProcessNode(CodeRegionTrieNode* parent) {
+    const ZoneGrowableArray<CodeRegionTrieNode*>& children = parent->children();
+    intptr_t parent_index = parent->code_region_index();
+    ASSERT(parent_index >= 0);
+    CodeRegion* parent_region = At(parent_index);
+    ASSERT(parent_region != NULL);
+    for (intptr_t i = 0; i < children.length(); i++) {
+      CodeRegionTrieNode* node = children[i];
+      ProcessNode(node);
+      intptr_t index = node->code_region_index();
+      ASSERT(index >= 0);
+      CodeRegion* region = At(index);
+      ASSERT(region != NULL);
+      region->AddCallee(parent_index, node->count());
+      parent_region->AddCaller(index, node->count());
+    }
+  }
+
+  CodeRegion* At(intptr_t final_index) {
+    ASSERT(final_index >= 0);
+    if (final_index < dead_code_table_offset_) {
+      return live_code_table_->At(final_index);
+    } else if (final_index < tag_code_table_offset_) {
+      return dead_code_table_->At(final_index - dead_code_table_offset_);
+    } else {
+      return tag_code_table_->At(final_index - tag_code_table_offset_);
+    }
+  }
+
+  CodeRegionTrieNode* exclusive_root_;
+  CodeRegionTable* live_code_table_;
+  CodeRegionTable* dead_code_table_;
+  CodeRegionTable* tag_code_table_;
+  intptr_t dead_code_table_offset_;
+  intptr_t tag_code_table_offset_;
+};
+
+
+void ProfilerService::PrintJSON(JSONStream* stream, TagOrder tag_order) {
+  Isolate* isolate = Isolate::Current();
+  // Disable profile interrupts while processing the buffer.
+  Profiler::EndExecution(isolate);
+  MutexLocker profiler_data_lock(isolate->profiler_data_mutex());
+  IsolateProfilerData* profiler_data = isolate->profiler_data();
+  if (profiler_data == NULL) {
+    JSONObject error(stream);
+    error.AddProperty("type", "Error");
+    error.AddProperty("text", "Isolate does not have profiling enabled.");
+    return;
+  }
+  SampleBuffer* sample_buffer = profiler_data->sample_buffer();
+  ASSERT(sample_buffer != NULL);
+  {
+    StackZone zone(isolate);
+    {
+      // Live code holds Dart, Native, and Collected CodeRegions.
+      CodeRegionTable live_code_table;
+      // Dead code holds Overwritten CodeRegions.
+      CodeRegionTable dead_code_table;
+      // Tag code holds Tag CodeRegions.
+      CodeRegionTable tag_code_table;
+      CodeRegionTableBuilder builder(isolate,
+                                     &live_code_table,
+                                     &dead_code_table,
+                                     &tag_code_table);
+      {
+        ScopeTimer sw("FixTopFrame", FLAG_trace_profiler);
+        // Preprocess samples and fix the caller when the top PC is in a
+        // stub or intrinsic without a frame.
+        FixTopFrameVisitor fixTopFrame(isolate);
+        sample_buffer->VisitSamples(&fixTopFrame);
+      }
+      {
+        // Build CodeRegion tables.
+        ScopeTimer sw("CodeRegionTableBuilder", FLAG_trace_profiler);
+        sample_buffer->VisitSamples(&builder);
+      }
+      intptr_t samples = builder.visited();
+      intptr_t frames = builder.frames();
+      if (FLAG_trace_profiler) {
+        intptr_t total_live_code_objects = live_code_table.Length();
+        intptr_t total_dead_code_objects = dead_code_table.Length();
+        intptr_t total_tag_code_objects = tag_code_table.Length();
+        OS::Print("Processed %" Pd " frames\n", frames);
+        OS::Print("CodeTables: live=%" Pd " dead=%" Pd " tag=%" Pd "\n",
+                  total_live_code_objects,
+                  total_dead_code_objects,
+                  total_tag_code_objects);
+      }
+#if defined(DEBUG)
+      live_code_table.Verify();
+      dead_code_table.Verify();
+      tag_code_table.Verify();
+      if (FLAG_trace_profiler) {
+        OS::Print("CodeRegionTables verified to be ordered and not overlap.\n");
+      }
+#endif
+      CodeRegionExclusiveTrieBuilder build_trie(isolate,
+                                                &live_code_table,
+                                                &dead_code_table,
+                                                &tag_code_table);
+      build_trie.set_tag_order(tag_order);
+      {
+        // Build CodeRegion trie.
+        ScopeTimer sw("CodeRegionExclusiveTrieBuilder", FLAG_trace_profiler);
+        sample_buffer->VisitSamples(&build_trie);
+        build_trie.root()->SortByCount();
+      }
+      CodeRegionTableCallersBuilder build_callers(build_trie.root(),
+                                                  &live_code_table,
+                                                  &dead_code_table,
+                                                  &tag_code_table);
+      {
+        // Build CodeRegion callers.
+        ScopeTimer sw("CodeRegionTableCallersBuilder", FLAG_trace_profiler);
+        build_callers.Build();
+      }
+      {
+        ScopeTimer sw("CodeTableStream", FLAG_trace_profiler);
+        // Serialize to JSON.
+        JSONObject obj(stream);
+        obj.AddProperty("type", "CpuProfile");
+        obj.AddProperty("id", "profile");
+        obj.AddProperty("samples", samples);
+        obj.AddProperty("depth", static_cast<intptr_t>(FLAG_profile_depth));
+        obj.AddProperty("period", static_cast<intptr_t>(FLAG_profile_period));
+        obj.AddProperty("timeSpan",
+                        MicrosecondsToSeconds(builder.TimeDeltaMicros()));
+        {
+          JSONArray exclusive_trie(&obj, "exclusive_trie");
+          CodeRegionTrieNode* root = build_trie.root();
+          ASSERT(root != NULL);
+          root->PrintToJSONArray(&exclusive_trie);
+        }
+        JSONArray codes(&obj, "codes");
+        for (intptr_t i = 0; i < live_code_table.Length(); i++) {
+          CodeRegion* region = live_code_table.At(i);
+          ASSERT(region != NULL);
+          region->PrintToJSONArray(isolate, &codes);
+        }
+        for (intptr_t i = 0; i < dead_code_table.Length(); i++) {
+          CodeRegion* region = dead_code_table.At(i);
+          ASSERT(region != NULL);
+          region->PrintToJSONArray(isolate, &codes);
+        }
+        for (intptr_t i = 0; i < tag_code_table.Length(); i++) {
+          CodeRegion* region = tag_code_table.At(i);
+          ASSERT(region != NULL);
+          region->PrintToJSONArray(isolate, &codes);
+        }
+      }
+    }
+  }
+  // Enable profile interrupts.
+  Profiler::BeginExecution(isolate);
+}
+
+}  // namespace dart
diff --git a/runtime/vm/profiler_service.h b/runtime/vm/profiler_service.h
new file mode 100644
index 0000000..80c2b49
--- /dev/null
+++ b/runtime/vm/profiler_service.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef VM_PROFILER_SERVICE_H_
+#define VM_PROFILER_SERVICE_H_
+
+#include "vm/allocation.h"
+#include "vm/code_observers.h"
+#include "vm/globals.h"
+#include "vm/tags.h"
+#include "vm/thread_interrupter.h"
+
+// Profile VM Service.
+// NOTE: For sampling and stack walking related code, see profiler.h.
+
+namespace dart {
+
+// Forward declarations.
+class JSONArray;
+class JSONStream;
+class ProfilerCodeRegionTable;
+
+class ProfilerService : public AllStatic {
+ public:
+  enum TagOrder {
+    kNoTags,
+    kUser,
+    kUserVM,
+    kVM,
+    kVMUser
+  };
+
+  static void PrintJSON(JSONStream* stream,
+                        TagOrder tag_order);
+};
+
+}  // namespace dart
+
+#endif  // VM_PROFILER_SERVICE_H_
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 5f13b40..dd3e075 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -951,6 +951,7 @@
   RawArray* stackmaps_;
   RawLocalVarDescriptors* var_descriptors_;
   RawArray* inlined_intervals_;
+  RawArray* inlined_id_to_function_;
   RawArray* comments_;
   RawObject** to() {
     return reinterpret_cast<RawObject**>(&ptr()->comments_);
diff --git a/runtime/vm/scavenger.cc b/runtime/vm/scavenger.cc
index 5ea4ad4..5df2fe9 100644
--- a/runtime/vm/scavenger.cc
+++ b/runtime/vm/scavenger.cc
@@ -475,7 +475,6 @@
 
 
 void Scavenger::Epilogue(Isolate* isolate,
-                         ScavengerVisitor* visitor,
                          bool invoke_api_callbacks) {
   // All objects in the to space have been copied from the from space at this
   // moment.
@@ -818,33 +817,38 @@
   intptr_t promo_candidate_words =
       (survivor_end_ - FirstObjectStart()) / kWordSize;
   Prologue(isolate, invoke_api_callbacks);
-  const bool prologue_weak_are_strong = !invoke_api_callbacks;
+  // The API prologue/epilogue may create/destroy zones, so we must not
+  // depend on zone allocations surviving beyond the epilogue callback.
+  {
+    StackZone zone(isolate);
+    // Setup the visitor and run the scavenge.
+    ScavengerVisitor visitor(isolate, this);
+    page_space->AcquireDataLock();
+    const bool prologue_weak_are_strong = !invoke_api_callbacks;
+    IterateRoots(isolate, &visitor, prologue_weak_are_strong);
+    int64_t start = OS::GetCurrentTimeMicros();
+    ProcessToSpace(&visitor);
+    int64_t middle = OS::GetCurrentTimeMicros();
+    IterateWeakReferences(isolate, &visitor);
+    ScavengerWeakVisitor weak_visitor(this, prologue_weak_are_strong);
+    // Include the prologue weak handles, since we must process any promotion.
+    const bool visit_prologue_weak_handles = true;
+    IterateWeakRoots(isolate, &weak_visitor, visit_prologue_weak_handles);
+    visitor.Finalize();
+    ProcessWeakTables();
+    page_space->ReleaseDataLock();
 
-  // Setup the visitor and run the scavenge.
-  ScavengerVisitor visitor(isolate, this);
-  page_space->AcquireDataLock();
-  IterateRoots(isolate, &visitor, prologue_weak_are_strong);
-  int64_t start = OS::GetCurrentTimeMicros();
-  ProcessToSpace(&visitor);
-  int64_t middle = OS::GetCurrentTimeMicros();
-  IterateWeakReferences(isolate, &visitor);
-  ScavengerWeakVisitor weak_visitor(this, prologue_weak_are_strong);
-  // Include the prologue weak handles, since we must process any promotion.
-  const bool visit_prologue_weak_handles = true;
-  IterateWeakRoots(isolate, &weak_visitor, visit_prologue_weak_handles);
-  visitor.Finalize();
-  ProcessWeakTables();
-  page_space->ReleaseDataLock();
-
-  // Scavenge finished. Run accounting and epilogue.
-  int64_t end = OS::GetCurrentTimeMicros();
-  heap_->RecordTime(kProcessToSpace, middle - start);
-  heap_->RecordTime(kIterateWeaks, end - middle);
-  stats_history_.Add(ScavengeStats(start, end,
-                                   usage_before, GetCurrentUsage(),
-                                   promo_candidate_words,
-                                   visitor.bytes_promoted() >> kWordSizeLog2));
-  Epilogue(isolate, &visitor, invoke_api_callbacks);
+    // Scavenge finished. Run accounting.
+    int64_t end = OS::GetCurrentTimeMicros();
+    heap_->RecordTime(kProcessToSpace, middle - start);
+    heap_->RecordTime(kIterateWeaks, end - middle);
+    stats_history_.Add(
+        ScavengeStats(start, end,
+                      usage_before, GetCurrentUsage(),
+                      promo_candidate_words,
+                      visitor.bytes_promoted() >> kWordSizeLog2));
+  }
+  Epilogue(isolate, invoke_api_callbacks);
 
   // TODO(koda): Make verification more compatible with concurrent sweep.
   if (FLAG_verify_after_gc && !FLAG_concurrent_sweep) {
diff --git a/runtime/vm/scavenger.h b/runtime/vm/scavenger.h
index 117b3fe..508aabe 100644
--- a/runtime/vm/scavenger.h
+++ b/runtime/vm/scavenger.h
@@ -247,7 +247,6 @@
   uword ProcessWeakProperty(RawWeakProperty* raw_weak,
                             ScavengerVisitor* visitor);
   void Epilogue(Isolate* isolate,
-                ScavengerVisitor* visitor,
                 bool invoke_api_callbacks);
 
   bool IsUnreachable(RawObject** p);
diff --git a/runtime/vm/scope_timer.h b/runtime/vm/scope_timer.h
new file mode 100644
index 0000000..b90270b
--- /dev/null
+++ b/runtime/vm/scope_timer.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+#ifndef VM_SCOPE_TIMER_H_
+#define VM_SCOPE_TIMER_H_
+
+namespace dart {
+
+// Simple utility class for timing a block of code.
+class ScopeTimer : public ValueObject {
+ public:
+  explicit ScopeTimer(const char* name, bool enabled = true)
+      : enabled_(enabled),
+        name_(name) {
+    if (!enabled_)     {
+      return;
+    }
+    start_ = OS::GetCurrentTimeMicros();
+  }
+
+  int64_t GetElapsed() const {
+    int64_t end = OS::GetCurrentTimeMicros();
+    ASSERT(end >= start_);
+    return end - start_;
+  }
+
+  ~ScopeTimer() {
+    if (!enabled_) {
+      return;
+    }
+    int64_t elapsed = GetElapsed();
+    OS::Print("%s took %" Pd64 " micros.\n", name_, elapsed);
+  }
+
+ private:
+  const bool enabled_;
+  const char* name_;
+  int64_t start_;
+};
+
+}  // namespace dart
+
+#endif  // VM_SCOPE_TIMER_H_
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 0361262..cbb1699 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -25,7 +25,7 @@
 #include "vm/object_store.h"
 #include "vm/parser.h"
 #include "vm/port.h"
-#include "vm/profiler.h"
+#include "vm/profiler_service.h"
 #include "vm/reusable_handles.h"
 #include "vm/stack_frame.h"
 #include "vm/symbols.h"
@@ -287,6 +287,9 @@
 void Service::SetServiceIsolate(Isolate* isolate) {
   MonitorLocker ml(monitor_);
   service_isolate_ = isolate;
+  if (service_isolate_ != NULL) {
+    service_isolate_->is_service_isolate_ = true;
+  }
 }
 
 
@@ -2173,6 +2176,10 @@
     JSONObject jsobj(js);
     jsobj.AddProperty("type", "Success");
     jsobj.AddProperty("id", "");
+    {
+      DebuggerEvent resumeEvent(isolate, DebuggerEvent::kIsolateResumed);
+      Service::HandleDebuggerEvent(&resumeEvent);
+    }
     return true;
   }
   if (isolate->message_handler()->paused_on_exit()) {
@@ -2180,6 +2187,7 @@
     JSONObject jsobj(js);
     jsobj.AddProperty("type", "Success");
     jsobj.AddProperty("id", "");
+    // We don't send a resume event because we will be exiting.
     return true;
   }
   if (isolate->debugger()->PauseEvent() != NULL) {
@@ -2235,27 +2243,24 @@
 }
 
 static bool HandleIsolateGetCpuProfile(Isolate* isolate, JSONStream* js) {
-  // A full profile includes disassembly of all Dart code objects.
-  // TODO(johnmccutchan): Add option to trigger full code dump.
-  bool full_profile = false;
-  Profiler::TagOrder tag_order = Profiler::kUserVM;
+  ProfilerService::TagOrder tag_order = ProfilerService::kUserVM;
   if (js->HasParam("tags")) {
     if (js->ParamIs("tags", "None")) {
-      tag_order = Profiler::kNoTags;
+      tag_order = ProfilerService::kNoTags;
     } else if (js->ParamIs("tags", "UserVM")) {
-      tag_order = Profiler::kUserVM;
+      tag_order = ProfilerService::kUserVM;
     } else if (js->ParamIs("tags", "UserOnly")) {
-      tag_order = Profiler::kUser;
+      tag_order = ProfilerService::kUser;
     } else if (js->ParamIs("tags", "VMUser")) {
-      tag_order = Profiler::kVMUser;
+      tag_order = ProfilerService::kVMUser;
     } else if (js->ParamIs("tags", "VMOnly")) {
-      tag_order = Profiler::kVM;
+      tag_order = ProfilerService::kVM;
     } else {
       PrintInvalidParamError(js, "tags");
       return true;
     }
   }
-  Profiler::PrintJSON(isolate, js, full_profile, tag_order);
+  ProfilerService::PrintJSON(js, tag_order);
   return true;
 }
 
diff --git a/runtime/vm/simulator_arm.cc b/runtime/vm/simulator_arm.cc
index 4050429..d3cfe92 100644
--- a/runtime/vm/simulator_arm.cc
+++ b/runtime/vm/simulator_arm.cc
@@ -24,9 +24,9 @@
 
 namespace dart {
 
-DEFINE_FLAG(int, trace_sim_after, -1,
+DEFINE_FLAG(uint64_t, trace_sim_after, ULLONG_MAX,
             "Trace simulator execution after instruction count reached.");
-DEFINE_FLAG(int, stop_sim_at, -1,
+DEFINE_FLAG(uint64_t, stop_sim_at, ULLONG_MAX,
             "Instruction address or instruction count to stop simulator at.");
 
 
@@ -200,10 +200,6 @@
       return true;
     }
   }
-  if (strcmp("icount", desc) == 0) {
-    *value = sim_->get_icount();
-    return true;
-  }
   bool retval = SScanF(desc, "0x%x", value) == 1;
   if (!retval) {
     retval = SScanF(desc, "%x", value) == 1;
@@ -462,13 +458,16 @@
       } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
         if (args == 2) {
           uint32_t value;
-          if (GetValue(arg1, &value)) {
+          if (strcmp(arg1, "icount") == 0) {
+            const uint64_t icount = sim_->get_icount();
+            OS::Print("icount: %"Pu64" 0x%"Px64"\n", icount, icount);
+          } else if (GetValue(arg1, &value)) {
             OS::Print("%s: %u 0x%x\n", arg1, value, value);
           } else {
             OS::Print("%s unrecognized\n", arg1);
           }
         } else {
-          OS::Print("print <reg or value or *addr>\n");
+          OS::Print("print <reg or icount or value or *addr>\n");
         }
       } else if ((strcmp(cmd, "ps") == 0) ||
                  (strcmp(cmd, "printsingle") == 0)) {
@@ -595,11 +594,11 @@
           OS::Print("Not at debugger stop.\n");
         }
       } else if (strcmp(cmd, "trace") == 0) {
-        if (FLAG_trace_sim_after == -1) {
+        if (FLAG_trace_sim_after == ULLONG_MAX) {
           FLAG_trace_sim_after = sim_->get_icount();
           OS::Print("execution tracing on\n");
         } else {
-          FLAG_trace_sim_after = -1;
+          FLAG_trace_sim_after = ULLONG_MAX;
           OS::Print("execution tracing off\n");
         }
       } else if (strcmp(cmd, "bt") == 0) {
@@ -811,9 +810,18 @@
       : external_function_(external_function),
         call_kind_(call_kind),
         argument_count_(argument_count),
-        svc_instruction_(Instr::kSimulatorRedirectInstruction),
-        next_(list_) {
-    list_ = this;
+        svc_instruction_(Instr::kSimulatorRedirectInstruction) {
+    // Atomically prepend this element to the front of the global list.
+    // Note: Since elements are never removed, there is no ABA issue.
+    Redirection* list_head = list_;
+    do {
+      next_ = list_head;
+      list_head = reinterpret_cast<Redirection*>(
+          AtomicOperations::CompareAndSwapWord(
+              reinterpret_cast<uword*>(&list_),
+              reinterpret_cast<uword>(next_),
+              reinterpret_cast<uword>(this)));
+    } while (list_head != next_);
   }
 
   uword external_function_;
@@ -1177,10 +1185,7 @@
 
 
 bool Simulator::IsTracingExecution() const {
-  // Integer flag values are signed, so we must cast to unsigned.
-  // The default of -1 hence becomes the maximum unsigned value.
-  return (static_cast<uintptr_t>(icount_) >
-          static_cast<uintptr_t>(FLAG_trace_sim_after));
+  return icount_ > FLAG_trace_sim_after;
 }
 
 
@@ -3582,7 +3587,7 @@
 void Simulator::InstructionDecode(Instr* instr) {
   pc_modified_ = false;
   if (IsTracingExecution()) {
-    OS::Print("%" Pd " ", icount_);
+    OS::Print("%"Pu64, icount_);
     const uword start = reinterpret_cast<uword>(instr);
     const uword end = start + Instr::kInstrSize;
     Disassembler::Disassemble(start, end);
@@ -3647,7 +3652,7 @@
   // raw PC value and not the one used as input to arithmetic instructions.
   uword program_counter = get_pc();
 
-  if (FLAG_stop_sim_at == 0) {
+  if (FLAG_stop_sim_at == ULLONG_MAX) {
     // Fast version of the dispatch loop without checking whether the simulator
     // should be stopping at a particular executed instruction.
     while (program_counter != kEndSimulatingPC) {
@@ -3669,7 +3674,7 @@
       if (icount_ == FLAG_stop_sim_at) {
         SimulatorDebugger dbg(this);
         dbg.Stop(instr, "Instruction count reached");
-      } else if (reinterpret_cast<intptr_t>(instr) == FLAG_stop_sim_at) {
+      } else if (reinterpret_cast<uint64_t>(instr) == FLAG_stop_sim_at) {
         SimulatorDebugger dbg(this);
         dbg.Stop(instr, "Instruction address reached");
       } else if (IsIllegalAddress(program_counter)) {
diff --git a/runtime/vm/simulator_arm.h b/runtime/vm/simulator_arm.h
index 5ed1b2f..a846d60 100644
--- a/runtime/vm/simulator_arm.h
+++ b/runtime/vm/simulator_arm.h
@@ -73,7 +73,7 @@
   uword StackTop() const;
 
   // Accessor to the instruction counter.
-  intptr_t get_icount() const { return icount_; }
+  uint64_t get_icount() const { return icount_; }
 
   // The isolate's top_exit_frame_info refers to a Dart frame in the simulator
   // stack. The simulator's top_exit_frame_info refers to a C++ frame in the
@@ -156,7 +156,7 @@
   // Simulator support.
   char* stack_;
   bool pc_modified_;
-  intptr_t icount_;
+  uint64_t icount_;
   static int32_t flag_stop_sim_at_;
   SimulatorSetjmpBuffer* last_setjmp_buffer_;
   uword top_exit_frame_info_;
diff --git a/runtime/vm/simulator_arm64.cc b/runtime/vm/simulator_arm64.cc
index cbb4dc7..84f5257 100644
--- a/runtime/vm/simulator_arm64.cc
+++ b/runtime/vm/simulator_arm64.cc
@@ -23,9 +23,9 @@
 
 namespace dart {
 
-DEFINE_FLAG(int, trace_sim_after, -1,
+DEFINE_FLAG(uint64_t, trace_sim_after, ULLONG_MAX,
             "Trace simulator execution after instruction count reached.");
-DEFINE_FLAG(int, stop_sim_at, -1,
+DEFINE_FLAG(uint64_t, stop_sim_at, ULLONG_MAX,
             "Instruction address or instruction count to stop simulator at.");
 
 
@@ -198,13 +198,9 @@
     *value = sim_->get_pc();
     return true;
   }
-  if (strcmp("icount", desc) == 0) {
-    *value = sim_->get_icount();
-    return true;
-  }
-  bool retval = SScanF(desc, "0x%"Px64, value) == 1;
+  bool retval = SScanF(desc, "0x%" Px64, value) == 1;
   if (!retval) {
-    retval = SScanF(desc, "%"Px64, value) == 1;
+    retval = SScanF(desc, "%" Px64, value) == 1;
   }
   return retval;
 }
@@ -481,13 +477,16 @@
       } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
         if (args == 2) {
           uint64_t value;
-          if (GetValue(arg1, &value)) {
-            OS::Print("%s: %"Pu64" 0x%"Px64"\n", arg1, value, value);
+          if (strcmp(arg1, "icount") == 0) {
+            value = sim_->get_icount();
+            OS::Print("icount: %" Pu64 " 0x%" Px64 "\n", value, value);
+          } else 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");
+          OS::Print("print <reg or icount or value or *addr>\n");
         }
       } else if ((strcmp(cmd, "pf") == 0) ||
                  (strcmp(cmd, "printfloat") == 0)) {
@@ -509,7 +508,7 @@
           uint64_t long_value;
           if (GetDValue(arg1, &long_value)) {
             double dvalue = bit_cast<double, uint64_t>(long_value);
-            OS::Print("%s: %"Pu64" 0x%"Px64" %.8g\n",
+            OS::Print("%s: %" Pu64 " 0x%" Px64 " %.8g\n",
                 arg1, long_value, long_value, dvalue);
           } else {
             OS::Print("%s unrecognized\n", arg1);
@@ -534,8 +533,8 @@
             const float sval1 = bit_cast<float, int32_t>(s1);
             const float sval2 = bit_cast<float, int32_t>(s2);
             const float sval3 = bit_cast<float, int32_t>(s3);
-            OS::Print("%s: %"Pu64" 0x%"Px64" %.8g\n", arg1, d0, d0, dval0);
-            OS::Print("%s: %"Pu64" 0x%"Px64" %.8g\n", arg1, d1, d1, dval1);
+            OS::Print("%s: %" Pu64 " 0x%" Px64 " %.8g\n", arg1, d0, d0, dval0);
+            OS::Print("%s: %" Pu64 " 0x%" Px64 " %.8g\n", arg1, d1, d1, dval1);
             OS::Print("%s: %d 0x%x %.8g\n", arg1, s0, s0, sval0);
             OS::Print("%s: %d 0x%x %.8g\n", arg1, s1, s1, sval1);
             OS::Print("%s: %d 0x%x %.8g\n", arg1, s2, s2, sval2);
@@ -561,7 +560,7 @@
               obj.Print();
 #endif  // defined(DEBUG)
             } else {
-              OS::Print("0x%"Px64" is not an object reference\n", value);
+              OS::Print("0x%" Px64 " is not an object reference\n", value);
             }
           } else {
             OS::Print("%s unrecognized\n", arg1);
@@ -580,7 +579,7 @@
             // 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",
+              OS::Print("First argument yields invalid address: 0x%" Px64 "\n",
                         start);
               OS::Print("Using PC instead\n");
               start = sim_->get_pc();
@@ -592,7 +591,7 @@
           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",
+              OS::Print("First argument yields invalid address: 0x%" Px64 "\n",
                         start);
               OS::Print("Using PC instead\n");
               start = sim_->get_pc();
@@ -641,11 +640,11 @@
           OS::Print("Not at debugger stop.\n");
         }
       } else if (strcmp(cmd, "trace") == 0) {
-        if (FLAG_trace_sim_after == -1) {
+        if (FLAG_trace_sim_after == ULLONG_MAX) {
           FLAG_trace_sim_after = sim_->get_icount();
           OS::Print("execution tracing on\n");
         } else {
-          FLAG_trace_sim_after = -1;
+          FLAG_trace_sim_after = ULLONG_MAX;
           OS::Print("execution tracing off\n");
         }
       } else if (strcmp(cmd, "bt") == 0) {
@@ -850,7 +849,17 @@
         argument_count_(argument_count),
         hlt_instruction_(kRedirectInstruction),
         next_(list_) {
-    list_ = this;
+    // Atomically prepend this element to the front of the global list.
+    // Note: Since elements are never removed, there is no ABA issue.
+    Redirection* list_head = list_;
+    do {
+      next_ = list_head;
+      list_head = reinterpret_cast<Redirection*>(
+          AtomicOperations::CompareAndSwapWord(
+              reinterpret_cast<uword*>(&list_),
+              reinterpret_cast<uword>(next_),
+              reinterpret_cast<uword>(this)));
+    } while (list_head != next_);
   }
 
   uword external_function_;
@@ -1054,10 +1063,7 @@
 
 
 bool Simulator::IsTracingExecution() const {
-  // Integer flag values are signed, so we must cast to unsigned.
-  // The default of -1 hence becomes the maximum unsigned value.
-  return (static_cast<uintptr_t>(icount_) >
-          static_cast<uintptr_t>(FLAG_trace_sim_after));
+  return icount_ > FLAG_trace_sim_after;
 }
 
 
@@ -3297,7 +3303,7 @@
 void Simulator::InstructionDecode(Instr* instr) {
   pc_modified_ = false;
   if (IsTracingExecution()) {
-    OS::Print("%" Pd " ", icount_);
+    OS::Print("%" Pu64, icount_);
     const uword start = reinterpret_cast<uword>(instr);
     const uword end = start + Instr::kInstrSize;
     Disassembler::Disassemble(start, end);
@@ -3330,7 +3336,7 @@
   // raw PC value and not the one used as input to arithmetic instructions.
   uword program_counter = get_pc();
 
-  if (FLAG_stop_sim_at == 0) {
+  if (FLAG_stop_sim_at == ULLONG_MAX) {
     // Fast version of the dispatch loop without checking whether the simulator
     // should be stopping at a particular executed instruction.
     while (program_counter != kEndSimulatingPC) {
@@ -3349,10 +3355,10 @@
     while (program_counter != kEndSimulatingPC) {
       Instr* instr = reinterpret_cast<Instr*>(program_counter);
       icount_++;
-      if (static_cast<intptr_t>(icount_) == FLAG_stop_sim_at) {
+      if (icount_ == FLAG_stop_sim_at) {
         SimulatorDebugger dbg(this);
         dbg.Stop(instr, "Instruction count reached");
-      } else if (reinterpret_cast<intptr_t>(instr) == FLAG_stop_sim_at) {
+      } else if (reinterpret_cast<uint64_t>(instr) == FLAG_stop_sim_at) {
         SimulatorDebugger dbg(this);
         dbg.Stop(instr, "Instruction address reached");
       } else if (IsIllegalAddress(program_counter)) {
diff --git a/runtime/vm/simulator_arm64.h b/runtime/vm/simulator_arm64.h
index 46e6b46..83459bb 100644
--- a/runtime/vm/simulator_arm64.h
+++ b/runtime/vm/simulator_arm64.h
@@ -72,7 +72,7 @@
   uword StackTop() const;
 
   // Accessor to the instruction counter.
-  intptr_t get_icount() const { return icount_; }
+  uint64_t get_icount() const { return icount_; }
 
   // The isolate's top_exit_frame_info refers to a Dart frame in the simulator
   // stack. The simulator's top_exit_frame_info refers to a C++ frame in the
@@ -146,7 +146,7 @@
   int64_t pc_;
   char* stack_;
   bool pc_modified_;
-  intptr_t icount_;
+  uint64_t icount_;
   static int64_t flag_stop_sim_at_;
   SimulatorSetjmpBuffer* last_setjmp_buffer_;
   uword top_exit_frame_info_;
diff --git a/runtime/vm/simulator_mips.cc b/runtime/vm/simulator_mips.cc
index b4db0f1..b665764 100644
--- a/runtime/vm/simulator_mips.cc
+++ b/runtime/vm/simulator_mips.cc
@@ -23,9 +23,9 @@
 
 namespace dart {
 
-DEFINE_FLAG(int, trace_sim_after, -1,
+DEFINE_FLAG(uint64_t, trace_sim_after, ULLONG_MAX,
             "Trace simulator execution after instruction count reached.");
-DEFINE_FLAG(int, stop_sim_at, -1,
+DEFINE_FLAG(uint64_t, stop_sim_at, ULLONG_MAX,
             "Instruction address or instruction count to stop simulator at.");
 
 
@@ -211,10 +211,6 @@
     *value = sim_->get_pc();
     return true;
   }
-  if (strcmp("icount", desc) == 0) {
-    *value = sim_->get_icount();
-    return true;
-  }
   bool retval = SScanF(desc, "0x%x", value) == 1;
   if (!retval) {
     retval = SScanF(desc, "%x", value) == 1;
@@ -471,13 +467,16 @@
       } else if ((strcmp(cmd, "p") == 0) || (strcmp(cmd, "print") == 0)) {
         if (args == 2) {
           uint32_t value;
-          if (GetValue(arg1, &value)) {
+          if (strcmp(arg1, "icount") == 0) {
+            const uint64_t icount = sim_->get_icount();
+            OS::Print("icount: %" Pu64 " 0x%" Px64 "\n", icount, icount);
+          } else if (GetValue(arg1, &value)) {
             OS::Print("%s: %u 0x%x\n", arg1, value, value);
           } else {
             OS::Print("%s unrecognized\n", arg1);
           }
         } else {
-          OS::Print("print <reg or value or *addr>\n");
+          OS::Print("print <reg or icount or value or *addr>\n");
         }
       } else if ((strcmp(cmd, "pf") == 0) ||
                  (strcmp(cmd, "printfloat") == 0)) {
@@ -594,11 +593,11 @@
           OS::Print("Not at debugger stop.\n");
         }
       } else if (strcmp(cmd, "trace") == 0) {
-        if (FLAG_trace_sim_after == -1) {
+        if (FLAG_trace_sim_after == ULLONG_MAX) {
           FLAG_trace_sim_after = sim_->get_icount();
           OS::Print("execution tracing on\n");
         } else {
-          FLAG_trace_sim_after = -1;
+          FLAG_trace_sim_after = ULLONG_MAX;
           OS::Print("execution tracing off\n");
         }
       } else if (strcmp(cmd, "bt") == 0) {
@@ -795,7 +794,17 @@
         argument_count_(argument_count),
         break_instruction_(Instr::kSimulatorRedirectInstruction),
         next_(list_) {
-    list_ = this;
+    // Atomically prepend this element to the front of the global list.
+    // Note: Since elements are never removed, there is no ABA issue.
+    Redirection* list_head = list_;
+    do {
+      next_ = list_head;
+      list_head = reinterpret_cast<Redirection*>(
+          AtomicOperations::CompareAndSwapWord(
+              reinterpret_cast<uword*>(&list_),
+              reinterpret_cast<uword>(next_),
+              reinterpret_cast<uword>(this)));
+    } while (list_head != next_);
   }
 
   uword external_function_;
@@ -996,10 +1005,7 @@
 
 
 bool Simulator::IsTracingExecution() const {
-  // Integer flag values are signed, so we must cast to unsigned.
-  // The default of -1 hence becomes the maximum unsigned value.
-  return (static_cast<uintptr_t>(icount_) >
-          static_cast<uintptr_t>(FLAG_trace_sim_after));
+  return icount_ > FLAG_trace_sim_after;
 }
 
 
@@ -1979,7 +1985,7 @@
 
 void Simulator::InstructionDecode(Instr* instr) {
   if (IsTracingExecution()) {
-    OS::Print("%" Pd " ", icount_);
+    OS::Print("%" Pu64, icount_);
     const uword start = reinterpret_cast<uword>(instr);
     const uword end = start + Instr::kInstrSize;
     Disassembler::Disassemble(start, end);
@@ -2278,11 +2284,11 @@
   delay_slot_ = true;
   icount_++;
   Instr* instr = Instr::At(pc_ + Instr::kInstrSize);
-  if (FLAG_stop_sim_at != -1) {
-    if (static_cast<int>(icount_) == FLAG_stop_sim_at) {
+  if (FLAG_stop_sim_at != ULLONG_MAX) {
+    if (icount_ == FLAG_stop_sim_at) {
       SimulatorDebugger dbg(this);
       dbg.Stop(instr, "Instruction count reached");
-    } else if (reinterpret_cast<int>(instr) == FLAG_stop_sim_at) {
+    } else if (reinterpret_cast<uint64_t>(instr) == FLAG_stop_sim_at) {
       SimulatorDebugger dbg(this);
       dbg.Stop(instr, "Instruction address reached");
     }
@@ -2293,7 +2299,7 @@
 
 
 void Simulator::Execute() {
-  if (FLAG_stop_sim_at == -1) {
+  if (FLAG_stop_sim_at == ULLONG_MAX) {
     // Fast version of the dispatch loop without checking whether the simulator
     // should be stopping at a particular executed instruction.
     while (pc_ != kEndSimulatingPC) {
@@ -2311,10 +2317,10 @@
     while (pc_ != kEndSimulatingPC) {
       Instr* instr = Instr::At(pc_);
       icount_++;
-      if (static_cast<intptr_t>(icount_) == FLAG_stop_sim_at) {
+      if (icount_ == FLAG_stop_sim_at) {
         SimulatorDebugger dbg(this);
         dbg.Stop(instr, "Instruction count reached");
-      } else if (reinterpret_cast<intptr_t>(instr) == FLAG_stop_sim_at) {
+      } else if (reinterpret_cast<uint64_t>(instr) == FLAG_stop_sim_at) {
         SimulatorDebugger dbg(this);
         dbg.Stop(instr, "Instruction address reached");
       } else if (IsIllegalAddress(pc_)) {
diff --git a/runtime/vm/simulator_mips.h b/runtime/vm/simulator_mips.h
index c6641cd..901059dc 100644
--- a/runtime/vm/simulator_mips.h
+++ b/runtime/vm/simulator_mips.h
@@ -91,7 +91,7 @@
   uword StackTop() const;
 
   // Accessor to the instruction counter.
-  intptr_t get_icount() const { return icount_; }
+  uint64_t get_icount() const { return icount_; }
 
   // The isolate's top_exit_frame_info refers to a Dart frame in the simulator
   // stack. The simulator's top_exit_frame_info refers to a C++ frame in the
@@ -159,7 +159,7 @@
 
   // Simulator support.
   char* stack_;
-  intptr_t icount_;
+  uint64_t icount_;
   bool delay_slot_;
   SimulatorSetjmpBuffer* last_setjmp_buffer_;
   uword top_exit_frame_info_;
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 63837bd..26362c9 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -31,6 +31,11 @@
     return reinterpret_cast<Isolate*>(this);
   }
 
+  // The log for this thread.
+  class Log* Log() {
+    return reinterpret_cast<Isolate*>(this)->Log();
+  }
+
  private:
   DISALLOW_COPY_AND_ASSIGN(Thread);
 };
diff --git a/runtime/vm/vm_sources.gypi b/runtime/vm/vm_sources.gypi
index ee38ce6..46f92a3 100644
--- a/runtime/vm/vm_sources.gypi
+++ b/runtime/vm/vm_sources.gypi
@@ -259,6 +259,9 @@
     'locations.cc',
     'locations.h',
     'lockers.h',
+    'log_test.cc',
+    'log.cc',
+    'log.h',
     'longjump.cc',
     'longjump.h',
     'longjump_test.cc',
@@ -334,9 +337,11 @@
     'port_test.cc',
     'proccpuinfo.h',
     'proccpuinfo.cc',
+    'profiler_service.cc',
+    'profiler_service.h',
+    'profiler_test.cc',
     'profiler.cc',
     'profiler.h',
-    'profiler_test.cc',
     'random.cc',
     'random.h',
     'raw_object.cc',
@@ -373,6 +378,7 @@
     'scavenger.cc',
     'scavenger.h',
     'scavenger_test.cc',
+    'scope_timer.h',
     'scopes.cc',
     'scopes.h',
     'scopes_test.cc',
diff --git a/sdk/lib/_internal/compiler/js_lib/foreign_helper.dart b/sdk/lib/_internal/compiler/js_lib/foreign_helper.dart
index 156ab63..96fd0a5 100644
--- a/sdk/lib/_internal/compiler/js_lib/foreign_helper.dart
+++ b/sdk/lib/_internal/compiler/js_lib/foreign_helper.dart
@@ -154,11 +154,6 @@
 void JS_SET_CURRENT_ISOLATE(isolate) {}
 
 /**
- * Creates an isolate and returns it.
- */
-JS_CREATE_ISOLATE() {}
-
-/**
  * Returns the JavaScript constructor function for Dart's Object class.
  * This can be used for type tests, as in
  *
diff --git a/sdk/lib/_internal/compiler/js_lib/io_patch.dart b/sdk/lib/_internal/compiler/js_lib/io_patch.dart
index ee38707..8fbc6a7 100644
--- a/sdk/lib/_internal/compiler/js_lib/io_patch.dart
+++ b/sdk/lib/_internal/compiler/js_lib/io_patch.dart
@@ -359,7 +359,8 @@
   static Future<RawServerSocket> bind(address,
                                       int port,
                                       {int backlog: 0,
-                                       bool v6Only: false}) {
+                                       bool v6Only: false,
+                                       bool shared: false}) {
     throw new UnsupportedError("RawServerSocket.bind");
   }
 }
@@ -370,7 +371,8 @@
   static Future<ServerSocket> bind(address,
                                    int port,
                                    {int backlog: 0,
-                                    bool v6Only: false}) {
+                                    bool v6Only: false,
+                                    bool shared: false}) {
     throw new UnsupportedError("ServerSocket.bind");
   }
 }
@@ -378,7 +380,7 @@
 @patch
 class RawSocket {
   @patch
-  static Future<RawSocket> connect(host, int port) {
+  static Future<RawSocket> connect(host, int port, {sourceAddress}) {
     throw new UnsupportedError("RawSocket constructor");
   }
 }
@@ -386,7 +388,7 @@
 @patch
 class Socket {
   @patch
-  static Future<Socket> connect(host, int port) {
+  static Future<Socket> connect(host, int port, {sourceAddress}) {
     throw new UnsupportedError("Socket constructor");
   }
 }
diff --git a/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart b/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart
index 9936e00..4627da32 100644
--- a/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart
+++ b/sdk/lib/_internal/compiler/js_lib/isolate_helper.dart
@@ -7,6 +7,7 @@
 import 'dart:_js_embedded_names' show
     CLASS_ID_EXTRACTOR,
     CLASS_FIELDS_EXTRACTOR,
+    CREATE_NEW_ISOLATE,
     CURRENT_SCRIPT,
     GLOBAL_FUNCTIONS,
     INITIALIZE_EMPTY_INSTANCE,
@@ -28,7 +29,6 @@
 
 import 'dart:_foreign_helper' show DART_CLOSURE_TO_JS,
                                    JS,
-                                   JS_CREATE_ISOLATE,
                                    JS_CURRENT_ISOLATE_CONTEXT,
                                    JS_CURRENT_ISOLATE,
                                    JS_EMBEDDED_GLOBAL,
@@ -289,7 +289,8 @@
 
   /** Holds isolate globals (statics and top-level properties). */
   // native object containing all globals of an isolate.
-  final isolateStatics = JS_CREATE_ISOLATE();
+  final isolateStatics =
+      JS('', "#()", JS_EMBEDDED_GLOBAL('', CREATE_NEW_ISOLATE));
 
   final RawReceivePortImpl controlPort = new RawReceivePortImpl._controlPort();
 
diff --git a/sdk/lib/_internal/compiler/js_lib/js_helper.dart b/sdk/lib/_internal/compiler/js_lib/js_helper.dart
index f89fe9d..b170538 100644
--- a/sdk/lib/_internal/compiler/js_lib/js_helper.dart
+++ b/sdk/lib/_internal/compiler/js_lib/js_helper.dart
@@ -21,11 +21,18 @@
 import 'dart:collection';
 import 'dart:_isolate_helper' show
     IsolateNatives,
-    leaveJsAsync,
     enterJsAsync,
-    isWorker;
+    isWorker,
+    leaveJsAsync;
 
-import 'dart:async' show Future, DeferredLoadException, Completer;
+import 'dart:async' show
+  Future,
+  DeferredLoadException,
+  Completer,
+  StreamController,
+  Stream,
+  StreamSubscription,
+  scheduleMicrotask;
 
 import 'dart:_foreign_helper' show
     DART_CLOSURE_TO_JS,
@@ -3462,3 +3469,261 @@
 void mainHasTooManyParameters() {
   throw new MainError("'main' expects too many parameters.");
 }
+
+/// Runtime support for async-await transformation.
+///
+/// This function is called by a transformed function on each await and return
+/// in the untransformed function, and before starting.
+///
+/// If [object] is not a future it will be wrapped in a `new Future.value`.
+///
+/// If [helperCallback] is null it indicates a return from the async function,
+/// and we complete the completer with object.
+///
+/// Otherwise [helperCallback] is set up to be called when the future is
+/// successfull and [errorCallback] if it is completed with an error.
+///
+/// If helperCallback or errorCallback throws we complete the completer with the
+/// error.
+///
+/// Returns the future of the completer for convenience of the first call.
+dynamic thenHelper(dynamic object,
+                   dynamic /* js function */ helperCallback,
+                   Completer completer,
+                   dynamic /* js function */ errorCallback) {
+  if (helperCallback == null) {
+    completer.complete(object);
+    return;
+  }
+  Future future = object is Future ? object : new Future.value(object);
+  future.then(_wrapJsFunctionForThenHelper(helperCallback, completer),
+              onError: (errorCallback == null)
+                  ? null
+                  : _wrapJsFunctionForThenHelper(errorCallback, completer));
+  return completer.future;
+}
+
+Function _wrapJsFunctionForThenHelper(dynamic /* js function */ function,
+                                      Completer completer) {
+  return (result) {
+    try {
+      JS('', '#(#)', function, result);
+    } catch (e, st) {
+      completer.completeError(e, st);
+    }
+  };
+}
+
+
+/// Implements the runtime support for async* functions.
+///
+/// Called by the transformed function for each original return, await, yield,
+/// yield* and before starting the function.
+///
+/// When the async* function wants to return it calls this function. with
+/// [helperCallback] == null, the streamHelper takes this as signal to close the
+/// stream.
+///
+/// If the async* function wants to do a yield or yield* it calls this function
+/// with [object] being an [IterationMarker]. In this case [errorCallback] has a
+/// special meaning; it is a callback that will run all enclosing finalizers.
+///
+/// In the case of a yield or yield*, if the stream subscription has been
+/// canceled [errorCallback] is scheduled.
+///
+/// If [object] is a single-yield [IterationMarker], adds the value of the
+/// [IterationMarker] to the stream. If the stream subscription has been
+/// paused, return early. Otherwise schedule the helper function to be
+/// executed again.
+///
+/// If [object] is a yield-star [IterationMarker], starts listening to the
+/// yielded stream, and adds all events and errors to our own controller (taking
+/// care if the subscription has been paused or canceled) - when the sub-stream
+/// is done, schedules [helperCallback] again.
+///
+/// If the async* function wants to do an await it calls this function with
+/// [object] not and [IterationMarker].
+///
+/// If [object] is not a [Future], it is wrapped in a `Future.value`.
+/// The [helperCallback] is called on successfull completion of the
+/// future.
+///
+/// If [helperCallback] or [errorCallback] throws the error is added to the
+/// stream.
+void streamHelper(dynamic object,
+                     dynamic /* js function */ helperCallback,
+                     AsyncStarStreamController controller,
+                     dynamic /* js function */ errorCallback) {
+  if (helperCallback == null) {
+    // This happens on return from the async* function.
+    controller.close();
+    return;
+  }
+
+  if (object is IterationMarker) {
+    if (controller.stopRunning) {
+      _wrapJsFunctionForStream(errorCallback, controller)();
+      return;
+    }
+    if (object.state == IterationMarker.YIELD_SINGLE) {
+      controller.add(object.value);
+      // If the controller is paused we stop producing more values.
+      if (controller.isPaused) {
+        return;
+      }
+      // TODO(sigurdm): We should not suspend here according to the spec.
+      scheduleMicrotask(() {
+          _wrapJsFunctionForStream(helperCallback, controller)(null);
+      });
+      return;
+    } else if (object.state == IterationMarker.YIELD_STAR) {
+      Stream stream = object.value;
+      controller.isAdding = true;
+      // Errors of [stream] are passed though to the main stream. (see
+      // [AsyncStreamController.addStream].
+      // TODO(sigurdm): The spec is not very clear here. Clarify with Gilad.
+      controller.addStream(stream).then((_) {
+        controller.isAdding = false;
+        _wrapJsFunctionForStream(helperCallback, controller)(null);
+      });
+      return;
+    }
+  }
+
+  Future future = object is Future ? object : new Future.value(object);
+  future.then(_wrapJsFunctionForStream(helperCallback, controller),
+              onError: errorCallback == null
+                  ? null
+                  : _wrapJsFunctionForStream(errorCallback, controller));
+}
+
+Stream streamOfController(AsyncStarStreamController controller) {
+  return controller.stream;
+}
+
+/// A wrapper around a [StreamController] that remembers if that controller
+/// got a cancel.
+///
+/// Also has a subSubscription that when not null will provide events for the
+/// stream, and will be paused and resumed along with this controller.
+class AsyncStarStreamController {
+  StreamController controller;
+  Stream get stream => controller.stream;
+  bool stopRunning = false;
+  bool isAdding = false;
+  bool get isPaused => controller.isPaused;
+  add(event) => controller.add(event);
+  addStream(Stream stream) {
+    return controller.addStream(stream, cancelOnError: false);
+  }
+  addError(error, stackTrace) => controller.addError(error, stackTrace);
+  close() => controller.close();
+
+  AsyncStarStreamController(helperCallback) {
+    controller = new StreamController(
+      onListen: () {
+        scheduleMicrotask(() => JS('', '#(null)', helperCallback));
+      },
+      onResume: () {
+        if (!isAdding) {
+          streamHelper(null, helperCallback, this, null);
+        }
+      }, onCancel: () {
+        stopRunning = true;
+      });
+  }
+}
+
+makeAsyncStarController(helperCallback) {
+  return new AsyncStarStreamController(helperCallback);
+}
+
+Function _wrapJsFunctionForStream(dynamic /* js function */ function,
+                                  AsyncStarStreamController controller) {
+  return (result) {
+    try {
+      JS('', '#(#)', function, result);
+    } catch (e, st) {
+      controller.addError(e, st);
+    }
+  };
+}
+
+
+class IterationMarker {
+  static const YIELD_SINGLE = 0;
+  static const YIELD_STAR = 1;
+  static const ITERATION_ENDED = 2;
+
+  final value;
+  final int state;
+
+  IterationMarker._(this.state, this.value);
+
+  static yieldStar(dynamic /* Iterable or Stream */ values) {
+    return new IterationMarker._(YIELD_STAR, values);
+  }
+
+  static endOfIteration() {
+    return new IterationMarker._(ITERATION_ENDED, null);
+  }
+
+  static yieldSingle(dynamic value) {
+    return new IterationMarker._(YIELD_SINGLE, value);
+  }
+
+  toString() => "IterationMarker($state, $value)";
+}
+
+class SyncStarIterator implements Iterator {
+  final Function _helper;
+
+  // If [runningNested] this is the nested iterator, otherwise it is the
+  // current value.
+  dynamic _current = null;
+  bool _runningNested = false;
+
+  get current => _runningNested ? _current.current : _current;
+
+  SyncStarIterator(helper)
+      : _helper = ((arg) => JS('', '#(#)', helper, arg));
+
+  bool moveNext() {
+    if (_runningNested) {
+      if (_current.moveNext()) {
+        return true;
+      } else {
+        _runningNested = false;
+      }
+    }
+    _current = _helper(null);
+    if (_current is IterationMarker) {
+      if (_current.state == IterationMarker.ITERATION_ENDED) {
+        _current = null;
+        // Rely on [_helper] to repeatedly return `ITERATION_ENDED`.
+        return false;
+      } else {
+        assert(_current.state == IterationMarker.YIELD_STAR);
+        _current = _current.value.iterator;
+        _runningNested = true;
+        return moveNext();
+      }
+    }
+    return true;
+  }
+}
+
+/// An Iterable corresponding to a sync* method.
+///
+/// Each invocation of a sync* method will return a new instance of this class.
+class SyncStarIterable extends IterableBase {
+  // This is a function that will return a helper function that does the
+  // iteration of the sync*.
+  //
+  // Each invocation should give a helper with fresh state.
+  final dynamic /* js function */ _outerHelper;
+
+  SyncStarIterable(this._outerHelper);
+
+  Iterator get iterator => new SyncStarIterator(JS('', '#()', _outerHelper));
+}
diff --git a/sdk/lib/_internal/compiler/js_lib/js_number.dart b/sdk/lib/_internal/compiler/js_lib/js_number.dart
index 63afa4b..a94b46b 100644
--- a/sdk/lib/_internal/compiler/js_lib/js_number.dart
+++ b/sdk/lib/_internal/compiler/js_lib/js_number.dart
@@ -366,6 +366,28 @@
     return _bitCount(_spread(nonneg));
   }
 
+  // Returns pow(this, e) % m.
+  int modPow(int e, int m) {
+    if (e is! int) throw new ArgumentError(e);
+    if (m is! int) throw new ArgumentError(m);
+    if (e < 0) throw new RangeError(e);
+    if (m <= 0) throw new RangeError(m);
+    if (e == 0) return 1;
+    int b = this;
+    if (b < 0 || b > m) {
+      b %= m;
+    }
+    int r = 1;
+    while (e > 0) {
+      if (e.isOdd) {
+        r = (r * b) % m;
+      }
+      e ~/= 2;
+      b = (b * b) % m;
+    }
+    return r;
+  }
+
   // Assumes i is <= 32-bit and unsigned.
   static int _bitCount(int i) {
     // See "Hacker's Delight", section 5-1, "Counting 1-Bits".
diff --git a/sdk/lib/_internal/compiler/js_lib/shared/embedded_names.dart b/sdk/lib/_internal/compiler/js_lib/shared/embedded_names.dart
index aa899bd..9ae4f9f 100644
--- a/sdk/lib/_internal/compiler/js_lib/shared/embedded_names.dart
+++ b/sdk/lib/_internal/compiler/js_lib/shared/embedded_names.dart
@@ -44,6 +44,13 @@
 const IS_HUNK_LOADED = 'isHunkLoaded';
 const IS_HUNK_INITIALIZED = 'isHunkInitialized';
 const DEFERRED_INITIALIZED = 'deferredInitialized';
+
+/// Returns a function that creates a new Isolate (its static state).
+///
+/// (floitsch): Note that this will probably go away, since one JS heap will
+/// only contain one Dart isolate.
+const CREATE_NEW_ISOLATE = 'createNewIsolate';
+
 const CLASS_ID_EXTRACTOR = 'classIdExtractor';
 const CLASS_FIELDS_EXTRACTOR = 'classFieldsExtractor';
 const INSTANCE_FROM_CLASS_ID = "instanceFromClassId";
diff --git a/sdk/lib/core/duration.dart b/sdk/lib/core/duration.dart
index 402d289..c5b73a2 100644
--- a/sdk/lib/core/duration.dart
+++ b/sdk/lib/core/duration.dart
@@ -93,11 +93,11 @@
                   int milliseconds: 0,
                   int microseconds: 0})
       : this._microseconds(
-            days * MICROSECONDS_PER_DAY +
-            hours * MICROSECONDS_PER_HOUR +
-            minutes * MICROSECONDS_PER_MINUTE +
-            seconds * MICROSECONDS_PER_SECOND +
-            milliseconds * MICROSECONDS_PER_MILLISECOND +
+            MICROSECONDS_PER_DAY * days +
+            MICROSECONDS_PER_HOUR * hours +
+            MICROSECONDS_PER_MINUTE * minutes +
+            MICROSECONDS_PER_SECOND * seconds +
+            MICROSECONDS_PER_MILLISECOND * milliseconds +
             microseconds);
 
   // Fast path internal direct constructor to avoids the optional arguments and
diff --git a/sdk/lib/core/int.dart b/sdk/lib/core/int.dart
index 1733568..681b8e8 100644
--- a/sdk/lib/core/int.dart
+++ b/sdk/lib/core/int.dart
@@ -103,6 +103,14 @@
    */
   int operator >>(int shiftAmount);
 
+  /**
+   * Returns this integer to the power of [exponent] modulo [modulus].
+   *
+   * The [exponent] must be non-negative and [modulus] must be
+   * positive.
+   */
+  int modPow(int exponent, int modulus);
+
   /** Returns true if and only if this integer is even. */
   bool get isEven;
 
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index d93583c..2e8b119 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -8277,7 +8277,7 @@
    * For details about CSS selector syntax, see the
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
-  ElementList querySelectorAll(String selectors) {
+  ElementList<Element> querySelectorAll(String selectors) {
     return new _FrozenElementList._wrap(_querySelectorAll(selectors));
   }
 
@@ -8297,7 +8297,7 @@
   @deprecated
   @Experimental()
   @DomName('Document.querySelectorAll')
-  ElementList queryAll(String relativeSelectors) =>
+  ElementList<Element> queryAll(String relativeSelectors) =>
       querySelectorAll(relativeSelectors);
 
   /// Checks if [registerElement] is supported on the current platform.
@@ -8384,7 +8384,7 @@
    * For details about CSS selector syntax, see the
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
-  ElementList querySelectorAll(String selectors) =>
+  ElementList<Element> querySelectorAll(String selectors) =>
     new _FrozenElementList._wrap(_querySelectorAll(selectors));
 
 
@@ -8442,7 +8442,7 @@
   @deprecated
   @Experimental()
   @DomName('DocumentFragment.querySelectorAll')
-  ElementList queryAll(String relativeSelectors) {
+  ElementList<Element> queryAll(String relativeSelectors) {
     return querySelectorAll(relativeSelectors);
   }
   // To suppress missing implicit constructor warnings.
@@ -10878,7 +10878,7 @@
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
   @DomName('Element.querySelectorAll')
-  ElementList querySelectorAll(String selectors) =>
+  ElementList<Element> querySelectorAll(String selectors) =>
     new _FrozenElementList._wrap(_querySelectorAll(selectors));
 
   /**
@@ -10897,7 +10897,7 @@
   @deprecated
   @DomName('Element.querySelectorAll')
   @Experimental()
-  ElementList queryAll(String relativeSelectors) =>
+  ElementList<Element> queryAll(String relativeSelectors) =>
       querySelectorAll(relativeSelectors);
 
   /**
@@ -38498,7 +38498,7 @@
  */
 @deprecated
 @Experimental()
-ElementList queryAll(String relativeSelectors) => document.queryAll(relativeSelectors);
+ElementList<Element> queryAll(String relativeSelectors) => document.queryAll(relativeSelectors);
 
 /**
  * Finds the first descendant element of this document that matches the
@@ -38535,7 +38535,7 @@
  * For details about CSS selector syntax, see the
  * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
  */
-ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+ElementList<Element> querySelectorAll(String selectors) => document.querySelectorAll(selectors);
 
 /// A utility for changing the Dart wrapper type for elements.
 abstract class ElementUpgrader {
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index b195b1a..63d97d7 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -9205,7 +9205,7 @@
    * For details about CSS selector syntax, see the
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
-  ElementList querySelectorAll(String selectors) {
+  ElementList<Element> querySelectorAll(String selectors) {
     return new _FrozenElementList._wrap(_querySelectorAll(selectors));
   }
 
@@ -9225,7 +9225,7 @@
   @deprecated
   @Experimental()
   @DomName('Document.querySelectorAll')
-  ElementList queryAll(String relativeSelectors) =>
+  ElementList<Element> queryAll(String relativeSelectors) =>
       querySelectorAll(relativeSelectors);
 
   /// Checks if [registerElement] is supported on the current platform.
@@ -9302,7 +9302,7 @@
    * For details about CSS selector syntax, see the
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
-  ElementList querySelectorAll(String selectors) =>
+  ElementList<Element> querySelectorAll(String selectors) =>
     new _FrozenElementList._wrap(_querySelectorAll(selectors));
 
 
@@ -9360,7 +9360,7 @@
   @deprecated
   @Experimental()
   @DomName('DocumentFragment.querySelectorAll')
-  ElementList queryAll(String relativeSelectors) {
+  ElementList<Element> queryAll(String relativeSelectors) {
     return querySelectorAll(relativeSelectors);
   }
   // To suppress missing implicit constructor warnings.
@@ -11900,7 +11900,7 @@
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
   @DomName('Element.querySelectorAll')
-  ElementList querySelectorAll(String selectors) =>
+  ElementList<Element> querySelectorAll(String selectors) =>
     new _FrozenElementList._wrap(_querySelectorAll(selectors));
 
   /**
@@ -11919,7 +11919,7 @@
   @deprecated
   @DomName('Element.querySelectorAll')
   @Experimental()
-  ElementList queryAll(String relativeSelectors) =>
+  ElementList<Element> queryAll(String relativeSelectors) =>
       querySelectorAll(relativeSelectors);
 
   /**
@@ -40813,7 +40813,7 @@
  */
 @deprecated
 @Experimental()
-ElementList queryAll(String relativeSelectors) => document.queryAll(relativeSelectors);
+ElementList<Element> queryAll(String relativeSelectors) => document.queryAll(relativeSelectors);
 
 /**
  * Finds the first descendant element of this document that matches the
@@ -40850,7 +40850,7 @@
  * For details about CSS selector syntax, see the
  * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
  */
-ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+ElementList<Element> querySelectorAll(String selectors) => document.querySelectorAll(selectors);
 
 /// A utility for changing the Dart wrapper type for elements.
 abstract class ElementUpgrader {
diff --git a/sdk/lib/io/secure_server_socket.dart b/sdk/lib/io/secure_server_socket.dart
index 1a0bf6e..2a73418 100644
--- a/sdk/lib/io/secure_server_socket.dart
+++ b/sdk/lib/io/secure_server_socket.dart
@@ -67,7 +67,8 @@
        bool v6Only: false,
        bool requestClientCertificate: false,
        bool requireClientCertificate: false,
-       List<String> supportedProtocols}) {
+       List<String> supportedProtocols,
+       bool shared: false}) {
     return RawSecureServerSocket.bind(
         address,
         port,
@@ -76,7 +77,8 @@
         v6Only: v6Only,
         requestClientCertificate: requestClientCertificate,
         requireClientCertificate: requireClientCertificate,
-        supportedProtocols: supportedProtocols).then(
+        supportedProtocols: supportedProtocols,
+        shared: shared).then(
             (serverSocket) => new SecureServerSocket._(serverSocket));
   }
 
@@ -192,8 +194,10 @@
        bool v6Only: false,
        bool requestClientCertificate: false,
        bool requireClientCertificate: false,
-       List<String> supportedProtocols}) {
-    return RawServerSocket.bind(address, port, backlog: backlog, v6Only: v6Only)
+       List<String> supportedProtocols,
+       bool shared: false}) {
+    return RawServerSocket.bind(
+        address, port, backlog: backlog, v6Only: v6Only, shared: shared)
         .then((serverSocket) => new RawSecureServerSocket._(
             serverSocket,
             certificateName,
diff --git a/sdk/lib/io/socket.dart b/sdk/lib/io/socket.dart
index b95163c..08f973e 100644
--- a/sdk/lib/io/socket.dart
+++ b/sdk/lib/io/socket.dart
@@ -227,7 +227,8 @@
   external static Future<RawServerSocket> bind(address,
                                                int port,
                                                {int backlog: 0,
-                                                bool v6Only: false});
+                                                bool v6Only: false,
+                                                bool shared: false});
 
   /**
    * Returns the port used by this socket.
@@ -318,7 +319,8 @@
   external static Future<ServerSocket> bind(address,
                                             int port,
                                             {int backlog: 0,
-                                             bool v6Only: false});
+                                             bool v6Only: false,
+                                             bool shared: false});
 
   /**
    * Returns the port used by this socket.
@@ -453,8 +455,13 @@
    * all returned [InternetAddress]es, until connected. Unless a
    * connection was established, the error from the first failing connection is
    * returned.
+   *
+   * The argument [sourceAddress] can be used to specify the local
+   * address to bind when making the connection. `sourceAddress` can either
+   * be a `String` or an `InternetAddress`. If a `String` is passed it must
+   * hold a numeric IP address.
    */
-  external static Future<RawSocket> connect(host, int port);
+  external static Future<RawSocket> connect(host, int port, {sourceAddress});
 
   /**
    * Returns the number of received and non-read bytes in the socket that
@@ -546,8 +553,13 @@
    * all returned [InternetAddress]es, until connected. Unless a
    * connection was established, the error from the first failing connection is
    * returned.
+   *
+   * The argument [sourceAddress] can be used to specify the local
+   * address to bind when making the connection. `sourceAddress` can either
+   * be a `String` or an `InternetAddress`. If a `String` is passed it must
+   * hold a numeric IP address.
    */
-  external static Future<Socket> connect(host, int port);
+  external static Future<Socket> connect(host, int port, {sourceAddress});
 
   /**
    * Destroy the socket in both directions. Calling [destroy] will make the
diff --git a/sdk/lib/io/websocket.dart b/sdk/lib/io/websocket.dart
index 60414da..599e295 100644
--- a/sdk/lib/io/websocket.dart
+++ b/sdk/lib/io/websocket.dart
@@ -147,6 +147,9 @@
    *   - `upgrade`
    *
    * If any of these are passed in the `headers` map they will be ignored.
+   *
+   * If the `url` contains user information this will be passed as basic
+   * authentication when setting up the connection.
    */
   static Future<WebSocket> connect(String url,
                                    {Iterable<String> protocols,
diff --git a/sdk/lib/io/websocket_impl.dart b/sdk/lib/io/websocket_impl.dart
index ae4c91c..6e30809 100644
--- a/sdk/lib/io/websocket_impl.dart
+++ b/sdk/lib/io/websocket_impl.dart
@@ -775,9 +775,6 @@
     if (uri.scheme != "ws" && uri.scheme != "wss") {
       throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'");
     }
-    if (uri.userInfo != "") {
-      throw new WebSocketException("Unsupported user info '${uri.userInfo}'");
-    }
 
     Random random = new Random();
     // Generate 16 random bytes.
@@ -796,6 +793,13 @@
                   fragment: uri.fragment);
     return _httpClient.openUrl("GET", uri)
       .then((request) {
+        if (uri.userInfo != null && !uri.userInfo.isEmpty) {
+          // If the URL contains user information use that for basic
+          // authorization.
+          String auth =
+          _CryptoUtils.bytesToBase64(UTF8.encode(uri.userInfo));
+          request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth");
+        }
         if (headers != null) {
           headers.forEach((field, value) => request.headers.add(field, value));
         }
diff --git a/tests/co19/co19-analyzer.status b/tests/co19/co19-analyzer.status
index f940b1a..40521c1 100644
--- a/tests/co19/co19-analyzer.status
+++ b/tests/co19/co19-analyzer.status
@@ -4,6 +4,9 @@
 
 [ $compiler == dartanalyzer ]
 
+WebPlatformTest/html/semantics/forms/the-textarea-element/textarea-type_t01: fail
+LayoutTests/fast/forms/checkValidity-001_t01: fail
+
 # TBF: infinite look: class A {const A();final m = const A();}
 Language/12_Expressions/01_Constants_A17_t03: fail
 
diff --git a/tests/co19/co19-analyzer2.status b/tests/co19/co19-analyzer2.status
index 4404570..8807107 100644
--- a/tests/co19/co19-analyzer2.status
+++ b/tests/co19/co19-analyzer2.status
@@ -4,6 +4,9 @@
 
 [ $compiler == dart2analyzer ]
 
+WebPlatformTest/html/semantics/forms/the-textarea-element/textarea-type_t01: fail
+LayoutTests/fast/forms/checkValidity-001_t01: fail
+
 # TBF: infinite look: class A {const A();final m = const A();}
 Language/12_Expressions/01_Constants_A17_t03: fail
 
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 42e1478..a9a64c2 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -406,6 +406,7 @@
 LayoutTests/fast/forms/datetimelocal/datetimelocal-interactive-validation-required_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/form-submission-create-crash_t01.dart: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/formmethod-attribute-button-html_t01: Skip # Test reloads itself. Issue 18558.
+LayoutTests/fast/forms/formmethod-attribute-input-html_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/formmethod-attribute-input-2_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/missing-action_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/submit-form-with-dirname-attribute_t01: Skip # Test reloads itself. Issue 18558.
@@ -732,6 +733,7 @@
 LayoutTests/fast/dom/horizontal-scrollbar-in-rtl_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/horizontal-scrollbar-when-dir-change_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/implementation-createHTMLDocument_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/dom/location-hash_t01: Pass, RuntimeError # Please triage this failure
 LayoutTests/fast/dom/navigatorcontentutils/is-protocol-handler-registered_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/option-properties_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/partial-layout-overlay-scrollbars_t01: RuntimeError # Please triage this failure
@@ -2792,7 +2794,7 @@
 WebPlatformTest/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/attributes-common-to-form-controls/formaction_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/textfieldselection/selection-not-application-textarea_t01: RuntimeError # Please triage this failure
-WebPlatformTest/html/semantics/forms/textfieldselection/selection_t01: RuntimeError # Please triage this failure
+WebPlatformTest/html/semantics/forms/textfieldselection/selection_t01: Skip # Times out. Please triage this failure
 WebPlatformTest/html/semantics/forms/textfieldselection/textfieldselection-setRangeText_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/the-button-element/button-validation_t01: RuntimeError # Please triage this failure
@@ -6276,7 +6278,7 @@
 LayoutTests/fast/dom/createElement_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/css-delete-doc_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/css-innerHTML_t01: RuntimeError # Please triage this failure
-LayoutTests/fast/dom/css-insert-import-rule-twice_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/dom/css-insert-import-rule-twice_t01: Pass, RuntimeError # Please triage this failure
 LayoutTests/fast/dom/css-insert-import-rule_t01: Pass, RuntimeError # Please triage this failure
 LayoutTests/fast/dom/css-mediarule-functions_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/css-selectorText_t01: RuntimeError # Please triage this failure
@@ -6854,8 +6856,9 @@
 LayoutTests/fast/text/find-russian_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/text/find-soft-hyphen_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/text/find-spaces_t01: RuntimeError # Please triage this failure
-LayoutTests/fast/text/font-ligatures-linebreak-word_t01: RuntimeError # Please triage this failure
-LayoutTests/fast/text/font-ligatures-linebreak_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/text/font-ligature-letter-spacing_t01: Pass, RuntimeError # Please triage this failure
+LayoutTests/fast/text/font-ligatures-linebreak-word_t01: Pass, RuntimeError # Please triage this failure
+LayoutTests/fast/text/font-ligatures-linebreak_t01: Pass, RuntimeError # Please triage this failure
 LayoutTests/fast/text/international/cjk-segmentation_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/text/international/iso-8859-8_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/text/international/listbox-width-rtl_t01: RuntimeError # Please triage this failure
@@ -8154,7 +8157,7 @@
 LayoutTests/fast/dom/HTMLOutputElement/htmloutputelement-value_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/HTMLOutputElement/htmloutputelement_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/HTMLProgressElement/set-progress-properties_t01: RuntimeError # Please triage this failure
-LayoutTests/fast/dom/HTMLScriptElement/async-false-inside-async-false-load_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/dom/HTMLScriptElement/async-false-inside-async-false-load_t01: Pass, RuntimeError # Please triage this failure
 LayoutTests/fast/dom/HTMLScriptElement/async-inline-script_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/dom/HTMLScriptElement/async-onbeforeload_t01: Skip # Times out. Please triage this failure
 LayoutTests/fast/dom/HTMLScriptElement/defer-inline-script_t01: RuntimeError # Please triage this failure
@@ -8615,7 +8618,7 @@
 LayoutTests/fast/forms/input-widths_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/forms/listbox-select-all_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/forms/listbox-selection-2_t01: RuntimeError # Please triage this failure
-LayoutTests/fast/forms/menulist-disabled-selected-option_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/forms/menulist-disabled-selected-option_t01: Pass, RuntimeError # Please triage this failure
 LayoutTests/fast/forms/menulist-selection-reset_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/forms/menulist-submit-without-selection_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/forms/method-attribute_t01: RuntimeError # Please triage this failure
@@ -8699,7 +8702,7 @@
 LayoutTests/fast/loader/about-blank-hash-kept_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/loader/hashchange-event-properties_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/loader/loadInProgress_t01: RuntimeError # Please triage this failure
-LayoutTests/fast/loader/local-css-allowed-in-strict-mode_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/loader/local-css-allowed-in-strict-mode_t01: Pass, RuntimeError # Please triage this failure
 LayoutTests/fast/loader/onhashchange-attribute-listeners_t01: Skip # Times out. Please triage this failure
 LayoutTests/fast/loader/scroll-position-restored-on-back_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/loader/scroll-position-restored-on-reload-at-load-event_t01: RuntimeError # Please triage this failure
@@ -9256,7 +9259,7 @@
 WebPlatformTest/html/semantics/forms/attributes-common-to-form-controls/formAction_document_address_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/attributes-common-to-form-controls/formaction_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/textfieldselection/selection-not-application-textarea_t01: RuntimeError # Please triage this failure
-WebPlatformTest/html/semantics/forms/textfieldselection/selection_t01: RuntimeError # Please triage this failure
+WebPlatformTest/html/semantics/forms/textfieldselection/selection_t01: Skip # Times out. Please triage this failure
 WebPlatformTest/html/semantics/forms/textfieldselection/textfieldselection-setRangeText_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/textfieldselection/textfieldselection-setSelectionRange_t01: RuntimeError # Please triage this failure
 WebPlatformTest/html/semantics/forms/the-button-element/button-validation_t01: RuntimeError # Please triage this failure
diff --git a/tests/co19/co19-dartium.status b/tests/co19/co19-dartium.status
index b254d09..758d027 100644
--- a/tests/co19/co19-dartium.status
+++ b/tests/co19/co19-dartium.status
@@ -12,7 +12,6 @@
 LayoutTests/fast/writing-mode/flipped-blocks-hit-test-overflow_t01: Pass, RuntimeError # Issue 21605
 
 [ $compiler == none && ($runtime == dartium || $runtime == ContentShellOnAndroid) ]
-LayoutTests/fast/canvas/canvas-lineDash-input-sequence_t01: Skip # Issue 20867
 LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-video-rgba4444_t01: Skip # Issue 20540
 LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-video-rgb565_t01: Skip # Issue 20540
 LayoutTests/fast/canvas/webgl/tex-image-and-sub-image-2d-with-video-rgba5551_t01: Skip # Issue 20540
@@ -52,6 +51,7 @@
 LayoutTests/fast/forms/datetimelocal/datetimelocal-interactive-validation-required_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/form-submission-create-crash_t01.dart: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/formmethod-attribute-button-html_t01: Skip # Test reloads itself. Issue 18558.
+LayoutTests/fast/forms/formmethod-attribute-input-html_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/formmethod-attribute-input-2_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/missing-action_t01: Skip # Test reloads itself. Issue 18558.
 LayoutTests/fast/forms/submit-form-with-dirname-attribute_t01: Skip # Test reloads itself. Issue 18558.
@@ -476,7 +476,6 @@
 LayoutTests/fast/canvas/webgl/renderbuffer-initialization_t01: RuntimeError, Pass # co19-roll r761: Please triage this failure.
 LayoutTests/fast/canvas/webgl/triangle_t01: RuntimeError, Pass # co19-roll r761: Please triage this failure.
 LayoutTests/fast/canvas/webgl/uniform-location_t01: RuntimeError # co19-roll r761: Please triage this failure.
-LayoutTests/fast/canvas/winding-enumeration_t01: RuntimeError # co19-roll r761: Please triage this failure.
 LayoutTests/fast/css/add-remove-stylesheets-at-once-minimal-recalc-style_t01: RuntimeError # co19-roll r761: Please triage this failure.
 LayoutTests/fast/css/checked-pseudo-selector_t01: RuntimeError # co19-roll r761: Please triage this failure.
 LayoutTests/fast/css/collapsed-whitespace-reattach-in-style-recalc_t01: RuntimeError # co19-roll r761: Please triage this failure.
@@ -1043,12 +1042,14 @@
 LayoutTests/fast/text/international/listbox-width-rtl_t01: RuntimeError # co19-roll r786: Please triage this failure.
 LayoutTests/fast/text-autosizing/text-removal_t01: RuntimeError # co19-roll r786: Please triage this failure.
 LayoutTests/fast/transforms/scrollIntoView-transformed_t01: RuntimeError # co19-roll r786: Please triage this failure.
+LayoutTests/fast/dom/HTMLElement/innerHTML-selection-crash_t01: Skip # Times out.  Please triage this failure.
 LayoutTests/fast/dom/partial-layout-overlay-scrollbars_t01: Pass, RuntimeError # Please triage this failure.
 LayoutTests/fast/dom/partial-layout-non-overlay-scrollbars_t01: Pass, RuntimeError # Please triage this failure.
 LayoutTests/fast/dom/shadow/shadowdom-for-input-type-change_t01: RuntimeError # Please triage this failure.
 LayoutTests/fast/dom/jsDevicePixelRatio_t01: RuntimeError # Please triage this failure.
 LayoutTests/fast/dom/HTMLLinkElement/prefetch-beforeload_t01: Pass, RuntimeError # Please triage this failure.
 LayoutTests/fast/forms/activate-and-disabled-elements_t01: Skip # Times out. co19-roll r801: Please triage this failure.
+LayoutTests/fast/forms/text-set-value-crash_t01: Skip # Times out. Please triage this failure.
 LayoutTests/fast/inline/continuation-inlines-inserted-in-reverse-after-block_t01: Skip # Times out. co19-roll r801: Please triage this failure.
 LayoutTests/fast/inline/fixed-pos-moves-with-abspos-inline-parent_t01: Skip # Times out. co19-roll r801: Please triage this failure.
 LayoutTests/fast/inline/fixed-pos-moves-with-abspos-parent_t01: Skip # Times out. co19-roll r801: Please triage this failure.
diff --git a/tests/co19/co19-runtime.status b/tests/co19/co19-runtime.status
index 54503e7..7f50f90 100644
--- a/tests/co19/co19-runtime.status
+++ b/tests/co19/co19-runtime.status
@@ -82,3 +82,6 @@
 LibTest/html/*: SkipByDesign # dart:html not supported on VM.
 LayoutTests/fast/*: SkipByDesign # DOM not supported on VM.
 WebPlatformTest/*: SkipByDesign # dart:html not supported on VM.
+
+[ $runtime == vm && $mode == debug && $builder_tag == asan ]
+Language/15_Types/4_Interface_Types_A11_t01: Skip  # Issue 21174.
diff --git a/tests/compiler/dart2js/analyze_helper.dart b/tests/compiler/dart2js/analyze_helper.dart
index 62de389..b901595 100644
--- a/tests/compiler/dart2js/analyze_helper.dart
+++ b/tests/compiler/dart2js/analyze_helper.dart
@@ -154,7 +154,8 @@
       currentDirectory.resolveUri(new Uri.file('${Platform.packageRoot}/'));
   var provider = new CompilerSourceFileProvider();
   var handler = new CollectingDiagnosticHandler(whiteList, provider);
-  var options = <String>['--analyze-only', '--categories=Client,Server'];
+  var options = <String>['--analyze-only', '--categories=Client,Server',
+    '--show-package-warnings'];
   if (analyzeAll) options.add('--analyze-all');
   var compiler = new Compiler(
       provider.readStringFromUri,
diff --git a/tests/compiler/dart2js/async_await_js_transform_test.dart b/tests/compiler/dart2js/async_await_js_transform_test.dart
new file mode 100644
index 0000000..1006186
--- /dev/null
+++ b/tests/compiler/dart2js/async_await_js_transform_test.dart
@@ -0,0 +1,949 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import "package:compiler/src/js/js.dart";
+import "package:compiler/src/js/rewrite_async.dart";
+
+import "backend_dart/dart_printer_test.dart" show PrintDiagnosticListener;
+
+void testTransform(String source, String expected) {
+  Fun fun = js(source);
+  Fun rewritten = new AsyncRewriter(
+      null, // The diagnostic helper should not be used in these tests.
+      null,
+      thenHelper: new VariableUse("thenHelper"),
+      newCompleter: new VariableUse("Completer"),
+      endOfIteration: new VariableUse("endOfIteration"),
+      newIterable: new VariableUse("Iterator"),
+      safeVariableName: (String name) => "__$name").rewrite(fun);
+ 
+  JavaScriptPrintingOptions options = new JavaScriptPrintingOptions();
+  JavaScriptPrintingContext context = new SimpleJavaScriptPrintingContext();
+  Printer printer = new Printer(options, context);
+  printer.visit(rewritten);
+  Expect.stringEquals(expected, context.getText());
+}
+
+main() {
+  testTransform("""
+function() async {
+  print(this.x); // Ensure `this` is translated in the helper function.
+  await foo();
+}""", """
+function() {
+  var __goto = 0, __completer = new Completer(), __self = this;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+          print(__self.x);
+          __goto = 1;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 1:
+          // returning from await.
+          // implicit return
+          return thenHelper(null, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+
+  testTransform("""
+function() async {
+  try {
+    __outer: while (true) { // Overlapping label name.
+      try {
+        inner: while (true) {
+          break __outer; // Break from untranslated loop to translated target.
+          break; // break to untranslated target.
+        }
+        while (true) {
+          return; // Return via finallies.
+        }
+        var __helper = await foo(); // Overlapping variable name.
+      } finally {
+        foo();
+        continue; // Continue from finally with pending finally.
+        return 2; // Return from finally with pending finally.
+      }
+    }
+  } finally {
+    return 3; // Return from finally with no pending finally.
+  }
+  return 4;
+}""", """
+function() {
+  var __goto = 0, __completer = new Completer(), __handler = null, __next, __returnValue, __helper;
+  function __helper1(__result) {
+    while (true)
+      try {
+        __outer1:
+          switch (__goto) {
+            case 0:
+              // Function start
+              __handler = 2;
+            case 6:
+              // continue __outer
+            case 7:
+              // while condition
+              __handler = 9;
+              inner: {
+                while (true) {
+                  __next = [5];
+                  // goto finally
+                  __goto = 10;
+                  break __outer1;
+                  break;
+                }
+              }
+              while (true) {
+                __next = [1, 3];
+                // goto finally
+                __goto = 10;
+                break __outer1;
+              }
+              __goto = 12;
+              return thenHelper(foo(), __helper1, __completer, function(__error) {
+                __goto = 9;
+                __helper1(__error);
+              });
+            case 12:
+              // returning from await.
+              __helper = __result;
+              __next = [11];
+              // goto finally
+              __goto = 10;
+              break;
+            case 9:
+              // catch
+              __handler = 2;
+              __next = [11];
+            case 10:
+              // finally
+              __handler = 2;
+              foo();
+              // goto while condition
+              __goto = 7;
+              break;
+              __returnValue = 2;
+              __next = [1];
+              // goto finally
+              __goto = 3;
+              break;
+              // goto the next finally handler
+              __goto = __next.pop();
+              break;
+            case 11:
+              // after finally
+              // goto while condition
+              __goto = 7;
+              break;
+            case 8:
+              // after while
+            case 5:
+              // break __outer
+              __next = [4];
+              // goto finally
+              __goto = 3;
+              break;
+            case 2:
+              // catch
+              __handler = null;
+              __next = [4];
+            case 3:
+              // finally
+              __handler = null;
+              __returnValue = 3;
+              // goto return
+              __goto = 1;
+              break;
+              // goto the next finally handler
+              __goto = __next.pop();
+              break;
+            case 4:
+              // after finally
+              __returnValue = 4;
+              // goto return
+              __goto = 1;
+              break;
+            case 1:
+              // return
+              return thenHelper(__returnValue, null, __completer, null);
+          }
+      } catch (__error) {
+        if (__handler === null)
+          throw __error;
+        __result = __error;
+        __goto = __handler;
+      }
+
+  }
+  return thenHelper(null, __helper1, __completer, null);
+}""");
+  testTransform("""
+function() async {
+  var a, b, c, d, e, f;
+  a = b++; // post- and preincrements.
+  b = --b;
+  c = (await foo()).a++;
+  d = ++(await foo()).a;
+  e = foo1()[await foo2()]--;
+  f = --foo1()[await foo2()];
+}""", """
+function() {
+  var __goto = 0, __completer = new Completer(), a, b, c, d, e, f, __temp1;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+          a = b++;
+          b = --b;
+          __goto = 1;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 1:
+          // returning from await.
+          c = __result.a++;
+          __goto = 2;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 2:
+          // returning from await.
+          d = ++__result.a;
+          __temp1 = foo1();
+          __goto = 3;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 3:
+          // returning from await.
+          e = __temp1[__result]--;
+          __temp1 = foo1();
+          __goto = 4;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 4:
+          // returning from await.
+          f = --__temp1[__result];
+          // implicit return
+          return thenHelper(null, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+  testTransform("""
+function() async {
+  var a, b, c, d, e, f, g, h; // empty initializer
+  a = foo1() || await foo2(); // short circuiting operators
+  b = await foo1() || foo2();
+  c = await foo1() || foo3(await foo2());
+  d = foo1() || foo2();
+  e = foo1() && await foo2();
+  f = await foo1() && foo2();
+  g = await foo1() && await foo2();
+  h = foo1() && foo2();
+}""", """
+function() {
+  var __goto = 0, __completer = new Completer(), a, b, c, d, e, f, g, h, __temp1;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+          __temp1 = foo1();
+          if (__temp1) {
+            // goto then
+            __goto = 1;
+            break;
+          } else
+            __result = __temp1;
+          // goto join
+          __goto = 2;
+          break;
+        case 1:
+          // then
+          __goto = 3;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 3:
+          // returning from await.
+        case 2:
+          // join
+          a = __result;
+          __goto = 4;
+          return thenHelper(foo1(), __helper, __completer, null);
+        case 4:
+          // returning from await.
+          b = __result || foo2();
+          __goto = 7;
+          return thenHelper(foo1(), __helper, __completer, null);
+        case 7:
+          // returning from await.
+          __temp1 = __result;
+          if (__temp1) {
+            // goto then
+            __goto = 5;
+            break;
+          } else
+            __result = __temp1;
+          // goto join
+          __goto = 6;
+          break;
+        case 5:
+          // then
+          __temp1 = foo3;
+          __goto = 8;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 8:
+          // returning from await.
+          __result = __temp1(__result);
+        case 6:
+          // join
+          c = __result;
+          d = foo1() || foo2();
+          __temp1 = foo1();
+          if (__temp1)
+            __result = __temp1;
+          else {
+            // goto then
+            __goto = 9;
+            break;
+          }
+          // goto join
+          __goto = 10;
+          break;
+        case 9:
+          // then
+          __goto = 11;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 11:
+          // returning from await.
+        case 10:
+          // join
+          e = __result;
+          __goto = 12;
+          return thenHelper(foo1(), __helper, __completer, null);
+        case 12:
+          // returning from await.
+          f = __result && foo2();
+          __goto = 15;
+          return thenHelper(foo1(), __helper, __completer, null);
+        case 15:
+          // returning from await.
+          __temp1 = __result;
+          if (__temp1)
+            __result = __temp1;
+          else {
+            // goto then
+            __goto = 13;
+            break;
+          }
+          // goto join
+          __goto = 14;
+          break;
+        case 13:
+          // then
+          __goto = 16;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 16:
+          // returning from await.
+        case 14:
+          // join
+          g = __result;
+          h = foo1() && foo2();
+          // implicit return
+          return thenHelper(null, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+  testTransform("""
+function(x, y) async {
+  while (true) {
+    switch(y) { // Switch with no awaits in case key expressions
+      case 0:
+      case 1: 
+        await foo();
+        continue; // Continue the loop, not the switch
+      case 1: // Duplicate case
+        await foo();
+        break; // Break the switch
+      case 2:
+        foo(); // No default
+    }
+  }
+}""", """
+function(x, y) {
+  var __goto = 0, __completer = new Completer();
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+        case 1:
+          // while condition
+        case 3:
+          // switch
+          switch (y) {
+            case 0:
+              // goto case
+              __goto = 5;
+              break;
+            case 1:
+              // goto case
+              __goto = 6;
+              break;
+            case 1:
+              // goto case
+              __goto = 7;
+              break;
+            case 2:
+              // goto case
+              __goto = 8;
+              break;
+          }
+          // goto after switch
+          __goto = 4;
+          break;
+        case 5:
+          // case
+        case 6:
+          // case
+          __goto = 9;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 9:
+          // returning from await.
+          // goto while condition
+          __goto = 1;
+          break;
+        case 7:
+          // case
+          __goto = 10;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 10:
+          // returning from await.
+          // goto after switch
+          __goto = 4;
+          break;
+        case 8:
+          // case
+          foo();
+        case 4:
+          // after switch
+          // goto while condition
+          __goto = 1;
+          break;
+        case 2:
+          // after while
+          // implicit return
+          return thenHelper(null, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+  testTransform("""
+function() async {
+  do {
+    var a = await foo();
+    if (a) // If with no awaits in body
+      break;
+    else
+      continue;
+  } while (await foo());
+}
+""", """
+function() {
+  var __goto = 0, __completer = new Completer(), a;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+        case 1:
+          // do body
+          __goto = 4;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 4:
+          // returning from await.
+          a = __result;
+          if (a) {
+            // goto after do
+            __goto = 3;
+            break;
+          } else {
+            // goto do condition
+            __goto = 2;
+            break;
+          }
+        case 2:
+          // do condition
+          __goto = 5;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 5:
+          // returning from await.
+          if (__result) {
+            // goto do body
+            __goto = 1;
+            break;
+          }
+        case 3:
+          // after do
+          // implicit return
+          return thenHelper(null, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+
+  testTransform("""
+function() async {
+  for (var i = 0; i < await foo1(); i += await foo2()) {
+    if (foo(i))
+      continue;
+    else
+      break;
+    if (!foo(i)) { // If with no else and await in body.
+      await foo();
+      return;
+    }
+    print(await(foo(i)));
+  } 
+}
+""", """
+function() {
+  var __goto = 0, __completer = new Completer(), __returnValue, i, __temp1;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+          i = 0;
+        case 2:
+          // for condition
+          __temp1 = i;
+          __goto = 5;
+          return thenHelper(foo1(), __helper, __completer, null);
+        case 5:
+          // returning from await.
+          if (!(__temp1 < __result)) {
+            // goto after for
+            __goto = 4;
+            break;
+          }
+          if (foo(i)) {
+            // goto for update
+            __goto = 3;
+            break;
+          } else {
+            // goto after for
+            __goto = 4;
+            break;
+          }
+          __goto = !foo(i) ? 6 : 7;
+          break;
+        case 6:
+          // then
+          __goto = 8;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 8:
+          // returning from await.
+          // goto return
+          __goto = 1;
+          break;
+        case 7:
+          // join
+          __temp1 = print;
+          __goto = 9;
+          return thenHelper(foo(i), __helper, __completer, null);
+        case 9:
+          // returning from await.
+          __temp1(__result);
+        case 3:
+          // for update
+          __goto = 10;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 10:
+          // returning from await.
+          i = __result;
+          // goto for condition
+          __goto = 2;
+          break;
+        case 4:
+          // after for
+        case 1:
+          // return
+          return thenHelper(__returnValue, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+
+  testTransform("""
+function(a) async {
+  var x = {"a": foo1(), "b": await foo2(), "c": foo3()};
+  x["a"] = 2; // Different assignments
+  (await foo()).a = 3;
+  x[await foo()] = 4;
+  x[(await foo1()).a = await foo2()] = 5;
+  (await foo1())[await foo2()] = await foo3(6);
+}
+""", """
+function(a) {
+  var __goto = 0, __completer = new Completer(), x, __temp1, __temp2;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+          __temp1 = foo1();
+          __goto = 1;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 1:
+          // returning from await.
+          x = {a: __temp1, b: __result, c: foo3()};
+          x.a = 2;
+          __goto = 2;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 2:
+          // returning from await.
+          __result.a = 3;
+          __temp1 = x;
+          __goto = 3;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 3:
+          // returning from await.
+          __temp1[__result] = 4;
+          __temp1 = x;
+          __goto = 4;
+          return thenHelper(foo1(), __helper, __completer, null);
+        case 4:
+          // returning from await.
+          __temp2 = __result;
+          __goto = 5;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 5:
+          // returning from await.
+          __temp1[__temp2.a = __result] = 5;
+          __goto = 6;
+          return thenHelper(foo1(), __helper, __completer, null);
+        case 6:
+          // returning from await.
+          __temp1 = __result;
+          __goto = 7;
+          return thenHelper(foo2(), __helper, __completer, null);
+        case 7:
+          // returning from await.
+          __temp2 = __result;
+          __goto = 8;
+          return thenHelper(foo3(6), __helper, __completer, null);
+        case 8:
+          // returning from await.
+          __temp1[__temp2] = __result;
+          // implicit return
+          return thenHelper(null, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+  testTransform("""
+function(c) async {
+  try {
+    var x = c ? await foo() : foo(); // conditional
+    var y = {};
+  } catch (error) {
+    try {
+      x = c ? await fooError(error) : fooError(error);
+    } catch (error) { // nested error handler with overlapping name
+      y.x = foo(error);
+    } finally {
+      foo(x);
+    }
+  }
+}
+""", """
+function(c) {
+  var __goto = 0, __completer = new Completer(), __handler = null, x, y, __error1, __error2;
+  function __helper(__result) {
+    while (true)
+      try {
+        switch (__goto) {
+          case 0:
+            // Function start
+            __handler = 1;
+            __goto = c ? 4 : 6;
+            break;
+          case 4:
+            // then
+            __goto = 7;
+            return thenHelper(foo(), __helper, __completer, function(__error) {
+              __goto = 1;
+              __helper(__error);
+            });
+          case 7:
+            // returning from await.
+            // goto join
+            __goto = 5;
+            break;
+          case 6:
+            // else
+            __result = foo();
+          case 5:
+            // join
+            x = __result;
+            y = {};
+            __next = [3];
+            __handler = null;
+            // goto after finally
+            __goto = 3;
+            break;
+          case 1:
+            // catch
+            __handler = null;
+            __error1 = __result;
+            __handler = 8;
+            __goto = c ? 11 : 13;
+            break;
+          case 11:
+            // then
+            __goto = 14;
+            return thenHelper(fooError(__error1), __helper, __completer, function(__error) {
+              __goto = 8;
+              __helper(__error);
+            });
+          case 14:
+            // returning from await.
+            // goto join
+            __goto = 12;
+            break;
+          case 13:
+            // else
+            __result = fooError(__error1);
+          case 12:
+            // join
+            x = __result;
+            __next = [10];
+            // goto finally
+            __goto = 9;
+            break;
+          case 8:
+            // catch
+            __handler = null;
+            __error2 = __result;
+            y.x = foo(__error2);
+            __handler = null;
+            __next = [10];
+          case 9:
+            // finally
+            __handler = null;
+            foo(x);
+            // goto the next finally handler
+            __goto = __next.pop();
+            break;
+          case 10:
+            // after finally
+          case 3:
+            // after finally
+            // implicit return
+            return thenHelper(null, null, __completer, null);
+        }
+      } catch (__error) {
+        if (__handler === null)
+          throw __error;
+        __result = __error;
+        __goto = __handler;
+      }
+
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+  testTransform("""
+function(x, y) async {
+  print(await(foo(x))); // calls
+  (await print)(foo(x));
+  print(foo(await x));
+  await (print(foo(await x)));
+  print(foo(x, await y, z));
+}
+""", """
+function(x, y) {
+  var __goto = 0, __completer = new Completer(), __temp1, __temp2, __temp3;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+          __temp1 = print;
+          __goto = 1;
+          return thenHelper(foo(x), __helper, __completer, null);
+        case 1:
+          // returning from await.
+          __temp1(__result);
+          __goto = 2;
+          return thenHelper(print, __helper, __completer, null);
+        case 2:
+          // returning from await.
+          __result(foo(x));
+          __temp1 = print;
+          __temp2 = foo;
+          __goto = 3;
+          return thenHelper(x, __helper, __completer, null);
+        case 3:
+          // returning from await.
+          __temp1(__temp2(__result));
+          __temp1 = print;
+          __temp2 = foo;
+          __goto = 5;
+          return thenHelper(x, __helper, __completer, null);
+        case 5:
+          // returning from await.
+          __goto = 4;
+          return thenHelper(__temp1(__temp2(__result)), __helper, __completer, null);
+        case 4:
+          // returning from await.
+          __temp1 = print;
+          __temp2 = foo;
+          __temp3 = x;
+          __goto = 6;
+          return thenHelper(y, __helper, __completer, null);
+        case 6:
+          // returning from await.
+          __temp1(__temp2(__temp3, __result, z));
+          // implicit return
+          return thenHelper(null, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+  testTransform("""
+function(x, y) async {
+  while (await(foo())) {
+    lab: { // labelled statement
+      switch(y) {
+      case 0:
+        foo();
+      case 0: // Duplicate case
+        print(await foo1(x));
+        return y;
+      case await bar(): // await in case
+        print(await foobar(x));
+        return y;
+      case x:
+        if (a) {
+          throw new Error();
+        } else {
+          continue;
+        }
+      default: // defaul case
+        break lab; // break to label
+      }
+      foo();
+    }
+  }
+}""", """
+function(x, y) {
+  var __goto = 0, __completer = new Completer(), __returnValue, __temp1;
+  function __helper(__result) {
+    while (true)
+      switch (__goto) {
+        case 0:
+          // Function start
+        case 2:
+          // while condition
+          __goto = 4;
+          return thenHelper(foo(), __helper, __completer, null);
+        case 4:
+          // returning from await.
+          if (!__result) {
+            // goto after while
+            __goto = 3;
+            break;
+          }
+        case 6:
+          // continue lab
+        case 7:
+          // switch
+          __temp1 = y;
+          if (__temp1 === 0) {
+            // goto case
+            __goto = 9;
+            break;
+          }
+          if (__temp1 === 0) {
+            // goto case
+            __goto = 10;
+            break;
+          }
+          __goto = 12;
+          return thenHelper(bar(), __helper, __completer, null);
+        case 12:
+          // returning from await.
+          if (__temp1 === __result) {
+            // goto case
+            __goto = 11;
+            break;
+          }
+          if (__temp1 === x) {
+            // goto case
+            __goto = 13;
+            break;
+          }
+          // goto default
+          __goto = 14;
+          break;
+        case 9:
+          // case
+          foo();
+        case 10:
+          // case
+          __temp1 = print;
+          __goto = 15;
+          return thenHelper(foo1(x), __helper, __completer, null);
+        case 15:
+          // returning from await.
+          __temp1(__result);
+          __returnValue = y;
+          // goto return
+          __goto = 1;
+          break;
+        case 11:
+          // case
+          __temp1 = print;
+          __goto = 16;
+          return thenHelper(foobar(x), __helper, __completer, null);
+        case 16:
+          // returning from await.
+          __temp1(__result);
+          __returnValue = y;
+          // goto return
+          __goto = 1;
+          break;
+        case 13:
+          // case
+          if (a) {
+            throw new Error();
+          } else {
+            // goto while condition
+            __goto = 2;
+            break;
+          }
+        case 14:
+          // default
+          // goto break lab
+          __goto = 5;
+          break;
+        case 8:
+          // after switch
+          foo();
+        case 5:
+          // break lab
+          // goto while condition
+          __goto = 2;
+          break;
+        case 3:
+          // after while
+        case 1:
+          // return
+          return thenHelper(__returnValue, null, __completer, null);
+      }
+  }
+  return thenHelper(null, __helper, __completer, null);
+}""");
+}
diff --git a/tests/compiler/dart2js/async_await_syntax_test.dart b/tests/compiler/dart2js/async_await_syntax_test.dart
index 8e0e651..336a7cd 100644
--- a/tests/compiler/dart2js/async_await_syntax_test.dart
+++ b/tests/compiler/dart2js/async_await_syntax_test.dart
@@ -7,9 +7,15 @@
 

 import 'frontend_checker.dart';

 

-const List<String> TESTS = const <String>[

-  'language/async_await_syntax_test.dart',

-];

+/// Map of test files to run together with their associated whitelist.

+///

+/// For instance

+///     'language/async_await_syntax_test.dart': const ['a03b', 'a04b']

+/// includes the multitest in 'language/async_await_syntax_test.dart' but

+/// expects the subtests 'a03b' and 'a04c' to fail.

+const Map<String, List<String>> TESTS = const <String, List<String>>{

+  'language/async_await_syntax_test.dart': const [],

+};

 

 void main(List<String> arguments) {

   check(TESTS, arguments: arguments, options: ['--enable-async']);

diff --git a/tests/compiler/dart2js/backend_dart/end2end_test.dart b/tests/compiler/dart2js/backend_dart/end2end_test.dart
index 5f70f16..a2a2520 100644
--- a/tests/compiler/dart2js/backend_dart/end2end_test.dart
+++ b/tests/compiler/dart2js/backend_dart/end2end_test.dart
@@ -16,20 +16,22 @@
 import 'test_helper.dart';

 import '../output_collector.dart';

 

-main() {

-  performTests(TEST_DATA, asyncTester, (TestSpec result) {

-    OutputCollector outputCollector = new OutputCollector();

-    asyncTest(() => compilerFor(result.input, outputProvider: outputCollector)

-        .then((Compiler compiler) {

-      String expectedOutput = result.output.trim();

-      compiler.phase = Compiler.PHASE_COMPILING;

-      DartBackend backend = compiler.backend;

-      backend.assembleProgram();

-      String output = outputCollector.getOutput('', 'dart').trim();

-      Expect.equals(expectedOutput, output,

-          '\nInput:\n${result.input}\n'

-          'Expected:\n$expectedOutput\n'

-          'Actual:\n$output\n');

-    }));

-  });

+main(List<String> args) {

+  performTests(TEST_DATA, asyncTester, runTest, args);

+}

+

+runTest(TestSpec result) {

+  OutputCollector outputCollector = new OutputCollector();

+  asyncTest(() => compilerFor(result.input, outputProvider: outputCollector)

+      .then((Compiler compiler) {

+    String expectedOutput = result.output.trim();

+    compiler.phase = Compiler.PHASE_COMPILING;

+    DartBackend backend = compiler.backend;

+    backend.assembleProgram();

+    String output = outputCollector.getOutput('', 'dart').trim();

+    Expect.equals(expectedOutput, output,

+        '\nInput:\n${result.input}\n'

+        'Expected:\n$expectedOutput\n'

+        'Actual:\n$output\n');

+  }));

 }
\ No newline at end of file
diff --git a/tests/compiler/dart2js/backend_dart/sexpr2_test.dart b/tests/compiler/dart2js/backend_dart/sexpr2_test.dart
index eef2d35..3cff4de 100644
--- a/tests/compiler/dart2js/backend_dart/sexpr2_test.dart
+++ b/tests/compiler/dart2js/backend_dart/sexpr2_test.dart
@@ -6,6 +6,7 @@
 library dart_backend.sexpr2_test;

 

 import 'package:compiler/src/dart2jslib.dart';

+import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';

 import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';

 import 'package:compiler/src/elements/elements.dart';

 import 'package:expect/expect.dart';

@@ -15,38 +16,47 @@
 

 import 'test_helper.dart';

 

-main() {

-  performTests(TEST_DATA, asyncTester, (TestSpec result) {

-    return compilerFor(result.input).then((Compiler compiler) {

-      void checkOutput(String elementName,

-                       Element element,

-                       String expectedOutput) {

+main(List<String> args) {

+  performTests(TEST_DATA, asyncTester, runTest, args);

+}

+

+runTest(TestSpec result) {

+  return compilerFor(result.input).then((Compiler compiler) {

+    void checkOutput(String elementName,

+                     Element element,

+                     String expectedOutput) {

+      ExecutableDefinition ir = compiler.irBuilder.getIr(element);

+      if (expectedOutput == null) {

+        Expect.isNull(ir, "\nInput:\n${result.input}\n"

+                          "No CPS IR expected for $element");

+      } else {

+        Expect.isNotNull(ir, "\nInput:\n${result.input}\n"

+                             "No CPS IR for $element");

         expectedOutput = expectedOutput.trim();

-        String output = compiler.irBuilder.getIr(element)

-            .accept(new SExpressionStringifier()).trim();

+        String output = ir.accept(new SExpressionStringifier()).trim();

         Expect.equals(expectedOutput, output,

             "\nInput:\n${result.input}\n"

             "Expected for '$elementName':\n$expectedOutput\n"

             "Actual for '$elementName':\n$output\n");

       }

+    }

 

-      if (result.output is String) {

-        checkOutput('main', compiler.mainFunction, result.output);

-      } else {

-        assert(result.output is Map<String, String>);

-        result.output.forEach((String elementName, String output) {

-          Element element;

-          if (elementName.contains('.')) {

-            ClassElement cls = compiler.mainApp.localLookup(

-                elementName.substring(0, elementName.indexOf('.')));

-            element = cls.localLookup(

-                elementName.substring(elementName.indexOf('.') + 1));

-          } else {

-            element = compiler.mainApp.localLookup(elementName);

-          }

-          checkOutput(elementName, element, output);

-        });

-      }

-    });

+    if (result.output is String) {

+      checkOutput('main', compiler.mainFunction, result.output);

+    } else {

+      assert(result.output is Map<String, String>);

+      result.output.forEach((String elementName, String output) {

+        Element element;

+        if (elementName.contains('.')) {

+          ClassElement cls = compiler.mainApp.localLookup(

+              elementName.substring(0, elementName.indexOf('.')));

+          element = cls.localLookup(

+              elementName.substring(elementName.indexOf('.') + 1));

+        } else {

+          element = compiler.mainApp.localLookup(elementName);

+        }

+        checkOutput(elementName, element, output);

+      });

+    }

   });

 }
\ No newline at end of file
diff --git a/tests/compiler/dart2js/dart2js.status b/tests/compiler/dart2js/dart2js.status
index 5607f3f..b5cf3a6 100644
--- a/tests/compiler/dart2js/dart2js.status
+++ b/tests/compiler/dart2js/dart2js.status
@@ -4,7 +4,10 @@
 
 boolified_operator_test: Fail # Issue 8001
 
-async_await_syntax_test: Skip # Not sure how/why test cases a03b, a04c fail.
+# Don't mark these tests as failing. Instead, fix the errors/warnings that they
+# report or update the whitelist in the test-files to temporarily allow
+# digression.
+async_await_syntax_test: Pass # DON'T CHANGE THIS LINE -- SEE ABOVE.
 
 # simple_function_subtype_test is temporarily(?) disabled due to new method for
 # building function type tests.
diff --git a/tests/compiler/dart2js/expect_annotations_test.dart b/tests/compiler/dart2js/expect_annotations_test.dart
index ed8889a..3ee1fdc 100644
--- a/tests/compiler/dart2js/expect_annotations_test.dart
+++ b/tests/compiler/dart2js/expect_annotations_test.dart
@@ -8,6 +8,8 @@
 import 'package:compiler/src/dart2jslib.dart';
 import 'package:compiler/src/elements/elements.dart';
 import 'package:compiler/src/js_backend/js_backend.dart';
+import 'package:compiler/src/types/types.dart';
+import 'type_mask_test_helper.dart';
 import 'memory_compiler.dart';
 
 const Map MEMORY_SOURCE_FILES = const {
@@ -28,12 +30,19 @@
 @NoInlining() @TrustTypeAnnotations()
 int methodNoInliningTrustTypeAnnotations(String arg) => arg.length;
 
+@AssumeDynamic() @TrustTypeAnnotations()
+int methodAssumeDynamicTrustTypeAnnotations(String arg) => arg.length;
+
+
 void main(List<String> args) {
   print(method(args[0]));
   print(methodAssumeDynamic('foo'));
-  print(methodTrustTypeAnnotations(null));
+  print(methodTrustTypeAnnotations(42));
+  print(methodTrustTypeAnnotations("fourtyTwo"));
   print(methodNoInlining('bar'));
-  print(methodNoInliningTrustTypeAnnotations(null));
+  print(methodNoInliningTrustTypeAnnotations(42));
+  print(methodNoInliningTrustTypeAnnotations("fourtyTwo"));
+  print(methodAssumeDynamicTrustTypeAnnotations(null));
 }
 """
 };
@@ -50,9 +59,25 @@
     Expect.isNotNull(backend.annotations.expectAssumeDynamicClass,
         'AssumeDynamicClass is unresolved.');
 
+    void testTypeMatch(FunctionElement function, TypeMask expectedParameterType,
+                       TypeMask expectedReturnType, TypesInferrer inferrer) {
+      for (ParameterElement parameter in function.parameters) {
+        TypeMask type = inferrer.getTypeOfElement(parameter);
+        Expect.equals(expectedParameterType, simplify(type, compiler),
+            "$parameter");
+      }
+      if (expectedReturnType != null) {
+        TypeMask type = inferrer.getReturnTypeOfElement(function);
+        Expect.equals(expectedReturnType, simplify(type, compiler),
+            "$function");
+      }
+    }
+
     void test(String name,
               {bool expectNoInlining: false,
                bool expectTrustTypeAnnotations: false,
+               TypeMask expectedParameterType: null,
+               TypeMask expectedReturnType: null,
                bool expectAssumeDynamic: false}) {
        Element method = compiler.mainApp.find(name);
        Expect.isNotNull(method);
@@ -68,14 +93,35 @@
            expectAssumeDynamic,
            backend.annotations.assumeDynamic(method),
            "Unexpected annotation of @AssumeDynamic on '$method'.");
+       TypesInferrer inferrer = compiler.typesTask.typesInferrer;
+       if (expectTrustTypeAnnotations && expectedParameterType != null) {
+         testTypeMatch(method, expectedParameterType, expectedReturnType,
+             inferrer);
+       } else if (expectAssumeDynamic) {
+         testTypeMatch(method, compiler.typesTask.dynamicType, null, inferrer);
+       }
     }
 
+    TypeMask jsStringType = compiler.typesTask.stringType;
+    TypeMask jsIntType = compiler.typesTask.intType;
+    TypeMask coreStringType = new TypeMask.subtype(compiler.stringClass,
+        compiler.world);
+
     test('method');
     test('methodAssumeDynamic', expectAssumeDynamic: true);
-    test('methodTrustTypeAnnotations', expectTrustTypeAnnotations: true);
+    test('methodTrustTypeAnnotations',
+        expectTrustTypeAnnotations: true,
+        expectedParameterType: jsStringType);
     test('methodNoInlining', expectNoInlining: true);
     test('methodNoInliningTrustTypeAnnotations',
          expectNoInlining: true,
-         expectTrustTypeAnnotations: true);
+         expectTrustTypeAnnotations: true,
+         expectedParameterType: jsStringType,
+         expectedReturnType: jsIntType);
+    test('methodAssumeDynamicTrustTypeAnnotations',
+         expectAssumeDynamic: true,
+         expectTrustTypeAnnotations: true,
+         expectedParameterType: coreStringType);
+
   }));
 }
diff --git a/tests/compiler/dart2js/frontend_checker.dart b/tests/compiler/dart2js/frontend_checker.dart
index 24f83c0..b4bd3a0 100644
--- a/tests/compiler/dart2js/frontend_checker.dart
+++ b/tests/compiler/dart2js/frontend_checker.dart
@@ -6,10 +6,11 @@
 // compile-time errors for the provided multitests.

 

 import 'dart:async';

+import 'dart:io';

 

-import 'package:expect/expect.dart';

 import 'package:async_helper/async_helper.dart';

-

+import 'package:compiler/src/util/uri_extras.dart'

+    show relativize;

 import 'memory_compiler.dart';

 

 import '../../../tools/testing/dart/multitest.dart'

@@ -17,13 +18,24 @@
 import '../../../tools/testing/dart/path.dart'

     show Path;

 

-void check(List<String> testFiles,

+

+/// Check the analysis of the multitests in [testFiles] to result in the

+/// expected static warnings and compile-time errors.

+///

+/// [testFiles] is a map of the test files to be checked together with their

+/// associated white listing.

+///

+/// For instance if [testFiles] contain the mapping

+///     'language/async_await_syntax_test.dart': const ['a03b', 'a04b']

+/// the multitests in 'language/async_await_syntax_test.dart' are checked but

+/// the subtests 'a03b' and 'a04c' are expected to fail.

+void check(Map<String, List<String>> testFiles,

            {List<String> arguments: const <String>[],

             List<String> options: const <String>[]}) {

   bool outcomeMismatch = false;

   bool verbose = arguments.contains('-v');

   var cachedCompiler;

-  asyncTest(() => Future.forEach(testFiles, (String testFile) {

+  asyncTest(() => Future.forEach(testFiles.keys, (String testFile) {

     Map<String, String> testSources = {};

     Map<String, Set<String>> testOutcomes = {};

     String fileName = 'tests/$testFile';

@@ -31,6 +43,7 @@
     return Future.forEach(testSources.keys, (String testName) {

       String testFileName = '$fileName/$testName';

       Set<String> expectedOutcome = testOutcomes[testName];

+      bool expectFailure = testFiles[testFile].contains(testName);

       DiagnosticCollector collector = new DiagnosticCollector();

       var compiler = compilerFor(

            {testFileName: testSources[testName]},

@@ -39,15 +52,16 @@
            showDiagnostics: verbose,

            cachedCompiler: cachedCompiler);

       return compiler.run(Uri.parse('memory:$testFileName')).then((_) {

+        bool unexpectedResult = false;

         if (expectedOutcome.contains('compile-time error')) {

           if (collector.errors.isEmpty) {

             print('$testFileName: Missing compile-time error.');

-            outcomeMismatch = true;

+            unexpectedResult = true;

           }

         } else if (expectedOutcome.contains('static type warning')) {

           if (collector.warnings.isEmpty) {

             print('$testFileName: Missing static type warning.');

-            outcomeMismatch = true;

+            unexpectedResult = true;

           }

         } else {

           // Expect ok.

@@ -59,13 +73,38 @@
             collector.warnings.forEach((message) {

               print('$testFileName: Unexpected warning: ${message.message}');

             });

-            outcomeMismatch = true;

+            unexpectedResult = true;

           }

         }

+        if (expectFailure) {

+          if (unexpectedResult) {

+            unexpectedResult = false;

+          } else {

+            print('$testFileName: The test is white-listed '

+                  'and therefore expected to fail.');

+            unexpectedResult = true;

+          }

+        }

+        if (unexpectedResult) {

+          outcomeMismatch = true;

+        }

         cachedCompiler = compiler;

       });

     });

   }).then((_) {

-    Expect.isFalse(outcomeMismatch, 'Outcome mismatch');

+    if (outcomeMismatch) {

+      String testFileName =

+            relativize(Uri.base, Platform.script, Platform.isWindows);

+      print('''

+

+===

+=== ERROR: Unexpected result of analysis.

+===

+=== Please update the white-listing in $testFileName

+===

+

+''');

+      exit(1);

+    }

   }));

 }

diff --git a/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart b/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
index 1243ba2..004d80f 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
@@ -10,6 +10,36 @@
 import 'js_backend_cps_ir.dart';
 
 const List<TestEntry> tests = const [
+  const TestEntry(r"""
+main() {
+  var e = 1;
+  var l = [1, 2, 3];
+  var m = {'s': 1};
+
+  print('(' ')');
+  print('(${true})');
+  print('(${1})');
+  print('(${[1, 2, 3]})');
+  print('(${{'s': 1}})');
+  print('($e)');
+  print('($l)');
+  print('($m)');
+}""",r"""
+function() {
+  var e, l, m;
+  e = 1;
+  l = [1, 2, 3];
+  m = P.LinkedHashMap_LinkedHashMap$_literal(["s", 1]);
+  P.print("()");
+  P.print("(" + true + ")");
+  P.print("(" + 1 + ")");
+  P.print("(" + H.S([1, 2, 3]) + ")");
+  P.print("(" + H.S(P.LinkedHashMap_LinkedHashMap$_literal(["s", 1])) + ")");
+  P.print("(" + H.S(e) + ")");
+  P.print("(" + H.S(l) + ")");
+  P.print("(" + H.S(m) + ")");
+  return null;
+}"""),
   const TestEntry("""
 foo(a, [b = "b"]) => b;
 bar(a, {b: "b", c: "c"}) => c;
@@ -24,11 +54,14 @@
 """,
 """
 function() {
+  var v0, v1;
   V.foo(0, "b");
   V.foo(1, 2);
   V.bar(3, "b", "c");
   V.bar(4, 5, "c");
-  V.bar(6, "b", 7);
+  v0 = 6;
+  v1 = 7;
+  V.bar(v0, "b", v1);
   V.bar(8, 9, 10);
   return null;
 }"""),
diff --git a/tests/compiler/dart2js/js_backend_cps_ir_constructor_test.dart b/tests/compiler/dart2js/js_backend_cps_ir_constructor_test.dart
index b807c29..abf40d0 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_constructor_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_constructor_test.dart
@@ -138,6 +138,37 @@
   v4 = V.foo("x2");
   return new V.Sub(v0, v1, V.foo("y3"), v2, v4, v3);
 }"""),
+
+
+  const TestEntry.forMethod('generative_constructor(Foo#)', """
+class Bar {
+  Bar(x, {y, z: 'z', w: '_', q}) {
+    print(x);
+    print(y);
+    print(z);
+    print(w);
+    print(q);
+  }
+}
+class Foo extends Bar {
+  Foo() : super('x', y: 'y', w: 'w');
+}
+main() {
+  new Foo();
+}
+""",
+r"""
+function() {
+  var x, y, w, z, q, v0;
+  x = "x";
+  y = "y";
+  w = "w";
+  z = "z";
+  q = null;
+  v0 = new V.Foo();
+  v0.Bar$5$q$w$y$z(x, q, w, y, z);
+  return v0;
+}"""),
 ];
 
 void main() {
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index 177083d..f210f1e 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -92,8 +92,7 @@
               preserveComments: preserveComments,
               trustTypeAnnotations: trustTypeAnnotations,
               showPackageWarnings: true,
-              outputProvider: outputProvider,
-              enableAsyncAwait: enableAsyncAwait) {
+              outputProvider: outputProvider) {
     this.disableInlining = disableInlining;
 
     deferredLoadTask = new MockDeferredLoadTask(this);
diff --git a/tests/compiler/dart2js/mock_libraries.dart b/tests/compiler/dart2js/mock_libraries.dart
index 507d123..ab820a4 100644
--- a/tests/compiler/dart2js/mock_libraries.dart
+++ b/tests/compiler/dart2js/mock_libraries.dart
@@ -209,6 +209,8 @@
   'stringTypeCheck': 'stringTypeCheck(x) {}',
   'subtypeCast': 'subtypeCast(object, isField, checks, asField) {}',
   'subtypeOfRuntimeTypeCast': 'subtypeOfRuntimeTypeCast(object, type) {}',
+  'thenHelper':
+      'thenHelper(object, helperCallback, completer, errorCallback) {}',
   'throwAbstractClassInstantiationError':
       'throwAbstractClassInstantiationError(className) {}',
   'throwCyclicInit': 'throwCyclicInit(staticName) {}',
@@ -366,6 +368,8 @@
   'DeferredLibrary': 'class DeferredLibrary {}',
   'Future': 'class Future<T> {}',
   'Stream': 'class Stream<T> {}',
+  'Completer': 'class Completer<T> {}',
+  'StreamIterator': 'class StreamIterator<T> {}',
 };
 
 const Map<String, String> DEFAULT_MIRRORS_LIBRARY = const <String, String>{
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 22cb07d..c5b1d81 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -217,6 +217,7 @@
 iterable_element_at_test: StaticWarning, OK # Test generates errors on purpose.
 num_clamp_test: StaticWarning, OK # Test generates errors on purpose.
 string_test: StaticWarning, OK # Test generates error on purpose.
+duration2_test: StaticWarning, OK # Test generates error on purpose.
 
 [ $system == windows && $arch == x64 ]
 stopwatch_test: Skip  # Flaky test due to expected performance behaviour.
diff --git a/tests/corelib/duration2_test.dart b/tests/corelib/duration2_test.dart
new file mode 100644
index 0000000..5bdc885
--- /dev/null
+++ b/tests/corelib/duration2_test.dart
@@ -0,0 +1,33 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+main() {
+  // If the duration class multiplies "str" * microseconds-per-day (instead of
+  // (microseconds-per-day * "str") it will try to build up a huge string and
+  // terminate with an out-of-memory exception instead of an ArgumentError or
+  // TypeError.
+  // See dartbug.com/22309
+
+  String longString = "str" * 1000;
+  Expect.throws(() => new Duration(days: longString),
+                (e) => e is ArgumentError || e is TypeError ||
+                       e is NoSuchMethodError);
+  Expect.throws(() => new Duration(hours: longString),
+                (e) => e is ArgumentError || e is TypeError ||
+                       e is NoSuchMethodError);
+  Expect.throws(() => new Duration(minutes: longString),
+                (e) => e is ArgumentError || e is TypeError ||
+                       e is NoSuchMethodError);
+  Expect.throws(() => new Duration(seconds: longString),
+                (e) => e is ArgumentError || e is TypeError ||
+                       e is NoSuchMethodError);
+  Expect.throws(() => new Duration(milliseconds: longString),
+                (e) => e is ArgumentError || e is TypeError ||
+                       e is NoSuchMethodError);
+  Expect.throws(() => new Duration(microseconds: longString),
+                (e) => e is ArgumentError || e is TypeError ||
+                       e is NoSuchMethodError);
+}
diff --git a/tests/html/element_classes_test.dart b/tests/html/element_classes_test.dart
index cf6a35c..d1d23ee 100644
--- a/tests/html/element_classes_test.dart
+++ b/tests/html/element_classes_test.dart
@@ -198,7 +198,7 @@
 
   Element listElement;
 
-  ElementList listElementSetup() {
+  ElementList<Element> listElementSetup() {
     listElement = makeListElement();
     document.documentElement.children.add(listElement);
     return document.queryAll('li');
diff --git a/tests/html/element_test.dart b/tests/html/element_test.dart
index 6f75758..42c4ebe 100644
--- a/tests/html/element_test.dart
+++ b/tests/html/element_test.dart
@@ -915,7 +915,7 @@
     //      some of these tests simply check that a method can be called.
     //   2. Some methods are implemented by mixins.
 
-    ElementList makeElementList() =>
+    ElementList<Element> makeElementList() =>
         (new Element.html("<div>Foo<br/><!--baz--><br/><br/></div>"))
         .queryAll('br');
 
diff --git a/tests/html/html.status b/tests/html/html.status
index cc9fa8b..0d80d41 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -15,7 +15,6 @@
 indexeddb_1_test/functional: Skip # Issue 19512 (timing out)
 indexeddb_4_test: Skip # Issue 19726
 mouse_event_test: Fail # Issue 20437
-storage_quota_test/missingenumcheck: Fail
 worker_api_test: Fail # Issue 10223
 two_scripts_htmltest: Fail # Issue 16603
 media_stream_test/constructors: Pass, Crash # Issue 22267
@@ -101,6 +100,7 @@
 xhr_cross_origin_test: Pass, Fail # Issue 11884
 xhr_test: Pass, Fail # Issue 11884
 element_types_test/constructors: Skip # Times out. Issue 22050
+audiocontext_test/functional: Skip # Renderer crash. Issue 22327
 
 [$runtime == drt || $runtime == dartium || $runtime == chrome || $runtime == chromeOnAndroid || $runtime == ContentShellOnAndroid ]
 webgl_1_test: Pass, Fail # Issue 8219
@@ -393,6 +393,7 @@
 htmlelement_test: StaticWarning
 localstorage_test: StaticWarning
 mutationobserver_test: StaticWarning
+queryall_test: fail
 track_element_constructor_test: StaticWarning
 transferables_test: StaticWarning
 typed_arrays_range_checks_test: StaticWarning
diff --git a/tests/language/asyncstar_concat_test.dart b/tests/language/asyncstar_concat_test.dart
new file mode 100644
index 0000000..aeb1361
--- /dev/null
+++ b/tests/language/asyncstar_concat_test.dart
@@ -0,0 +1,29 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+range(start, end) async* {
+  for (int i = start; i < end; i++) {
+    yield i;
+  }
+}
+
+concat(a, b) async* {
+  yield* a;
+  yield* b;
+}
+
+test() async {
+  Expect.listEquals([1, 2, 3, 11, 12, 13],
+                    await concat(range(1, 4), range(11, 14)).toList());
+}
+
+main() {
+  asyncStart();
+  test().then((_) {
+    asyncEnd();
+  });
+}
\ No newline at end of file
diff --git a/tests/language/asyncstar_yield_test.dart b/tests/language/asyncstar_yield_test.dart
new file mode 100644
index 0000000..3b64900
--- /dev/null
+++ b/tests/language/asyncstar_yield_test.dart
@@ -0,0 +1,75 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+Stream<int> foo1() async* {
+  yield 1;
+  var p = await new Future.value(10);
+  yield p + 10;
+}
+
+Stream<int> foo2() async* {
+  int i = 0;
+  while (true) {
+    await (new Future.delayed(new Duration(milliseconds: 0), () {}));
+    if (i > 10) return;
+    yield i;
+    i++;
+  }
+}
+
+Stream<int> foo3(p) async* {
+  int i = 0;
+  bool t = false;
+  yield null;
+  while (true) {
+    i++;
+    a: for (int i = 0; i < p; i++) {
+      if (!t) {
+        for (int j = 0; j < 3; j++) {
+          yield -1;
+          t = true;
+          break a;
+        }
+      }
+      await 4;
+      yield i;
+    }
+  }
+}
+
+Completer<bool> finalized = new Completer<bool>();
+
+Stream<int> foo4() async* {
+  int i = 0;
+  try {
+    while (true) {
+      yield i;
+      i++;
+    }
+  } finally {
+    // Canceling the stream-subscription should run the finalizer.
+    finalized.complete(true);
+  }
+}
+
+test() async {
+  Expect.listEquals([1, 20], await (foo1().toList()));
+  Expect.listEquals([0, 1, 2, 3], await (foo2().take(4).toList()));
+  Expect.listEquals([null, -1, 0, 1, 2, 3, 0, 1, 2, 3],
+      await (foo3(4).take(10).toList()));
+  Expect.listEquals([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
+      await (foo4().take(10).toList()));
+  Expect.isTrue(await (finalized.future));
+}
+
+main ()  {
+  asyncStart();
+  test().then((_) {
+    asyncEnd();
+  });
+}
\ No newline at end of file
diff --git a/tests/language/asyncstar_yieldstar_test.dart b/tests/language/asyncstar_yieldstar_test.dart
new file mode 100644
index 0000000..4caaf2d
--- /dev/null
+++ b/tests/language/asyncstar_yieldstar_test.dart
@@ -0,0 +1,100 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+Stream<int> subStream(p) async* {
+  yield p;
+  yield p+1;
+}
+
+Stream foo(Completer<bool> finalized) async* {
+  int i = 0;
+  try {
+    while (true) {
+      yield "outer";
+      yield* subStream(i);
+      i++;
+    }
+  } finally {
+    // See that we did not run too many iterations.
+    Expect.isTrue(i < 10);
+    // Canceling the stream-subscription should run the finalizer.
+    finalized.complete(true);
+  }
+}
+
+foo2(Stream subStream) async* {
+  yield* subStream;
+}
+
+test() async {
+  Expect.listEquals([0, 1],
+      await (subStream(0).toList()));
+  Completer<bool> finalized = new Completer<bool>();
+  Expect.listEquals(["outer", 0, 1, "outer", 1, 2, "outer", 2],
+      await (foo(finalized).take(8).toList()));
+  Expect.isTrue(await (finalized.future));
+  
+  finalized = new Completer<bool>();
+  // Canceling the stream while it is yield*-ing from the sub-stream.
+  Expect.listEquals(["outer", 0, 1, "outer", 1, 2, "outer"],
+      await (foo(finalized).take(7).toList()));
+  Expect.isTrue(await (finalized.future));
+  finalized = new Completer<bool>();
+ 
+  Completer<bool> pausedCompleter = new Completer<bool>();
+  Completer<bool> resumedCompleter = new Completer<bool>();
+  Completer<bool> canceledCompleter = new Completer<bool>();
+ 
+  StreamController controller;
+  int i = 0;
+  addNext() {
+    if (i >= 10) return;
+    controller.add(i);
+    i++;
+    if (!controller.isPaused) {
+      scheduleMicrotask(addNext);
+    }
+  }
+ 
+  controller = new StreamController(onListen: () {
+    scheduleMicrotask(addNext);
+  }, onPause: () {
+    pausedCompleter.complete(true);
+  }, onResume: () {
+    resumedCompleter.complete(true);
+    scheduleMicrotask(addNext);
+  }, onCancel: () {
+    canceledCompleter.complete(true);
+  });
+ 
+  StreamSubscription subscription;
+  // Test that the yield*'ed stream is paused and resumed.
+  subscription = foo2(controller.stream).listen((event) {
+    if (event == 2) {
+      subscription.pause();
+      scheduleMicrotask(() {
+        subscription.resume();
+      });
+    }
+    if (event == 5) {
+      subscription.cancel();
+    }
+  });
+  // Test that the yield*'ed streamSubscription is paused, resumed and canceled
+  // by the async* stream.
+  Expect.isTrue(await pausedCompleter.future);
+  Expect.isTrue(await resumedCompleter.future);
+  Expect.isTrue(await canceledCompleter.future);
+}
+
+main() {
+  asyncStart();
+  test().then((_) {
+    asyncEnd();
+  });
+}
\ No newline at end of file
diff --git a/tests/language/await_exceptions_test.dart b/tests/language/await_exceptions_test.dart
index 02779ce..f1d39a1 100644
--- a/tests/language/await_exceptions_test.dart
+++ b/tests/language/await_exceptions_test.dart
@@ -5,6 +5,7 @@
 // VMOptions=--optimization-counter-threshold=5
 
 import 'package:expect/expect.dart';
+import "package:async_helper/async_helper.dart";
 
 import 'dart:async';
 
@@ -69,7 +70,7 @@
   return '!';
 }
 
-main() async {
+test() async {
   var result;
   for (int i = 0; i < 10; i++) {
     await test0();
@@ -77,4 +78,21 @@
     result = await test2();
     Expect.equals('abcd', result);
   }
+  await 1;
+}
+
+foo() {
+  throw "Error";
+}
+
+awaitFoo() async {
+  await foo();
+}
+
+main() {
+  asyncStart();
+  test().then((_) => awaitFoo().then(
+          (_) => Expect.fail("Should have thrown"),
+          onError: (error) => Expect.equals("Error", error)))
+      .whenComplete(asyncEnd);
 }
diff --git a/tests/language/await_for_cancel_test.dart b/tests/language/await_for_cancel_test.dart
new file mode 100644
index 0000000..1374f4b
--- /dev/null
+++ b/tests/language/await_for_cancel_test.dart
@@ -0,0 +1,92 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "dart:async";
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+bool canceled;
+
+test1() async {
+  canceled = false;
+  try {
+    StreamController controller = infiniteStreamController();
+    outer: while(true) {
+      await for (var x in controller.stream) {
+        for (int j = 0; j < 10; j++) {
+          if (j == 5) break outer;
+        }
+      }
+    }
+  } finally {
+    Expect.isTrue(canceled);
+  }
+}
+
+test2() async {
+  canceled = false;
+  try {
+    StreamController controller = infiniteStreamController();
+    bool first = true;
+    outer: while(true) {
+      if (first) {
+        first = false;
+      } else {
+        break;
+      }
+      await for (var x in controller.stream) {
+        for (int j = 0; j < 10; j++) {
+          if (j == 5) continue outer;
+        }
+      }
+    }
+  } finally {
+    Expect.isTrue(canceled);
+  }
+}
+
+test() async {
+  await test1();
+  await test2();
+}
+
+main() {
+  asyncStart();
+  test().then((_) {
+    asyncEnd();
+  });
+}
+
+
+// Create a stream that produces numbers [1, 2, ... ]
+StreamController infiniteStreamController() {
+  StreamController controller;
+  Timer timer;
+  int counter = 0;
+
+  void tick() {
+    if (controller.isPaused) {
+      return;
+    }
+    if (canceled) {
+      return;
+    }
+    counter++;
+    controller.add(counter); // Ask stream to send counter values as event.
+    Timer.run(tick);
+  }
+
+  void startTimer() {
+    Timer.run(tick);
+  }
+
+  controller = new StreamController(
+      onListen: startTimer,
+      onResume: startTimer,
+      onCancel: () {
+        canceled = true;
+      });
+
+  return controller;
+}
diff --git a/tests/language/await_for_test.dart b/tests/language/await_for_test.dart
index 9d22464..8c6f18e 100644
--- a/tests/language/await_for_test.dart
+++ b/tests/language/await_for_test.dart
@@ -38,7 +38,32 @@
   await for (var x in makeMeAStream()) {
     t2.record(x);
   }
-  t2.record("X");
+  t2.record("Y");
+}
+
+Trace t3 = new Trace();
+
+consumeNested() async {
+  await for (var x in makeMeAStream()) {
+    t3.record(x);
+    await for (var y in makeMeAStream()) {
+      t3.record(y);
+    }
+    t3.record("|");
+  }
+  t3.record("Z");
+}
+
+Trace t4 = new Trace();
+
+consumeSomeOfInfinite() async {
+  int i = 0;
+  await for (var x in infiniteStream()) {
+    i++;
+    if (i > 10) break;
+    t4.record(x);
+  }
+  t4.record("U");
 }
 
 main() {
@@ -47,18 +72,23 @@
 
   var f2 = consumeTwo();
   t2.record("T2:");
+ 
+  var f3 = consumeNested();
+  t3.record("T3:");
+ 
+  var f4 = consumeSomeOfInfinite();
+  t4.record("T4:");
 
   asyncStart();
-  Future.wait([f1, f2]).then((_) {
-    print("Trace 1: $t1");
-    print("Trace 2: $t2");
+  Future.wait([f1, f2, f3, f4]).then((_) {
     Expect.equals("T1:12345X", t1.toString());
-    Expect.equals("T2:12345X", t2.toString());
+    Expect.equals("T2:12345Y", t2.toString());
+    Expect.equals("T3:112345|212345|312345|412345|512345|Z", t3.toString());
+    Expect.equals("T4:12345678910U", t4.toString());
     asyncEnd();
   });
 }
 
-
 // Create a stream that produces numbers [1, 2, ... maxCount]
 Stream timedCounter(int maxCount) {
   StreamController controller;
@@ -93,3 +123,34 @@
 
   return controller.stream;
 }
+
+// Create a stream that produces numbers [1, 2, ... ]
+Stream infiniteStream() {
+  StreamController controller;
+  Timer timer;
+  int counter = 0;
+
+  void tick(_) {
+    counter++;
+    controller.add(counter); // Ask stream to send counter values as event.
+  }
+
+  void startTimer() {
+    timer = new Timer.periodic(const Duration(milliseconds: 10), tick);
+  }
+
+  void stopTimer() {
+    if (timer != null) {
+      timer.cancel();
+      timer = null;
+    }
+  }
+
+  controller = new StreamController(
+      onListen: startTimer,
+      onPause: stopTimer,
+      onResume: startTimer,
+      onCancel: stopTimer);
+
+  return controller.stream;
+}
diff --git a/tests/language/await_future_test.dart b/tests/language/await_future_test.dart
index 2bd9a51..b0151c5 100644
--- a/tests/language/await_future_test.dart
+++ b/tests/language/await_future_test.dart
@@ -5,6 +5,7 @@
 // VMOptions=--optimization-counter-threshold=5
 
 import 'package:expect/expect.dart';
+import "package:async_helper/async_helper.dart";
 
 import 'dart:async';
 
@@ -181,7 +182,7 @@
   return k;
 }
 
-main() async {
+test() async {
   var result;
   for (int i = 0; i < 10; i++) {
     result = await foo();
@@ -226,3 +227,10 @@
     Expect.equals('abc', result);
   }
 }
+
+main() {
+  asyncStart();
+  test().then((_) {
+    asyncEnd();
+  });
+}
\ No newline at end of file
diff --git a/tests/language/await_not_started_immediately_test.dart b/tests/language/await_not_started_immediately_test.dart
new file mode 100644
index 0000000..035885e
--- /dev/null
+++ b/tests/language/await_not_started_immediately_test.dart
@@ -0,0 +1,22 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test that an async function does not start immediately.
+
+import "package:expect/expect.dart";
+import "package:async_helper/async_helper.dart";
+
+var x = 0;
+
+foo() async {
+  x++;
+  await 1;
+  x++;
+}
+
+void main() {
+  asyncStart();
+  foo().then((_) => Expect.equals(2, x)).whenComplete(asyncEnd);
+  Expect.equals(0, x);
+}
\ No newline at end of file
diff --git a/tests/language/language.status b/tests/language/language.status
index acad979..733ba43 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -7,6 +7,10 @@
 
 [ $compiler == none ]
 built_in_identifier_prefix_test: Fail # Issue 6970
+await_for_cancel_test: RuntimeError # Issue 21404
+asyncstar_yield_test: Fail # Issue 21404
+asyncstar_yieldstar_test: Fail # Issue 21404
+asyncstar_concat_test: Fail # Issue 21404
 
 # These bugs refer currently ongoing language discussions.
 constructor_initializer_test/none: Fail # Issue 12633
@@ -59,6 +63,7 @@
 async_await_syntax_test/b11d: CompileTimeError # Issue 21404
 async_await_syntax_test/c03a: CompileTimeError # Issue 21404
 async_await_syntax_test/d03a: CompileTimeError # Issue 21404
+sync_generator3_test/test2: Fail # Issue 22300
 
 [ $compiler == none && ($runtime == drt || $runtime == dartium|| $runtime == ContentShellOnAndroid) ]
 async_await_syntax_test/a03a: RuntimeError # Issue 21404
diff --git a/tests/language/language_analyzer.status b/tests/language/language_analyzer.status
index ffee566..357491a 100644
--- a/tests/language/language_analyzer.status
+++ b/tests/language/language_analyzer.status
@@ -3,10 +3,18 @@
 # BSD-style license that can be found in the LICENSE file.
 
 [ $compiler == dartanalyzer ]
+
 await_backwards_compatibility_test/none: CompileTimeError # Issue 22052
 await_test: CompileTimeError # Issue 22052
 
-sync_generator2_test: Skip #
+sync_generator2_test/01: MissingCompileTimeError # Issue 22252
+sync_generator2_test/02: MissingCompileTimeError # Issue 22252
+sync_generator2_test/03: MissingCompileTimeError # Issue 22252
+sync_generator2_test/04: MissingCompileTimeError # Issue 22252
+sync_generator2_test/05: MissingCompileTimeError # Issue 22252
+sync_generator2_test/06: MissingCompileTimeError # Issue 22252
+sync_generator2_test/07: MissingCompileTimeError # Issue 22252
+sync_generator2_test/10: MissingCompileTimeError # Issue 22252
 
 async_test/type-mismatch2: MissingStaticWarning # Issue 22053
 async_test/type-mismatch3: MissingStaticWarning # Issue 22053
@@ -18,7 +26,6 @@
 async_await_syntax_test/a10a: MissingStaticWarning
 async_await_syntax_test/b10a: MissingStaticWarning
 async_await_syntax_test/c10a: MissingStaticWarning
-async_await_syntax_test/d08b: MissingStaticWarning
 async_await_syntax_test/d10a: MissingStaticWarning
 
 # Runtime negative test. No static errors or warnings.
diff --git a/tests/language/language_analyzer2.status b/tests/language/language_analyzer2.status
index b0c7bfd..9b07cd8 100644
--- a/tests/language/language_analyzer2.status
+++ b/tests/language/language_analyzer2.status
@@ -13,8 +13,6 @@
 enum_syntax_test/05: Fail # 21649
 enum_syntax_test/06: Fail # 21649
 
-sync_generator1_test: Fail
-
 compile_time_constant_c_test/01: Fail # Issue 21000
 compile_time_constant12_test: Fail # Issue 21000
 
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 9a0121e..40de933 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -2,78 +2,25 @@
 # for details. All rights reserved. Use of this source code is governed by a
 # BSD-style license that can be found in the LICENSE file.
 
-[ $compiler == dart2js && $csp && $browser ]
-deferred_mixin_test: RuntimeError # Issue 21863
+[ $compiler == dart2js && $checked ]
+await_future_test: RuntimeError # Issue 22332
+async_test/none: RuntimeError # Issue 22332
 
 [ $compiler == dart2js ]
-async_control_structures_test: CompileTimeError # Issue 21411
-async_test/none: CompileTimeError # Issue 21411
-await_backwards_compatibility_test/none: CompileTimeError # Issue 21411
-await_exceptions_test: CompileTimeError # Issue 21411
-await_for_test: CompileTimeError # Issue 21411
-await_future_test: CompileTimeError # Issue 21411
-await_nonfuture_test: CompileTimeError # Issue 21411
-await_regression_test: CompileTimeError # Issue 21411
-await_test: CompileTimeError # Issue 21411
-sync_generator1_test: Fail # Issue 21411
-sync_generator2_test/none: Fail # Issue 21411
-async_await_syntax_test/a01a: CompileTimeError # Issue 21411
-async_await_syntax_test/a02a: CompileTimeError # Issue 21411
-async_await_syntax_test/a03a: CompileTimeError # Issue 21411
-async_await_syntax_test/a04a: CompileTimeError # Issue 21411
-async_await_syntax_test/a05a: CompileTimeError # Issue 21411
-async_await_syntax_test/a05b: CompileTimeError # Issue 21411
-async_await_syntax_test/a06a: CompileTimeError # Issue 21411
-async_await_syntax_test/a07a: CompileTimeError # Issue 21411
-async_await_syntax_test/a08a: CompileTimeError # Issue 21411
-async_await_syntax_test/a09a: CompileTimeError # Issue 21411
-async_await_syntax_test/a10a: CompileTimeError # Issue 21411
-async_await_syntax_test/a11b: CompileTimeError # Issue 21411
-async_await_syntax_test/a11c: CompileTimeError # Issue 21411
-async_await_syntax_test/a11d: CompileTimeError # Issue 21411
-async_await_syntax_test/b01a: CompileTimeError # Issue 21411
-async_await_syntax_test/b02a: CompileTimeError # Issue 21411
-async_await_syntax_test/b03a: CompileTimeError # Issue 21411
-async_await_syntax_test/b04a: CompileTimeError # Issue 21411
-async_await_syntax_test/b05a: CompileTimeError # Issue 21411
-async_await_syntax_test/b06a: CompileTimeError # Issue 21411
-async_await_syntax_test/b07a: CompileTimeError # Issue 21411
-async_await_syntax_test/b08a: CompileTimeError # Issue 21411
-async_await_syntax_test/b09a: CompileTimeError # Issue 21411
-async_await_syntax_test/b10a: CompileTimeError # Issue 21411
-async_await_syntax_test/b11b: CompileTimeError # Issue 21411
-async_await_syntax_test/b11c: CompileTimeError # Issue 21411
-async_await_syntax_test/b11d: CompileTimeError # Issue 21411
-async_await_syntax_test/c01a: CompileTimeError # Issue 21411
-async_await_syntax_test/c02a: CompileTimeError # Issue 21411
-async_await_syntax_test/c03a: CompileTimeError # Issue 21411
-async_await_syntax_test/c04a: CompileTimeError # Issue 21411
-async_await_syntax_test/c05a: CompileTimeError # Issue 21411
-async_await_syntax_test/c06a: CompileTimeError # Issue 21411
-async_await_syntax_test/c07a: CompileTimeError # Issue 21411
-async_await_syntax_test/c08a: CompileTimeError # Issue 21411
-async_await_syntax_test/c09a: CompileTimeError # Issue 21411
-async_await_syntax_test/c10a: CompileTimeError # Issue 21411
-async_await_syntax_test/d01a: CompileTimeError # Issue 21411
-async_await_syntax_test/d02a: CompileTimeError # Issue 21411
-async_await_syntax_test/d03a: CompileTimeError # Issue 21411
-async_await_syntax_test/d04a: CompileTimeError # Issue 21411
-async_await_syntax_test/d05a: CompileTimeError # Issue 21411
-async_await_syntax_test/d06a: CompileTimeError # Issue 21411
-async_await_syntax_test/d07a: CompileTimeError # Issue 21411
-async_await_syntax_test/d08a: CompileTimeError # Issue 21411
-async_await_syntax_test/d08b: CompileTimeError # Issue 21411
-async_await_syntax_test/d09a: CompileTimeError # Issue 21411
-async_await_syntax_test/d10a: CompileTimeError # Issue 21411
-async_await_syntax_test/a04c: CompileTimeError # Issue 22259
-async_await_syntax_test/a03b: CompileTimeError # Issue 22259
+syncstar_yield_test/copyParameters: RuntimeError # Issue 22322
+sync_generator2_test/07: MissingCompileTimeError # Issue 22324
+sync_generator2_test/08: MissingCompileTimeError # Issue 22324
+sync_generator2_test/10: MissingCompileTimeError # Issue 22324
+sync_generator2_test/20: MissingCompileTimeError # Issue 22324
+sync_generator2_test/30: MissingCompileTimeError # Issue 22324
+sync_generator2_test/51: MissingCompileTimeError # Issue 22324
+sync_generator2_test/52: MissingCompileTimeError # Issue 22324
 
+[ $compiler == dart2js && $runtime == jsshell ]
+await_for_test: Skip # Jsshell does not provide periodic timers, Issue 7728
 
-[ $compiler == dart2js && $unchecked ]
-async_test/type-mismatch1: CompileTimeError # Issue 21411
-async_test/type-mismatch2: CompileTimeError # Issue 21411
-async_test/type-mismatch3: CompileTimeError # Issue 21411
-async_test/type-mismatch4: CompileTimeError # Issue 21411
+[ $compiler == dart2js && $csp && $browser ]
+deferred_mixin_test: RuntimeError # Issue 21863
 
 [ $compiler == dart2js || $compiler == dart2dart ]
 symbol_literal_test/*: Fail # Issue 21825
@@ -272,17 +219,22 @@
 
 [ $compiler == dart2dart && $minified && $builder_tag != new_backend ]
 type_variable_conflict2_test/01: RuntimeError # Issue 16180
+sync_generator3_test/test2: Fail
 
 [ $compiler == dart2dart ]
-async_await_syntax_test/a04c: Fail
-async_await_syntax_test/a03b: Fail
-sync_generator2_test/07: Fail
-sync_generator2_test/08: Fail
-sync_generator2_test/10: Fail
-sync_generator2_test/20: Fail
-sync_generator2_test/30: Fail
-sync_generator2_test/51: Fail
-sync_generator2_test/52: Fail
+asyncstar_concat_test: CompileTimeError # Issue 21404
+asyncstar_yield_test: CompileTimeError # Issue 21404
+asyncstar_yieldstar_test: CompileTimeError # Issue 21404
+
+sync_generator2_test/07: MissingCompileTimeError # Issue 22324
+sync_generator2_test/08: MissingCompileTimeError # Issue 22324
+sync_generator2_test/10: MissingCompileTimeError # Issue 22324
+sync_generator2_test/20: MissingCompileTimeError # Issue 22324
+sync_generator2_test/30: MissingCompileTimeError # Issue 22324
+sync_generator2_test/51: MissingCompileTimeError # Issue 22324
+sync_generator2_test/52: MissingCompileTimeError # Issue 22324
+await_for_cancel_test: RuntimeError # Issue 21404
+
 
 regress_13494_test: Fail # Issue 13494
 
@@ -337,8 +289,6 @@
 function_subtype_call2_test: RuntimeError # Issue 21673
 
 [ $compiler == dart2dart && $minified ]
-sync_generator1_test: Fail
-sync_generator2_test/none: Fail
 cyclic_type_test/0*: Fail # Issue 12605.
 cyclic_type2_test: Fail # Issue 12605.
 super_getter_setter_test: Fail # Issue 11065.
diff --git a/tests/language/sync_generator1_test.dart b/tests/language/sync_generator1_test.dart
index 8da1a88..6c2b7d7 100644
--- a/tests/language/sync_generator1_test.dart
+++ b/tests/language/sync_generator1_test.dart
@@ -35,13 +35,18 @@
 }
 
 einsZwei() sync* {
-  yield* 1;
+  yield 1;
   yield* [2, 3];
   yield* [];
   yield 5;
   yield [6];
 }
 
+dreiVier() sync* {
+  // Throws type error: yielded object is not an iterable.
+  yield* 3; /// 01: static type warning
+}
+
 main() {
   for (int i = 0; i < 10; i++) {
     var sums = sum10();
@@ -74,5 +79,7 @@
 
     print(einsZwei());
     Expect.equals("(1, 2, 3, 5, [6])", einsZwei().toString());
+
+    Expect.throws(() => dreiVier().toString()); /// 01: continued
   }
 }
diff --git a/tests/language/sync_generator2_test.dart b/tests/language/sync_generator2_test.dart
index ca6920c..4e5aa08 100644
--- a/tests/language/sync_generator2_test.dart
+++ b/tests/language/sync_generator2_test.dart
@@ -59,6 +59,7 @@
   x = test04;  /// 40: continued
   test04 = x;  /// 41: continued
   x = new K();
+  print(x.sync().toList());
   Expect.equals(1, x.sync().length);
-  Expect.isTrue(x.sync().single is Function);
+//  Expect.isTrue(x.sync().single is Function);
 }
\ No newline at end of file
diff --git a/tests/language/sync_generator3_test.dart b/tests/language/sync_generator3_test.dart
new file mode 100644
index 0000000..d84275e
--- /dev/null
+++ b/tests/language/sync_generator3_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test program for sync* generator functions and yielding in try blocks.
+
+import "package:expect/expect.dart";
+
+f() sync* {
+  try {
+    yield 1;
+    throw "three";
+  } catch (e) {
+    yield 2;
+    yield e;
+  } finally {
+    yield 4;
+  }
+}
+
+test1() {
+  var s = f().toString();
+  Expect.equals("(1, 2, three, 4)", s);
+  print(s);
+}
+
+g() sync* {
+  try {
+    yield "a";
+    throw "pow!";
+  } finally {
+    yield "b";
+  }
+}
+
+test2() {
+  var s;
+  try {
+    s = g().toString();
+  } catch (e) {
+    Expect.equals("pow!", e);
+    Expect.equals("(a, b)", s);
+    print(s);
+  }
+}
+
+main() {
+  test1();  /// test1: ok
+  test2();  /// test2: ok
+}
\ No newline at end of file
diff --git a/tests/language/syncstar_yield_test.dart b/tests/language/syncstar_yield_test.dart
new file mode 100644
index 0000000..60cb609
--- /dev/null
+++ b/tests/language/syncstar_yield_test.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+Iterable<int> foo1() sync* {
+  yield 1;
+}
+
+Iterable<int> foo2(p) sync* {
+  bool t = false;
+  yield null;
+  while (true) {
+    a: for (int i = 0; i < p; i++) {
+      if (!t) {
+        for (int j = 0; j < 3; j++) {
+          yield -1;
+          t = true;
+          break a;
+        }
+      }
+      yield i;
+    }
+  }
+}
+
+// p is copied to all Iterators from the Iterable returned by foo3.
+// Also each iterator will have its own i.
+Iterable<int> foo3(int p) sync* {
+  int i = 0;
+  i++;
+  p++;
+  yield p + i;
+}
+
+main() {
+  Expect.listEquals([1], foo1().toList());
+  Expect.listEquals([null, -1, 0, 1, 2, 3, 0, 1, 2, 3],
+      foo2(4).take(10).toList());
+  Iterable t = foo3(0);
+  Iterator it1 = t.iterator;
+  Iterator it2 = t.iterator; /// copyParameters: ok
+  it1.moveNext();
+  it2.moveNext(); /// copyParameters: continued
+  Expect.equals(2, it1.current);
+  // TODO(sigurdm): Check up on the spec here.
+  Expect.equals(2, it2.current);  /// copyParameters: continued
+  Expect.isFalse(it1.moveNext());
+  // Test that two `moveNext()` calls are fine.
+  Expect.isFalse(it1.moveNext());
+  Expect.isFalse(it2.moveNext()); /// copyParameters: continued
+  Expect.isFalse(it2.moveNext()); /// copyParameters: continued
+}
diff --git a/tests/language/syncstar_yieldstar_test.dart b/tests/language/syncstar_yieldstar_test.dart
new file mode 100644
index 0000000..9da3936
--- /dev/null
+++ b/tests/language/syncstar_yieldstar_test.dart
@@ -0,0 +1,25 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+
+bar() sync* {
+  int i = 1;
+  int j = 1;
+  while (true) {
+    yield i;
+    j = i + j;
+    i = j - i;
+  }
+}
+
+foo() sync* {
+  yield* [1, 2, 3];
+  yield null;
+  yield* bar();
+}
+
+main () async {
+  Expect.listEquals([1, 2, 3, null, 1, 1, 2, 3, 5], foo().take(9).toList());
+}
\ No newline at end of file
diff --git a/tests/language/vm/optimized_stacktrace_test.dart b/tests/language/vm/optimized_stacktrace_test.dart
new file mode 100644
index 0000000..c5f98c9
--- /dev/null
+++ b/tests/language/vm/optimized_stacktrace_test.dart
@@ -0,0 +1,42 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// VMOptions=--stacktrace-every=3 --optimization-counter-threshold=10 --enable-inlining-annotations
+
+// Test generating stacktraces with inlining and deferred code.
+// Regression test for issue dartbug.com/22331
+
+class A {
+  final N;
+  final inc;
+  var next;
+  A(this.N, this.inc) {
+    next = this;
+  }
+}
+
+foo(o, value) {
+  for (var i = 0; i < o.N; i += o.inc) {
+    if (value < i) {
+      throw "";
+    }
+    o = o.next;
+  }
+  return value;
+}
+
+const NeverInline = 'NeverInline';
+
+@NeverInline baz(x, y, z) => z;
+
+bar(o) {
+  var value = 0x100000000 + o.inc;
+  baz(0, 0, foo(o, value));
+}
+
+main() {
+  var o = new A(10, 1);
+  for (var i = 0; i < 100; i++) bar(o);
+  bar(new A(100000, 1));
+}
+
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index aa1527b..c23efbe 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -246,6 +246,7 @@
 async/multiple_timer_test: Fail, Pass # Issue 15487
 async/stream_periodic3_test: Fail, Pass # Issue 15487
 async/timer_isolate_test: Fail, Pass # Issue 15487. Issue 13921: spawnFunction is not allowed on Dartium's DOM thread.
+async/timer_isActive_test: Fail, Pass # Issue 22352
 mirrors/immutable_collections_test: Skip # Dartium debug uses -O0, so this will just timeout and waste bot cycles.
 mirrors/mirrors_reader_test: Skip # Dartium debug uses -O0, so this will just timeout and waste bot cycles.
 mirrors/library_uri_io_test: Skip # Not intended for drt as it uses dart:io.
@@ -325,3 +326,7 @@
 
 [ $mode == debug && $arch != ia32 && $arch != x64 && $arch != simarm ]
 convert/streamed_conversion_json_utf8_decode_test: Skip  # Verification not yet implemented.
+
+[ $runtime == vm && $mode == debug && $builder_tag == asan ]
+mirrors/immutable_collections_test: Skip  # Timeout.
+convert/streamed_conversion_json_utf8_decode_test: Skip  # Timeout.
diff --git a/tests/standalone/debugger/deferred_code_test.dart b/tests/standalone/debugger/deferred_code_test.dart
index c1ea8c0..c4578ab 100644
--- a/tests/standalone/debugger/deferred_code_test.dart
+++ b/tests/standalone/debugger/deferred_code_test.dart
@@ -39,6 +39,10 @@
   MatchLine(22),
   MatchLocals({"loaded": "false"}),
   SetBreakpoint(10, url: "deferred_code_lib.dart"),
+  // Regression test: we used to have a bug that hung the debugger when
+  // processing latent brakepoints for urls that have no match.
+  // The BP below will not match any loaded file.
+  SetBreakpoint(10, url: "non_existing_file.dart"),
   Resume(),
   MatchFrame(0, "stopTheBuck"),  // Expect to be stopped in deferred library code.
   // MatchLine(10), // Line matching only works for the main script.
diff --git a/tests/standalone/io/file_test.dart b/tests/standalone/io/file_test.dart
index 633fe38..c241a5b 100644
--- a/tests/standalone/io/file_test.dart
+++ b/tests/standalone/io/file_test.dart
@@ -134,8 +134,6 @@
 
   // Test for file stream buffered handling of large files.
   static void testReadWriteStreamLargeFile() {
-    asyncTestStarted();
-
     // Create the test data - arbitrary binary data.
     List<int> buffer = new List<int>(100000);
     for (var i = 0; i < buffer.length; ++i) {
@@ -148,44 +146,56 @@
     output.add(buffer);
     output.add(buffer);
     output.flush().then((_) => output.close());
+
+    asyncTestStarted();
     output.done.then((_) {
       Stream input = file.openRead();
       int position = 0;
       final int expectedLength = 200000;
+
       // Start an independent asynchronous check on the length.
-      asyncTestStarted();
-      file.length().then((len) {
-        Expect.equals(expectedLength, len);
-        asyncTestDone('testReadWriteStreamLargeFile: length check');
-      });
+      Future lengthTest() {
+        asyncTestStarted();
+        return file.length().then((len) {
+          Expect.equals(expectedLength, len);
+          asyncTestDone('testReadWriteStreamLargeFile: length check');
+        });
+      }
 
       // Immediate read should read 0 bytes.
-      input.listen(
-        (d) {
-          for (int i = 0; i < d.length; ++i) {
-            Expect.equals(buffer[(i + position) % buffer.length], d[i]);
-          }
-          position += d.length;
-        },
-        onError: (error, trace) {
-          print('Error on input in testReadWriteStreamLargeFile');
-          print('with error $error');
-          if (trace != null) print("StackTrace: $trace");
-          throw error;
-        },
-        onDone: () {
-          Expect.equals(expectedLength, position);
-          testPipe(file, buffer)
-              .then((_) => file.delete())
-              .then((_) {
-                  asyncTestDone('testReadWriteStreamLargeFile: main test');
-              })
-              .catchError((e, trace) {
-                print('Exception while deleting ReadWriteStreamLargeFile file');
-                print('Exception $e');
-                if (trace != null) print("StackTrace: $trace");
-              });
-        });
+      Future contentTest() {
+        asyncTestStarted();
+        var completer = new Completer();
+        input.listen(
+          (data) {
+            for (int i = 0; i < data.length; ++i) {
+              Expect.equals(buffer[(i + position) % buffer.length], data[i]);
+            }
+            position += data.length;
+          },
+          onError: (error, trace) {
+            print('Error on input in testReadWriteStreamLargeFile');
+            print('with error $error');
+            if (trace != null) print("StackTrace: $trace");
+            throw error;
+          },
+          onDone: () {
+            Expect.equals(expectedLength, position);
+            testPipe(file, buffer).then((_) {
+              asyncTestDone('testReadWriteStreamLargeFile: main test');
+            }).catchError((error, trace) {
+              print('Exception while deleting ReadWriteStreamLargeFile file');
+              print('Exception $error');
+              if (trace != null) print("StackTrace: $trace");
+              throw error;
+            }).whenComplete(completer.complete);
+          });
+        return completer.future;
+      }
+
+      return Future.forEach([lengthTest, contentTest], (test) => test());
+    }).whenComplete(file.delete).whenComplete(() {
+      asyncTestDone('testReadWriteStreamLargeFile finished');
     });
   }
 
diff --git a/tests/standalone/io/socket_bind_test.dart b/tests/standalone/io/socket_bind_test.dart
new file mode 100644
index 0000000..9d36c22
--- /dev/null
+++ b/tests/standalone/io/socket_bind_test.dart
@@ -0,0 +1,203 @@
+// 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 'dart:async';
+import 'dart:io';
+import 'dart:convert';
+
+import 'package:async_helper/async_helper.dart';
+import 'package:expect/expect.dart';
+
+testBindShared(String host, bool v6Only) {
+  asyncStart();
+  ServerSocket.bind(
+      host, 0, v6Only: v6Only, shared: true).then((socket) {
+    Expect.isTrue(socket.port > 0);
+
+    asyncStart();
+    return ServerSocket.bind(
+        host, socket.port, v6Only: v6Only, shared: true).then((socket2) {
+      Expect.equals(socket.address.address, socket2.address.address);
+      Expect.equals(socket.port, socket2.port);
+      socket.close().whenComplete(asyncEnd);
+      socket2.close().whenComplete(asyncEnd);
+    });
+  });
+}
+
+negTestBindSharedMismatch(String host, bool v6Only) {
+  asyncStart();
+  ServerSocket.bind(host, 0, v6Only: v6Only).then((ServerSocket socket) {
+    Expect.isTrue(socket.port > 0);
+
+    asyncStart();
+    return ServerSocket.bind(
+        host, socket.port, v6Only: v6Only).catchError((error) {
+      Expect.isTrue(error is SocketException);
+      Expect.isTrue('$error'.contains('shared flag'));
+      socket.close().whenComplete(asyncEnd);
+      asyncEnd();
+    });
+  });
+}
+
+negTestBindV6OnlyMismatch(String host, bool v6Only) {
+  asyncStart();
+  ServerSocket.bind(
+      host, 0, v6Only: v6Only, shared: true).then((ServerSocket socket) {
+    Expect.isTrue(socket.port > 0);
+
+    asyncStart();
+    return ServerSocket.bind(
+        host, socket.port, v6Only: !v6Only, shared: true)
+        .catchError((error) {
+      Expect.isTrue(error is SocketException);
+      Expect.isTrue('$error'.contains('v6Only flag'));
+      socket.close().whenComplete(asyncEnd);
+      asyncEnd();
+    });
+  });
+}
+
+Future testBindDifferentAddresses(InternetAddress addr1,
+                                  InternetAddress addr2,
+                                  bool addr1V6Only,
+                                  bool addr2V6Only) {
+  asyncStart();
+  return ServerSocket.bind(
+      addr1, 0, v6Only: addr1V6Only, shared: false).then((socket) {
+    Expect.isTrue(socket.port > 0);
+
+    asyncStart();
+    return ServerSocket.bind(
+        addr2, socket.port, v6Only: addr2V6Only, shared: false).then((socket2) {
+      Expect.equals(socket.port, socket2.port);
+
+      return Future.wait([
+          socket.close().whenComplete(asyncEnd),
+          socket2.close().whenComplete(asyncEnd),
+      ]);
+    });
+  });
+}
+
+testSocketReferenceInteroperability(String host) {
+  asyncStart();
+    ServerSocket.bind(host, 0).then((ServerSocket socket) {
+      Expect.isTrue(socket.port > 0);
+
+      asyncStart();
+      socket.reference.create().then((socket2) {
+        bool gotResponseFrom1;
+        bool gotResponseFrom2;
+
+        Expect.isTrue(socket.port > 0);
+        Expect.equals(socket.port, socket2.port);
+
+        asyncStart();
+        asyncStart();
+        asyncStart();
+        socket.listen((client) {
+          client.drain().whenComplete(asyncEnd);
+          client.write('1: hello world');
+          client.close().whenComplete(asyncEnd);
+          // NOTE: Closing the socket un-subscribes as well, which means the
+          // other client connection must go to the other socket.
+          socket.close().whenComplete(asyncEnd);
+        }, onDone: asyncEnd);
+
+        asyncStart();
+        asyncStart();
+        asyncStart();
+        socket2.listen((client) {
+          client.drain().whenComplete(asyncEnd);
+          client.write('2: hello world');
+          client.close().whenComplete(asyncEnd);
+          // NOTE: Closing the socket un-subscribes as well, which means the
+          // other client connection must go to the other socket.
+          socket2.close().whenComplete(asyncEnd);
+        }, onDone: asyncEnd);
+
+        var futures = [];
+        for (int i = 0; i < 2; i++) {
+          asyncStart();
+          futures.add(
+              Socket.connect(socket.address, socket.port).then((Socket socket) {
+            socket.close().whenComplete(asyncEnd);
+            asyncStart();
+            return socket
+                .transform(ASCII.decoder).join('').then((String result) {
+                if (result == '1: hello world') gotResponseFrom1 = true;
+                else if (result == '2: hello world') gotResponseFrom2 = true;
+                else throw 'Unexpected result from server: $result';
+                asyncEnd();
+             });
+          }));
+        }
+        asyncStart();
+        Future.wait(futures).then((_) {
+          Expect.isTrue(gotResponseFrom1);
+          Expect.isTrue(gotResponseFrom2);
+          asyncEnd();
+        });
+      });
+   });
+}
+
+testListenCloseListenClose(String host) async {
+  asyncStart();
+
+  ServerSocket socket =
+      await ServerSocket.bind(host, 0, shared: true);
+  ServerSocket socket2 =
+      await ServerSocket.bind(host, socket.port, shared: true);
+
+  var subscription = socket.listen((_) { throw 'error'; });
+  subscription.cancel();
+  await socket.close();
+
+  // The second socket should have kept the OS socket alive. We can therefore
+  // test if it is working correctly.
+  asyncStart();
+  socket2.first.then((socket) async {
+    await socket.drain();
+    await socket.close();
+    asyncEnd();
+  });
+
+  Socket client = await Socket.connect(host, socket2.port);
+  await client.close();
+  await client.drain();
+
+  asyncEnd();
+}
+
+void main() {
+  for (var host in ['127.0.0.1', '::1']) {
+    testBindShared(host, false);
+    testBindShared(host, true);
+
+    negTestBindSharedMismatch(host, false);
+    negTestBindSharedMismatch(host, true);
+
+    negTestBindV6OnlyMismatch(host, true);
+    negTestBindV6OnlyMismatch(host, false);
+
+    testSocketReferenceInteroperability(host);
+
+    testListenCloseListenClose(host);
+  }
+
+  asyncStart();
+  testBindDifferentAddresses(InternetAddress.ANY_IP_V6,
+                             InternetAddress.ANY_IP_V4,
+                             true,
+                             false).then((_) {
+    testBindDifferentAddresses(InternetAddress.ANY_IP_V4,
+                               InternetAddress.ANY_IP_V6,
+                               false,
+                               true);
+    asyncEnd();
+  });
+}
diff --git a/tests/standalone/io/socket_source_address_test.dart b/tests/standalone/io/socket_source_address_test.dart
new file mode 100644
index 0000000..a341568
--- /dev/null
+++ b/tests/standalone/io/socket_source_address_test.dart
@@ -0,0 +1,145 @@
+// 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 "dart:async";
+import "dart:io";
+
+import "package:async_helper/async_helper.dart";
+import "package:expect/expect.dart";
+
+Future throws(Function f, Function check) async {
+  try {
+    await f();
+    Expect.fail('Did not throw');
+  } catch (e) {
+    if (check != null) {
+      if (!check(e)) {
+        Expect.fail('Unexpected: $e');
+      }
+    }
+  }
+}
+
+Future testArguments(connectFunction) async {
+  asyncStart();
+  var server = await ServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0);
+  server.listen((_) { throw 'Connection unexpected'; },
+                onDone: () => asyncEnd());
+
+  asyncStart();
+  // Illegal type for sourceAddress.
+  for (var sourceAddress in ['www.google.com', 'abc']) {
+    await throws(() => connectFunction('127.0.0.1',
+                                       server.port,
+                                       sourceAddress: sourceAddress),
+                 (e) => e is ArgumentError);
+  }
+  // Unsupported local address.
+  for (var sourceAddress in ['8.8.8.8', new InternetAddress('8.8.8.8')]) {
+    await throws(() => connectFunction('127.0.0.1',
+                                       server.port,
+                                       sourceAddress: sourceAddress),
+                 (e) => e is SocketException);
+  }
+  // Address family mismatch.
+  for (var sourceAddress in ['::1', InternetAddress.LOOPBACK_IP_V6]) {
+    await throws(() => connectFunction('127.0.0.1',
+                                       server.port,
+                                       sourceAddress: sourceAddress),
+                 (e) => e is SocketException);
+  }
+  asyncEnd();
+  server.close();
+}
+
+// IPv4 addresses to use as source address when connecting locally.
+var ipV4SourceAddresses = [InternetAddress.LOOPBACK_IP_V4,
+                           InternetAddress.ANY_IP_V4,
+                           '127.0.0.1',
+                           '0.0.0.0'];
+
+// IPv6 addresses to use as source address when connecting locally.
+var ipV6SourceAddresses = [InternetAddress.LOOPBACK_IP_V6,
+                           InternetAddress.ANY_IP_V6,
+                           '::1',
+                           '::'];
+
+Future testConnect(InternetAddress bindAddress,
+                   bool v6Only,
+                   Function connectFunction,
+                   Function closeDestroyFunction) async {
+  var successCount = 0;
+  if (!v6Only) successCount += ipV4SourceAddresses.length;
+  if (bindAddress.type == InternetAddressType.IP_V6) {
+     successCount += ipV6SourceAddresses.length;
+  }
+  var count = 0;
+  var allConnected = new Completer();
+  if (successCount == 0) allConnected.complete();
+
+  asyncStart();
+  var server = await ServerSocket.bind(bindAddress, 0, v6Only: v6Only);
+  server.listen((s) {
+    s.destroy();
+    count++;
+    if (count == successCount) allConnected.complete();
+  }, onDone: () => asyncEnd());
+
+  asyncStart();
+
+  // Connect with IPv4 source addesses.
+  for (var sourceAddress in ipV4SourceAddresses) {
+    if (!v6Only) {
+      var s = await connectFunction(InternetAddress.LOOPBACK_IP_V4,
+                                   server.port,
+                                   sourceAddress: sourceAddress);
+      closeDestroyFunction(s);
+    } else {
+      // Cannot use an IPv6 source address to connect to IPv6 if
+      // v6Only is specified.
+      await throws(() => connectFunction(InternetAddress.LOOPBACK_IP_V4,
+                                         server.port,
+                                         sourceAddress: sourceAddress),
+                   (e) => e is SocketException);
+    }
+  }
+
+  // Connect with IPv6 source addesses.
+  for (var sourceAddress in ipV6SourceAddresses) {
+    if (bindAddress.type == InternetAddressType.IP_V6) {
+      var s = await connectFunction(InternetAddress.LOOPBACK_IP_V6,
+                                    server.port,
+                                    sourceAddress: sourceAddress);
+      closeDestroyFunction(s);
+    } else {
+      // Cannot use an IPv6 source address to connect to IPv4.
+      await throws(() => connectFunction(InternetAddress.LOOPBACK_IP_V6,
+                                         server.port,
+                                         sourceAddress: sourceAddress),
+                   (e) => e is SocketException);
+    }
+  }
+
+  await allConnected.future;
+  await server.close();
+  asyncEnd();
+}
+
+main() {
+  testArguments(RawSocket.connect);
+  testArguments(Socket.connect);
+  testConnect(
+      InternetAddress.ANY_IP_V4, false, RawSocket.connect, (s) => s.close());
+  testConnect(
+      InternetAddress.ANY_IP_V4, false, Socket.connect, (s) => s.destroy());
+  testConnect(
+      InternetAddress.ANY_IP_V6, false, RawSocket.connect, (s) => s.close());
+  testConnect(
+      InternetAddress.ANY_IP_V6, false, Socket.connect, (s) => s.destroy());
+  testConnect(
+      InternetAddress.ANY_IP_V6, true, RawSocket.connect, (s) => s.close());
+  testConnect(
+      InternetAddress.ANY_IP_V6, true, Socket.connect, (s) => s.destroy());
+}
diff --git a/tests/standalone/io/web_socket_test.dart b/tests/standalone/io/web_socket_test.dart
index d9229b6..97059042 100644
--- a/tests/standalone/io/web_socket_test.dart
+++ b/tests/standalone/io/web_socket_test.dart
@@ -8,6 +8,7 @@
 // VMOptions=--short_socket_read --short_socket_write
 
 import "dart:async";
+import "dart:convert";
 import "dart:io";
 import "dart:typed_data";
 
@@ -452,9 +453,6 @@
       var url = '${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}/';
       var headers = {'My-Header': 'my-value',
                      'My-Header-Multiple': ['my-value-1', 'my-value-2']};
-      print(headers);
-      print(headers['My-Header-Multiple'] is Iterable);
-      print(headers['My-Header-Multiple'].length);
       WebSocket.connect(url, headers: headers).then((websocket) {
         return websocket.listen((message) {
           Expect.equals("Hello", message);
@@ -468,6 +466,40 @@
   }
 
 
+  void testBasicAuthentication() {
+    var userInfo = 'user:password';
+
+    asyncStart();
+    asyncStart();
+    createServer().then((server) {
+      server.listen((request) {
+        Expect.isTrue(WebSocketTransformer.isUpgradeRequest(request));
+        String auth =
+              CryptoUtils.bytesToBase64(UTF8.encode(userInfo));
+        Expect.equals('Basic $auth', request.headers['Authorization'][0]);
+        Expect.equals(1, request.headers['Authorization'].length);
+        WebSocketTransformer.upgrade(request).then((webSocket) {
+          webSocket.listen((_) { throw 'Unexpected'; },
+                           onDone: () { asyncEnd(); });
+          webSocket.add("Hello");
+        });
+      });
+
+      var url =
+          '${secure ? "wss" : "ws"}://$userInfo@$HOST_NAME:${server.port}/';
+      WebSocket.connect(url).then((websocket) {
+        return websocket.listen((message) {
+          Expect.equals("Hello", message);
+          return websocket.close();
+        }).asFuture();
+      }).then((_) {
+        return server.close();
+      }).whenComplete(() {
+        asyncEnd();
+      });
+    });
+  }
+
   void runTests() {
     testRequestResponseClientCloses(2, null, null, 1);
     testRequestResponseClientCloses(2, 3001, null, 2);
@@ -490,6 +522,7 @@
     testIndividualUpgrade(5);
     testFromUpgradedSocket();
     testAdditionalHeaders();
+    testBasicAuthentication();
   }
 }
 
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index eed5e47..18861b0 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -158,8 +158,11 @@
 [ $system == windows ]
 io/skipping_dart2js_compilations_test: Fail # Issue 19551.
 
-[ $system != linux ]
-io/server_socket_reference_issue21383_and_issue21384_test: Skip # Not supported on other platforms so far
-
 [ $arch != ia32 && $arch != x64 && $arch != simarm && $mode == debug ]
 verified_mem_test: Skip  # Not yet implemented.
+
+[ $runtime == vm && $mode == debug && $builder_tag == asan ]
+full_coverage_test: Skip  # Timeout.
+io/file_lock_test: Skip  # Timeout.
+io/test_runner_test: Skip  # Timeout.
+io/http_client_stays_alive_test: Skip  # Timeout.
diff --git a/tools/VERSION b/tools/VERSION
index 501caa3..314d74d 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 1
 MINOR 9
 PATCH 0
-PRERELEASE 7
-PRERELEASE_PATCH 1
+PRERELEASE 8
+PRERELEASE_PATCH 0
diff --git a/tools/archive_crash.py b/tools/archive_crash.py
index bac1db1..5da173c 100755
--- a/tools/archive_crash.py
+++ b/tools/archive_crash.py
@@ -17,7 +17,7 @@
 import utils
 import uuid
 
-GCS_FOLDER = 'dart-crashes'
+GCS_FOLDER = 'dart-temp-crash-archive'
 GSUTIL='/b/build/scripts/slave/gsutil'
 
 def CreateTarball(input_dir, tarname):
diff --git a/tools/dartium/update_deps.py b/tools/dartium/update_deps.py
index 742a114..128a82e 100755
--- a/tools/dartium/update_deps.py
+++ b/tools/dartium/update_deps.py
@@ -40,7 +40,7 @@
 # Repositories to auto-update
 ########################################################################
 
-BRANCH_CURRENT="dart/2125"
+BRANCH_CURRENT="dart/dartium"
 BRANCH_NEXT="dart/dartium"
 BRANCH_MULTIVM="dart/multivm"
 
@@ -48,8 +48,8 @@
   'dartium': (
     'https://dart.googlecode.com/svn/branches/bleeding_edge/deps/dartium.deps',
     'dartium',
-    # TODO(vsm): 'chromium' will move to git
-    ['webkit', 'chromium'],
+    # TODO(vsm): Reenable 'chromium'
+    ['webkit'],
     BRANCH_CURRENT,
     ),
   'integration': (
diff --git a/tools/dom/src/shared_html.dart b/tools/dom/src/shared_html.dart
index dd67c48..b6c9c62c 100644
--- a/tools/dom/src/shared_html.dart
+++ b/tools/dom/src/shared_html.dart
@@ -30,7 +30,7 @@
  */
 @deprecated
 @Experimental()
-ElementList queryAll(String relativeSelectors) => document.queryAll(relativeSelectors);
+ElementList<Element> queryAll(String relativeSelectors) => document.queryAll(relativeSelectors);
 
 /**
  * Finds the first descendant element of this document that matches the
@@ -67,7 +67,7 @@
  * For details about CSS selector syntax, see the
  * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
  */
-ElementList querySelectorAll(String selectors) => document.querySelectorAll(selectors);
+ElementList<Element> querySelectorAll(String selectors) => document.querySelectorAll(selectors);
 
 /// A utility for changing the Dart wrapper type for elements.
 abstract class ElementUpgrader {
diff --git a/tools/dom/templates/html/impl/impl_Document.darttemplate b/tools/dom/templates/html/impl/impl_Document.darttemplate
index 6767115..0640f99 100644
--- a/tools/dom/templates/html/impl/impl_Document.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Document.darttemplate
@@ -25,7 +25,7 @@
    * For details about CSS selector syntax, see the
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
-  ElementList querySelectorAll(String selectors) {
+  ElementList<Element> querySelectorAll(String selectors) {
     return new _FrozenElementList._wrap(_querySelectorAll(selectors));
   }
 
@@ -45,7 +45,7 @@
   @deprecated
   @Experimental()
   @DomName('Document.querySelectorAll')
-  ElementList queryAll(String relativeSelectors) =>
+  ElementList<Element> queryAll(String relativeSelectors) =>
       querySelectorAll(relativeSelectors);
 
   /// Checks if [registerElement] is supported on the current platform.
diff --git a/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate b/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate
index 42cc56f..e401956 100644
--- a/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate
+++ b/tools/dom/templates/html/impl/impl_DocumentFragment.darttemplate
@@ -57,7 +57,7 @@
    * For details about CSS selector syntax, see the
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
-  ElementList querySelectorAll(String selectors) =>
+  ElementList<Element> querySelectorAll(String selectors) =>
     new _FrozenElementList._wrap(_querySelectorAll(selectors));
 
 
@@ -115,7 +115,7 @@
   @deprecated
   @Experimental()
   @DomName('DocumentFragment.querySelectorAll')
-  ElementList queryAll(String relativeSelectors) {
+  ElementList<Element> queryAll(String relativeSelectors) {
     return querySelectorAll(relativeSelectors);
   }
 $!MEMBERS
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index 22de38f..38209ae 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -594,7 +594,7 @@
    * [CSS selector specification](http://www.w3.org/TR/css3-selectors/).
    */
   @DomName('Element.querySelectorAll')
-  ElementList querySelectorAll(String selectors) =>
+  ElementList<Element> querySelectorAll(String selectors) =>
     new _FrozenElementList._wrap(_querySelectorAll(selectors));
 
   /**
@@ -613,7 +613,7 @@
   @deprecated
   @DomName('Element.querySelectorAll')
   @Experimental()
-  ElementList queryAll(String relativeSelectors) =>
+  ElementList<Element> queryAll(String relativeSelectors) =>
       querySelectorAll(relativeSelectors);
 
   /**