Version 1.13.0-dev.2.0

Merge commit 'c99e5c02fa68a6c583c746b34768e9f6882185b8' into dev
diff --git a/pkg/analysis_server/doc/api.html b/pkg/analysis_server/doc/api.html
index 7170448..901200f 100644
--- a/pkg/analysis_server/doc/api.html
+++ b/pkg/analysis_server/doc/api.html
@@ -1511,6 +1511,7 @@
   "params": {
     "<b>file</b>": <a href="#type_FilePath">FilePath</a>
     "<b>offset</b>": int
+    "<b>superOnly</b>": <span style="color:#999999">optional</span> bool
   }
 }</pre><br><pre>response: {
   "id": String
@@ -1536,6 +1537,12 @@
             <p>
               The offset of the name of the type within the file.
             </p>
+          </dd><dt class="field"><b><i>superOnly ( <span style="color:#999999">optional</span> bool )</i></b></dt><dd>
+            
+            <p>
+              True if the client is only requesting superclasses and
+              interfaces hierarchy.
+            </p>
           </dd></dl><h4>Returns</h4><dl><dt class="field"><b><i>hierarchyItems ( <span style="color:#999999">optional</span> List&lt;<a href="#type_TypeHierarchyItem">TypeHierarchyItem</a>&gt; )</i></b></dt><dd>
             
             <p>
diff --git a/pkg/analysis_server/lib/analysis/analysis_domain.dart b/pkg/analysis_server/lib/analysis/analysis_domain.dart
new file mode 100644
index 0000000..27e8678
--- /dev/null
+++ b/pkg/analysis_server/lib/analysis/analysis_domain.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+/**
+ * Support for client code that extends the analysis aspect of analysis server.
+ */
+library analysis_server.analysis;
+
+import 'dart:async';
+
+import 'package:analysis_server/src/plugin/server_plugin.dart';
+import 'package:analysis_server/src/protocol.dart' show AnalysisService;
+import 'package:analyzer/src/generated/engine.dart'
+    show AnalysisContext, ComputedResult;
+import 'package:analyzer/src/generated/source.dart' show Source;
+import 'package:analyzer/task/model.dart' show ResultDescriptor;
+import 'package:plugin/plugin.dart';
+
+/**
+ * The identifier of the extension point that allows plugins to get access to
+ * `AnalysisSite`. The object used as an extension must be
+ * a [SetAnalysisDomain].
+ */
+final String SET_ANALYSIS_DOMAIN_EXTENSION_POINT_ID = Plugin.join(
+    ServerPlugin.UNIQUE_IDENTIFIER,
+    ServerPlugin.SET_ANALISYS_DOMAIN_EXTENSION_POINT);
+
+/**
+ * A function that is invoked on the `analysis` domain creation.
+ */
+typedef void SetAnalysisDomain(AnalysisDomain site);
+
+/**
+ * An object that gives [SetAnalysisDomain]s access to the `analysis` domain
+ * of the analysis server.
+ *
+ * Clients are not expected to subtype this class.
+ */
+abstract class AnalysisDomain {
+  /**
+   * Return the stream that is notified when a new value for the given
+   * [descriptor] is computed.
+   */
+  Stream<ComputedResult> onResultComputed(ResultDescriptor descriptor);
+
+  /**
+   * Schedule sending the given [service] notifications for the given [source]
+   * in the given [context].
+   */
+  void scheduleNotification(
+      AnalysisContext context, Source source, AnalysisService service);
+}
diff --git a/pkg/analysis_server/lib/src/domain_analysis.dart b/pkg/analysis_server/lib/src/domain_analysis.dart
index 655ca2f..5dd9feb 100644
--- a/pkg/analysis_server/lib/src/domain_analysis.dart
+++ b/pkg/analysis_server/lib/src/domain_analysis.dart
@@ -7,16 +7,23 @@
 import 'dart:async';
 import 'dart:core' hide Resource;
 
+import 'package:analysis_server/analysis/analysis_domain.dart';
 import 'package:analysis_server/src/analysis_server.dart';
 import 'package:analysis_server/src/computer/computer_hover.dart';
 import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/context_manager.dart';
 import 'package:analysis_server/src/domains/analysis/navigation.dart';
+import 'package:analysis_server/src/operation/operation_analysis.dart'
+    show sendAnalysisNotificationNavigation;
 import 'package:analysis_server/src/protocol_server.dart';
 import 'package:analysis_server/src/services/dependencies/library_dependencies.dart';
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/generated/ast.dart';
 import 'package:analyzer/src/generated/element.dart';
 import 'package:analyzer/src/generated/engine.dart' as engine;
+import 'package:analyzer/src/generated/java_engine.dart' show CaughtException;
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/task/model.dart' show ResultDescriptor;
 
 /**
  * Instances of the class [AnalysisDomainHandler] implement a [RequestHandler]
@@ -31,7 +38,9 @@
   /**
    * Initialize a newly created handler to handle requests for the given [server].
    */
-  AnalysisDomainHandler(this.server);
+  AnalysisDomainHandler(this.server) {
+    _callAnalysisDomainReceivers();
+  }
 
   /**
    * Implement the `analysis.getErrors` request.
@@ -290,4 +299,72 @@
     server.updateOptions(updaters);
     return new AnalysisUpdateOptionsResult().toResponse(request.id);
   }
+
+  /**
+   * Call all the registered [SetAnalysisDomain] functions.
+   */
+  void _callAnalysisDomainReceivers() {
+    AnalysisDomain analysisDomain = new AnalysisDomainImpl(server);
+    for (SetAnalysisDomain function
+        in server.serverPlugin.setAnalysisDomainFunctions) {
+      try {
+        function(analysisDomain);
+      } catch (exception, stackTrace) {
+        engine.AnalysisEngine.instance.logger.logError(
+            'Exception from analysis domain receiver: ${function.runtimeType}',
+            new CaughtException(exception, stackTrace));
+      }
+    }
+  }
+}
+
+/**
+ * An implementation of [AnalysisDomain] for [AnalysisServer].
+ */
+class AnalysisDomainImpl implements AnalysisDomain {
+  final AnalysisServer server;
+
+  final Map<ResultDescriptor,
+          StreamController<engine.ComputedResult>> controllers =
+      <ResultDescriptor, StreamController<engine.ComputedResult>>{};
+
+  AnalysisDomainImpl(this.server) {
+    server.onContextsChanged.listen((ContextsChangedEvent event) {
+      event.added.forEach(_subscribeForContext);
+    });
+  }
+
+  @override
+  Stream<engine.ComputedResult> onResultComputed(ResultDescriptor descriptor) {
+    Stream<engine.ComputedResult> stream = controllers
+        .putIfAbsent(descriptor,
+            () => new StreamController<engine.ComputedResult>.broadcast())
+        .stream;
+    server.getAnalysisContexts().forEach(_subscribeForContext);
+    return stream;
+  }
+
+  @override
+  void scheduleNotification(
+      engine.AnalysisContext context, Source source, AnalysisService service) {
+    // TODO(scheglov) schedule, don't do it right now
+    String file = source.fullName;
+    if (server.hasAnalysisSubscription(service, file)) {
+      if (service == AnalysisService.NAVIGATION) {
+        sendAnalysisNotificationNavigation(server, context, source);
+      }
+    }
+  }
+
+  void _subscribeForContext(engine.AnalysisContext context) {
+    for (ResultDescriptor descriptor in controllers.keys) {
+      context.onResultComputed(descriptor).listen((result) {
+        StreamController<engine.ComputedResult> controller =
+            controllers[result.descriptor];
+        if (controller != null) {
+          controller.add(result);
+        }
+      });
+    }
+  }
 }
diff --git a/pkg/analysis_server/lib/src/domains/analysis/navigation.dart b/pkg/analysis_server/lib/src/domains/analysis/navigation.dart
index 8cb08ae..859cd7c 100644
--- a/pkg/analysis_server/lib/src/domains/analysis/navigation.dart
+++ b/pkg/analysis_server/lib/src/domains/analysis/navigation.dart
@@ -32,6 +32,7 @@
           new CaughtException(exception, stackTrace));
     }
   }
+  holder.sortRegions();
   return holder;
 }
 
@@ -66,6 +67,12 @@
     regions.add(region);
   }
 
+  void sortRegions() {
+    regions.sort((a, b) {
+      return a.offset - b.offset;
+    });
+  }
+
   int _addFile(String file) {
     int index = fileMap[file];
     if (index == null) {
diff --git a/pkg/analysis_server/lib/src/generated_protocol.dart b/pkg/analysis_server/lib/src/generated_protocol.dart
index b6fe193..4ee4157 100644
--- a/pkg/analysis_server/lib/src/generated_protocol.dart
+++ b/pkg/analysis_server/lib/src/generated_protocol.dart
@@ -4339,6 +4339,7 @@
  * {
  *   "file": FilePath
  *   "offset": int
+ *   "superOnly": optional bool
  * }
  */
 class SearchGetTypeHierarchyParams implements HasToJson {
@@ -4346,6 +4347,8 @@
 
   int _offset;
 
+  bool _superOnly;
+
   /**
    * The file containing the declaration or reference to the type for which a
    * hierarchy is being requested.
@@ -4374,9 +4377,24 @@
     this._offset = value;
   }
 
-  SearchGetTypeHierarchyParams(String file, int offset) {
+  /**
+   * True if the client is only requesting superclasses and interfaces
+   * hierarchy.
+   */
+  bool get superOnly => _superOnly;
+
+  /**
+   * True if the client is only requesting superclasses and interfaces
+   * hierarchy.
+   */
+  void set superOnly(bool value) {
+    this._superOnly = value;
+  }
+
+  SearchGetTypeHierarchyParams(String file, int offset, {bool superOnly}) {
     this.file = file;
     this.offset = offset;
+    this.superOnly = superOnly;
   }
 
   factory SearchGetTypeHierarchyParams.fromJson(JsonDecoder jsonDecoder, String jsonPath, Object json) {
@@ -4396,7 +4414,11 @@
       } else {
         throw jsonDecoder.missingKey(jsonPath, "offset");
       }
-      return new SearchGetTypeHierarchyParams(file, offset);
+      bool superOnly;
+      if (json.containsKey("superOnly")) {
+        superOnly = jsonDecoder._decodeBool(jsonPath + ".superOnly", json["superOnly"]);
+      }
+      return new SearchGetTypeHierarchyParams(file, offset, superOnly: superOnly);
     } else {
       throw jsonDecoder.mismatch(jsonPath, "search.getTypeHierarchy params", json);
     }
@@ -4411,6 +4433,9 @@
     Map<String, dynamic> result = {};
     result["file"] = file;
     result["offset"] = offset;
+    if (superOnly != null) {
+      result["superOnly"] = superOnly;
+    }
     return result;
   }
 
@@ -4425,7 +4450,8 @@
   bool operator==(other) {
     if (other is SearchGetTypeHierarchyParams) {
       return file == other.file &&
-          offset == other.offset;
+          offset == other.offset &&
+          superOnly == other.superOnly;
     }
     return false;
   }
@@ -4435,6 +4461,7 @@
     int hash = 0;
     hash = _JenkinsSmiHash.combine(hash, file.hashCode);
     hash = _JenkinsSmiHash.combine(hash, offset.hashCode);
+    hash = _JenkinsSmiHash.combine(hash, superOnly.hashCode);
     return _JenkinsSmiHash.finish(hash);
   }
 }
diff --git a/pkg/analysis_server/lib/src/plugin/server_plugin.dart b/pkg/analysis_server/lib/src/plugin/server_plugin.dart
index 6139725..c1deee0 100644
--- a/pkg/analysis_server/lib/src/plugin/server_plugin.dart
+++ b/pkg/analysis_server/lib/src/plugin/server_plugin.dart
@@ -4,6 +4,7 @@
 
 library analysis_server.src.plugin.server_plugin;
 
+import 'package:analysis_server/analysis/analysis_domain.dart';
 import 'package:analysis_server/analysis/index/index_core.dart';
 import 'package:analysis_server/analysis/navigation/navigation_core.dart';
 import 'package:analysis_server/completion/completion_core.dart';
@@ -84,6 +85,12 @@
       'navigationContributor';
 
   /**
+   * The simple identifier of the extension point that allows plugins to
+   * register analysis result listeners.
+   */
+  static const String SET_ANALISYS_DOMAIN_EXTENSION_POINT = 'setAnalysisDomain';
+
+  /**
    * The unique identifier of this plugin.
    */
   static const String UNIQUE_IDENTIFIER = 'analysis_server.core';
@@ -128,6 +135,12 @@
   ExtensionPoint navigationContributorExtensionPoint;
 
   /**
+   * The extension point that allows plugins to get access to the `analysis`
+   * domain.
+   */
+  ExtensionPoint setAnalysisDomainExtensionPoint;
+
+  /**
    * Initialize a newly created plugin.
    */
   ServerPlugin();
@@ -173,6 +186,13 @@
   List<NavigationContributor> get navigationContributors =>
       navigationContributorExtensionPoint.extensions;
 
+  /**
+   * Return a list containing all of the receivers of the `analysis` domain
+   * instance.
+   */
+  List<SetAnalysisDomain> get setAnalysisDomainFunctions =>
+      setAnalysisDomainExtensionPoint.extensions;
+
   @override
   String get uniqueIdentifier => UNIQUE_IDENTIFIER;
 
@@ -191,6 +211,9 @@
 
   @override
   void registerExtensionPoints(RegisterExtensionPoint registerExtensionPoint) {
+    setAnalysisDomainExtensionPoint = registerExtensionPoint(
+        SET_ANALISYS_DOMAIN_EXTENSION_POINT,
+        _validateSetAnalysisDomainFunction);
     analyzeFileExtensionPoint = registerExtensionPoint(
         ANALYZE_FILE_EXTENSION_POINT, _validateAnalyzeFileExtension);
     assistContributorExtensionPoint = registerExtensionPoint(
@@ -270,7 +293,7 @@
     if (extension is! ShouldAnalyzeFile) {
       String id = analyzeFileExtensionPoint.uniqueIdentifier;
       throw new ExtensionError(
-          'Extensions to $id must be an ShouldAnalyzeFile function');
+          'Extensions to $id must be a ShouldAnalyzeFile function');
     }
   }
 
@@ -343,4 +366,16 @@
           'Extensions to $id must be an NavigationContributor');
     }
   }
+
+  /**
+   * Validate the given extension by throwing an [ExtensionError] if it is not a
+   * valid analysis domain receiver.
+   */
+  void _validateSetAnalysisDomainFunction(Object extension) {
+    if (extension is! SetAnalysisDomain) {
+      String id = setAnalysisDomainExtensionPoint.uniqueIdentifier;
+      throw new ExtensionError(
+          'Extensions to $id must be a SetAnalysisDomain function');
+    }
+  }
 }
diff --git a/pkg/analysis_server/lib/src/search/search_domain.dart b/pkg/analysis_server/lib/src/search/search_domain.dart
index 7fba8d8..668077b 100644
--- a/pkg/analysis_server/lib/src/search/search_domain.dart
+++ b/pkg/analysis_server/lib/src/search/search_domain.dart
@@ -127,20 +127,34 @@
    */
   Future getTypeHierarchy(protocol.Request request) async {
     var params = new protocol.SearchGetTypeHierarchyParams.fromRequest(request);
-    await server.onAnalysisComplete;
+    String file = params.file;
+    // wait for analysis
+    if (params.superOnly == true) {
+      await server.onFileAnalysisComplete(file);
+    } else {
+      await server.onAnalysisComplete;
+    }
     // prepare element
-    List<Element> elements =
-        server.getElementsAtOffset(params.file, params.offset);
+    List<Element> elements = server.getElementsAtOffset(file, params.offset);
     if (elements.isEmpty) {
-      protocol.Response response =
-          new protocol.SearchGetTypeHierarchyResult().toResponse(request.id);
-      server.sendResponse(response);
+      _sendTypeHierarchyNull(request);
       return;
     }
     Element element = elements.first;
+    // maybe supertype hierarchy only
+    if (params.superOnly == true) {
+      TypeHierarchyComputer computer =
+          new TypeHierarchyComputer(searchEngine, element);
+      List<protocol.TypeHierarchyItem> items = computer.computeSuper();
+      protocol.Response response = new protocol.SearchGetTypeHierarchyResult(
+          hierarchyItems: items).toResponse(request.id);
+      server.sendResponse(response);
+      return;
+    }
     // prepare type hierarchy
-    TypeHierarchyComputer computer = new TypeHierarchyComputer(searchEngine);
-    List<protocol.TypeHierarchyItem> items = await computer.compute(element);
+    TypeHierarchyComputer computer =
+        new TypeHierarchyComputer(searchEngine, element);
+    List<protocol.TypeHierarchyItem> items = await computer.compute();
     protocol.Response response = new protocol.SearchGetTypeHierarchyResult(
         hierarchyItems: items).toResponse(request.id);
     server.sendResponse(response);
@@ -190,6 +204,12 @@
     server.sendResponse(response);
   }
 
+  void _sendTypeHierarchyNull(protocol.Request request) {
+    protocol.Response response =
+        new protocol.SearchGetTypeHierarchyResult().toResponse(request.id);
+    server.sendResponse(response);
+  }
+
   static protocol.SearchResult toResult(SearchMatch match) {
     return protocol.newSearchResult_fromMatch(match);
   }
diff --git a/pkg/analysis_server/lib/src/search/type_hierarchy.dart b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
index c0c38ed..e9c3772 100644
--- a/pkg/analysis_server/lib/src/search/type_hierarchy.dart
+++ b/pkg/analysis_server/lib/src/search/type_hierarchy.dart
@@ -19,35 +19,42 @@
 class TypeHierarchyComputer {
   final SearchEngine _searchEngine;
 
+  Element _pivotElement;
   LibraryElement _pivotLibrary;
   ElementKind _pivotKind;
-  bool _pivotFieldFinal;
   String _pivotName;
+  bool _pivotFieldFinal;
+  ClassElement _pivotClass;
 
   final List<TypeHierarchyItem> _items = <TypeHierarchyItem>[];
   final List<ClassElement> _itemClassElements = <ClassElement>[];
   final Map<Element, TypeHierarchyItem> _elementItemMap =
       new HashMap<Element, TypeHierarchyItem>();
 
-  TypeHierarchyComputer(this._searchEngine);
+  TypeHierarchyComputer(this._searchEngine, this._pivotElement) {
+    _pivotLibrary = _pivotElement.library;
+    _pivotKind = _pivotElement.kind;
+    _pivotName = _pivotElement.name;
+    // try to find enclosing ClassElement
+    Element element = _pivotElement;
+    if (_pivotElement is FieldElement) {
+      _pivotFieldFinal = (_pivotElement as FieldElement).isFinal;
+      element = _pivotElement.enclosingElement;
+    }
+    if (_pivotElement is ExecutableElement) {
+      element = _pivotElement.enclosingElement;
+    }
+    if (element is ClassElement) {
+      _pivotClass = element;
+    }
+  }
 
   /**
    * Returns the computed type hierarchy, maybe `null`.
    */
-  Future<List<TypeHierarchyItem>> compute(Element element) {
-    _pivotLibrary = element.library;
-    _pivotKind = element.kind;
-    _pivotName = element.name;
-    if (element is FieldElement) {
-      _pivotFieldFinal = (element as FieldElement).isFinal;
-      element = element.enclosingElement;
-    }
-    if (element is ExecutableElement &&
-        element.enclosingElement is ClassElement) {
-      element = element.enclosingElement;
-    }
-    if (element is ClassElement) {
-      InterfaceType type = element.type;
+  Future<List<TypeHierarchyItem>> compute() {
+    if (_pivotClass != null) {
+      InterfaceType type = _pivotClass.type;
       _createSuperItem(type);
       return _createSubclasses(_items[0], 0, type).then((_) {
         return new Future.value(_items);
@@ -56,6 +63,18 @@
     return new Future.value(null);
   }
 
+  /**
+   * Returns the computed super type only type hierarchy, maybe `null`.
+   */
+  List<TypeHierarchyItem> computeSuper() {
+    if (_pivotClass != null) {
+      InterfaceType type = _pivotClass.type;
+      _createSuperItem(type);
+      return _items;
+    }
+    return null;
+  }
+
   Future _createSubclasses(
       TypeHierarchyItem item, int itemId, InterfaceType type) {
     var future = getDirectSubClasses(_searchEngine, type.element);
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 cf87d27..7b6f75a 100644
--- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
+++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart
@@ -394,7 +394,7 @@
   void _addProposal_convertToBlockFunctionBody() {
     FunctionBody body = getEnclosingFunctionBody();
     // prepare expression body
-    if (body is! ExpressionFunctionBody) {
+    if (body is! ExpressionFunctionBody || body.isGenerator) {
       _coverageMarker();
       return;
     }
@@ -405,10 +405,16 @@
     String prefix = utils.getNodePrefix(body.parent);
     String indent = utils.getIndent(1);
     // add change
-    String statementCode =
-        (returnValueType.isVoid ? '' : 'return ') + returnValueCode;
     SourceBuilder sb = new SourceBuilder(file, body.offset);
-    sb.append('{$eol$prefix$indent$statementCode;');
+    if (body.isAsynchronous) {
+      sb.append('async ');
+    }
+    sb.append('{$eol$prefix$indent');
+    if (!returnValueType.isVoid) {
+      sb.append('return ');
+    }
+    sb.append(returnValueCode);
+    sb.append(';');
     sb.setExitOffset();
     sb.append('$eol$prefix}');
     _insertBuilder(sb, body.length);
@@ -419,7 +425,7 @@
   void _addProposal_convertToExpressionFunctionBody() {
     // prepare current body
     FunctionBody body = getEnclosingFunctionBody();
-    if (body is! BlockFunctionBody) {
+    if (body is! BlockFunctionBody || body.isGenerator) {
       _coverageMarker();
       return;
     }
@@ -442,12 +448,17 @@
       return;
     }
     // add change
-    String newBodySource = '=> ${_getNodeText(returnExpression)}';
+    SourceBuilder sb = new SourceBuilder(file, body.offset);
+    if (body.isAsynchronous) {
+      sb.append('async ');
+    }
+    sb.append('=> ');
+    sb.append(_getNodeText(returnExpression));
     if (body.parent is! FunctionExpression ||
         body.parent.parent is FunctionDeclaration) {
-      newBodySource += ';';
+      sb.append(';');
     }
-    _addReplaceEdit(rangeNode(body), newBodySource);
+    _insertBuilder(sb, body.length);
     // add proposal
     _addAssist(DartAssistKind.CONVERT_INTO_EXPRESSION_BODY, []);
   }
diff --git a/pkg/analysis_server/test/analysis_abstract.dart b/pkg/analysis_server/test/analysis_abstract.dart
index 539f718..88567a1 100644
--- a/pkg/analysis_server/test/analysis_abstract.dart
+++ b/pkg/analysis_server/test/analysis_abstract.dart
@@ -16,6 +16,7 @@
 import 'package:analyzer/file_system/memory_file_system.dart';
 import 'package:analyzer/instrumentation/instrumentation.dart';
 import 'package:plugin/manager.dart';
+import 'package:plugin/plugin.dart';
 import 'package:unittest/unittest.dart';
 
 import 'mock_sdk.dart';
@@ -83,6 +84,8 @@
     handleSuccessfulRequest(request);
   }
 
+  void addServerPlugins(List<Plugin> plugins) {}
+
   String addTestFile(String content) {
     addFile(testFile, content);
     this.testCode = content;
@@ -90,12 +93,22 @@
   }
 
   AnalysisServer createAnalysisServer(Index index) {
-    ExtensionManager manager = new ExtensionManager();
     ServerPlugin serverPlugin = new ServerPlugin();
-    manager.processPlugins([serverPlugin]);
-    return new AnalysisServer(serverChannel, resourceProvider,
-        packageMapProvider, index, serverPlugin, new AnalysisServerOptions(),
-        new MockSdk(), InstrumentationService.NULL_SERVICE);
+    List<Plugin> plugins = <Plugin>[serverPlugin];
+    addServerPlugins(plugins);
+    // process plugins
+    ExtensionManager manager = new ExtensionManager();
+    manager.processPlugins(plugins);
+    // create server
+    return new AnalysisServer(
+        serverChannel,
+        resourceProvider,
+        packageMapProvider,
+        index,
+        serverPlugin,
+        new AnalysisServerOptions(),
+        new MockSdk(),
+        InstrumentationService.NULL_SERVICE);
   }
 
   Index createIndex() {
@@ -169,7 +182,8 @@
     packageMapProvider = new MockPackageMapProvider();
     Index index = createIndex();
     server = createAnalysisServer(index);
-    handler = new AnalysisDomainHandler(server);
+    handler = server.handlers
+        .singleWhere((handler) => handler is AnalysisDomainHandler);
     // listen for notifications
     Stream<Notification> notificationStream =
         serverChannel.notificationController.stream;
diff --git a/pkg/analysis_server/test/integration/integration_test_methods.dart b/pkg/analysis_server/test/integration/integration_test_methods.dart
index 8f3915e..f6aa68d 100644
--- a/pkg/analysis_server/test/integration/integration_test_methods.dart
+++ b/pkg/analysis_server/test/integration/integration_test_methods.dart
@@ -1062,6 +1062,11 @@
    *
    *   The offset of the name of the type within the file.
    *
+   * superOnly ( optional bool )
+   *
+   *   True if the client is only requesting superclasses and interfaces
+   *   hierarchy.
+   *
    * Returns
    *
    * hierarchyItems ( optional List<TypeHierarchyItem> )
@@ -1076,8 +1081,8 @@
    *   not represent a type, or if the file has not been sufficiently analyzed
    *   to allow a type hierarchy to be produced.
    */
-  Future<SearchGetTypeHierarchyResult> sendSearchGetTypeHierarchy(String file, int offset) {
-    var params = new SearchGetTypeHierarchyParams(file, offset).toJson();
+  Future<SearchGetTypeHierarchyResult> sendSearchGetTypeHierarchy(String file, int offset, {bool superOnly}) {
+    var params = new SearchGetTypeHierarchyParams(file, offset, superOnly: superOnly).toJson();
     return server.send("search.getTypeHierarchy", params)
         .then((result) {
       ResponseDecoder decoder = new ResponseDecoder(null);
diff --git a/pkg/analysis_server/test/integration/protocol_matchers.dart b/pkg/analysis_server/test/integration/protocol_matchers.dart
index 349e3f2..dfef91e 100644
--- a/pkg/analysis_server/test/integration/protocol_matchers.dart
+++ b/pkg/analysis_server/test/integration/protocol_matchers.dart
@@ -629,12 +629,15 @@
  * {
  *   "file": FilePath
  *   "offset": int
+ *   "superOnly": optional bool
  * }
  */
 final Matcher isSearchGetTypeHierarchyParams = new LazyMatcher(() => new MatchesJsonObject(
   "search.getTypeHierarchy params", {
     "file": isFilePath,
     "offset": isInt
+  }, optionalFields: {
+    "superOnly": isBool
   }));
 
 /**
diff --git a/pkg/analysis_server/test/plugin/set_analysis_domain_test.dart b/pkg/analysis_server/test/plugin/set_analysis_domain_test.dart
new file mode 100644
index 0000000..38953fa0
--- /dev/null
+++ b/pkg/analysis_server/test/plugin/set_analysis_domain_test.dart
@@ -0,0 +1,146 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.plugin.analysis_contributor;
+
+import 'dart:async';
+
+import 'package:analysis_server/analysis/analysis_domain.dart';
+import 'package:analysis_server/analysis/navigation/navigation_core.dart';
+import 'package:analysis_server/plugin/navigation.dart';
+import 'package:analysis_server/src/constants.dart';
+import 'package:analysis_server/src/protocol.dart';
+import 'package:analyzer/src/generated/engine.dart';
+import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/task/dart.dart';
+import 'package:plugin/plugin.dart';
+import 'package:test_reflective_loader/test_reflective_loader.dart';
+import 'package:unittest/unittest.dart';
+
+import '../analysis_abstract.dart';
+import '../utils.dart';
+
+main() {
+  initializeTestEnvironment();
+  if (AnalysisEngine.instance.useTaskModel) {
+    defineReflectiveTests(SetAnalysisDomainTest);
+  }
+}
+
+/**
+ * This test uses [SET_ANALYSIS_DOMAIN_EXTENSION_POINT_ID] and
+ * [NAVIGATION_CONTRIBUTOR_EXTENSION_POINT_ID] extension points to validate
+ * that plugins can listen for analysis and force sending navigation
+ * notifications.
+ */
+@reflectiveTest
+class SetAnalysisDomainTest extends AbstractAnalysisTest {
+  final Set<String> parsedUnitFiles = new Set<String>();
+  bool contributorMayAddRegion = false;
+
+  List<NavigationRegion> regions;
+  List<NavigationTarget> targets;
+  List<String> targetFiles;
+
+  @override
+  void addServerPlugins(List<Plugin> plugins) {
+    var plugin = new TestSetAnalysisDomainPlugin(this);
+    plugins.add(plugin);
+  }
+
+  @override
+  void processNotification(Notification notification) {
+    if (notification.event == ANALYSIS_NAVIGATION) {
+      var params = new AnalysisNavigationParams.fromNotification(notification);
+      // TODO(scheglov) we check for "params.regions.isNotEmpty" because
+      // normal, Dart only, navigation notifications are scheduled as
+      // operations, but plugins use "notificationSite.scheduleNavigation"
+      // which is not scheduled yet. So, it comes *before* the Dart one, and
+      // gets lost.
+      if (params.file == testFile && params.regions.isNotEmpty) {
+        regions = params.regions;
+        targets = params.targets;
+        targetFiles = params.files;
+      }
+    }
+  }
+
+  Future test_contributorIsInvoked() async {
+    createProject();
+    addAnalysisSubscription(AnalysisService.NAVIGATION, testFile);
+    addTestFile('// usually no navigation');
+    await server.onAnalysisComplete;
+    // we have PARSED_UNIT
+    expect(parsedUnitFiles, contains(testFile));
+    // we have an additional navigation region/target
+    expect(regions, hasLength(1));
+    {
+      NavigationRegion region = regions.single;
+      expect(region.offset, 1);
+      expect(region.length, 5);
+      expect(region.targets.single, 0);
+    }
+    {
+      NavigationTarget target = targets.single;
+      expect(target.fileIndex, 0);
+      expect(target.offset, 1);
+      expect(target.length, 2);
+      expect(target.startLine, 3);
+      expect(target.startColumn, 4);
+    }
+    expect(targetFiles.single, '/testLocation.dart');
+  }
+}
+
+class TestNavigationContributor implements NavigationContributor {
+  final SetAnalysisDomainTest test;
+
+  TestNavigationContributor(this.test);
+
+  @override
+  void computeNavigation(NavigationHolder holder, AnalysisContext context,
+      Source source, int offset, int length) {
+    if (test.contributorMayAddRegion) {
+      holder.addRegion(1, 5, ElementKind.CLASS,
+          new Location('/testLocation.dart', 1, 2, 3, 4));
+    }
+  }
+}
+
+class TestSetAnalysisDomainPlugin implements Plugin {
+  final SetAnalysisDomainTest test;
+
+  TestSetAnalysisDomainPlugin(this.test);
+
+  @override
+  String get uniqueIdentifier => 'test';
+
+  @override
+  void registerExtensionPoints(RegisterExtensionPoint register) {}
+
+  @override
+  void registerExtensions(RegisterExtension register) {
+    register(SET_ANALYSIS_DOMAIN_EXTENSION_POINT_ID, _setAnalysisDomain);
+    register(NAVIGATION_CONTRIBUTOR_EXTENSION_POINT_ID,
+        new TestNavigationContributor(test));
+  }
+
+  void _setAnalysisDomain(AnalysisDomain domain) {
+    domain.onResultComputed(PARSED_UNIT).listen((result) {
+      expect(result.context, isNotNull);
+      expect(result.target, isNotNull);
+      expect(result.value, isNotNull);
+      Source source = result.target.source;
+      test.parsedUnitFiles.add(source.fullName);
+      // let the navigation contributor to work
+      test.contributorMayAddRegion = true;
+      try {
+        domain.scheduleNotification(
+            result.context, source, AnalysisService.NAVIGATION);
+      } finally {
+        test.contributorMayAddRegion = false;
+      }
+    });
+  }
+}
diff --git a/pkg/analysis_server/test/plugin/test_all.dart b/pkg/analysis_server/test/plugin/test_all.dart
new file mode 100644
index 0000000..0dbf7d6
--- /dev/null
+++ b/pkg/analysis_server/test/plugin/test_all.dart
@@ -0,0 +1,19 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+library test.plugin.analysis_contributor;
+
+import 'package:unittest/unittest.dart';
+
+import '../utils.dart';
+import 'set_analysis_domain_test.dart' as set_analysis_domain_test;
+
+/**
+ * Utility for manually running all tests.
+ */
+main() {
+  initializeTestEnvironment();
+  group('plugin', () {
+    set_analysis_domain_test.main();
+  });
+}
diff --git a/pkg/analysis_server/test/search/type_hierarchy_test.dart b/pkg/analysis_server/test/search/type_hierarchy_test.dart
index 3dda479..67445aa 100644
--- a/pkg/analysis_server/test/search/type_hierarchy_test.dart
+++ b/pkg/analysis_server/test/search/type_hierarchy_test.dart
@@ -791,14 +791,86 @@
         itemD.memberElement.location.offset, findOffset('test(x) {} // in D'));
   }
 
-  Request _createGetTypeHierarchyRequest(String search) {
-    return new SearchGetTypeHierarchyParams(testFile, findOffset(search))
-        .toRequest(requestId);
+  test_superOnly() async {
+    addTestFile('''
+class A {}
+class B {}
+class C extends A implements B {}
+class D extends C {}
+''');
+    List<TypeHierarchyItem> items =
+        await _getTypeHierarchy('C extends', superOnly: true);
+    expect(_toJson(items), [
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'C',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 1,
+        'interfaces': [3],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'A',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 2,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'Object',
+          'location': anything,
+          'flags': 0
+        },
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      },
+      {
+        'classElement': {
+          'kind': 'CLASS',
+          'name': 'B',
+          'location': anything,
+          'flags': 0
+        },
+        'superclass': 2,
+        'interfaces': [],
+        'mixins': [],
+        'subclasses': []
+      }
+    ]);
   }
 
-  Future<List<TypeHierarchyItem>> _getTypeHierarchy(String search) async {
+  test_superOnly_fileDoesNotExist() async {
+    Request request = new SearchGetTypeHierarchyParams(
+        '/does/not/exist.dart', 0,
+        superOnly: true).toRequest(requestId);
+    Response response = await serverChannel.sendRequest(request);
+    List<TypeHierarchyItem> items =
+        new SearchGetTypeHierarchyResult.fromResponse(response).hierarchyItems;
+    expect(items, isNull);
+  }
+
+  Request _createGetTypeHierarchyRequest(String search, {bool superOnly}) {
+    return new SearchGetTypeHierarchyParams(testFile, findOffset(search),
+        superOnly: superOnly).toRequest(requestId);
+  }
+
+  Future<List<TypeHierarchyItem>> _getTypeHierarchy(String search,
+      {bool superOnly}) async {
     await waitForTasksFinished();
-    Request request = _createGetTypeHierarchyRequest(search);
+    Request request =
+        _createGetTypeHierarchyRequest(search, superOnly: superOnly);
     Response response = await serverChannel.sendRequest(request);
     expect(serverErrors, isEmpty);
     return new SearchGetTypeHierarchyResult.fromResponse(response)
diff --git a/pkg/analysis_server/test/services/correction/assist_test.dart b/pkg/analysis_server/test/services/correction/assist_test.dart
index 4d20cc0..9a256a2 100644
--- a/pkg/analysis_server/test/services/correction/assist_test.dart
+++ b/pkg/analysis_server/test/services/correction/assist_test.dart
@@ -823,6 +823,24 @@
     assertNoAssistAt('f();', DartAssistKind.ASSIGN_TO_LOCAL_VARIABLE);
   }
 
+  void test_convertToBlockBody_OK_async() {
+    resolveTestUnit('''
+class A {
+  mmm() async => 123;
+}
+''');
+    assertHasAssistAt(
+        'mmm()',
+        DartAssistKind.CONVERT_INTO_BLOCK_BODY,
+        '''
+class A {
+  mmm() async {
+    return 123;
+  }
+}
+''');
+  }
+
   void test_convertToBlockBody_OK_closure() {
     resolveTestUnit('''
 setup(x) {}
@@ -955,6 +973,24 @@
     assertNoAssistAt('fff() {', DartAssistKind.CONVERT_INTO_BLOCK_BODY);
   }
 
+  void test_convertToExpressionBody_OK_async() {
+    resolveTestUnit('''
+class A {
+  mmm() async {
+    return 42;
+  }
+}
+''');
+    assertHasAssistAt(
+        'mmm',
+        DartAssistKind.CONVERT_INTO_EXPRESSION_BODY,
+        '''
+class A {
+  mmm() async => 42;
+}
+''');
+  }
+
   void test_convertToExpressionBody_OK_closure() {
     resolveTestUnit('''
 setup(x) {}
diff --git a/pkg/analysis_server/test/test_all.dart b/pkg/analysis_server/test/test_all.dart
index 102b4c7..a89913d 100644
--- a/pkg/analysis_server/test/test_all.dart
+++ b/pkg/analysis_server/test/test_all.dart
@@ -14,6 +14,7 @@
 import 'domain_server_test.dart' as domain_server_test;
 import 'edit/test_all.dart' as edit_all;
 import 'operation/test_all.dart' as operation_test_all;
+import 'plugin/test_all.dart' as plugin_all;
 import 'protocol_server_test.dart' as protocol_server_test;
 import 'protocol_test.dart' as protocol_test;
 import 'search/test_all.dart' as search_all;
@@ -40,6 +41,7 @@
     domain_server_test.main();
     edit_all.main();
     operation_test_all.main();
+    plugin_all.main();
     protocol_server_test.main();
     protocol_test.main();
     search_all.main();
diff --git a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
index c183dac..0e2176a 100644
--- a/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
+++ b/pkg/analysis_server/tool/spec/generated/java/AnalysisServer.java
@@ -518,8 +518,9 @@
    * @param file The file containing the declaration or reference to the type for which a hierarchy
    *         is being requested.
    * @param offset The offset of the name of the type within the file.
+   * @param superOnly True if the client is only requesting superclasses and interfaces hierarchy.
    */
-  public void search_getTypeHierarchy(String file, int offset, GetTypeHierarchyConsumer consumer);
+  public void search_getTypeHierarchy(String file, int offset, boolean superOnly, GetTypeHierarchyConsumer consumer);
 
   /**
    * {@code server.getVersion}
diff --git a/pkg/analysis_server/tool/spec/spec_input.html b/pkg/analysis_server/tool/spec/spec_input.html
index c8b8c3a..974eb97 100644
--- a/pkg/analysis_server/tool/spec/spec_input.html
+++ b/pkg/analysis_server/tool/spec/spec_input.html
@@ -1308,6 +1308,13 @@
               The offset of the name of the type within the file.
             </p>
           </field>
+          <field name="superOnly" optional="true">
+            <ref>bool</ref>
+            <p>
+              True if the client is only requesting superclasses and
+              interfaces hierarchy.
+            </p>
+          </field>
         </params>
         <result>
           <field name="hierarchyItems" optional="true">
diff --git a/pkg/analyzer/lib/src/context/context.dart b/pkg/analyzer/lib/src/context/context.dart
index f472ed1..d90b091 100644
--- a/pkg/analyzer/lib/src/context/context.dart
+++ b/pkg/analyzer/lib/src/context/context.dart
@@ -1142,7 +1142,11 @@
       entry = getCacheEntry(unit);
       setValue(HINTS, AnalysisError.NO_ERRORS);
       // dartEntry.setValue(LINTS, AnalysisError.NO_ERRORS);
-      entry.setState(RESOLVE_REFERENCES_ERRORS, CacheState.FLUSHED);
+      setValue(INFER_STATIC_VARIABLE_TYPES_ERRORS, AnalysisError.NO_ERRORS);
+      setValue(LIBRARY_UNIT_ERRORS, AnalysisError.NO_ERRORS);
+      setValue(PARTIALLY_RESOLVE_REFERENCES_ERRORS, AnalysisError.NO_ERRORS);
+      setValue(RESOLVE_FUNCTION_BODIES_ERRORS, AnalysisError.NO_ERRORS);
+      setValue(RESOLVE_TYPE_NAMES_ERRORS, AnalysisError.NO_ERRORS);
       entry.setState(RESOLVED_UNIT, CacheState.FLUSHED);
       entry.setState(RESOLVED_UNIT1, CacheState.FLUSHED);
       entry.setState(RESOLVED_UNIT2, CacheState.FLUSHED);
@@ -1154,6 +1158,7 @@
       entry.setState(RESOLVED_UNIT8, CacheState.FLUSHED);
       // USED_IMPORTED_ELEMENTS
       // USED_LOCAL_ELEMENTS
+      setValue(VARIABLE_REFERENCE_ERRORS, AnalysisError.NO_ERRORS);
       setValue(VERIFY_ERRORS, AnalysisError.NO_ERRORS);
     });
 
diff --git a/pkg/analyzer/lib/src/generated/ast.dart b/pkg/analyzer/lib/src/generated/ast.dart
index 7940662..0c5dfc2 100644
--- a/pkg/analyzer/lib/src/generated/ast.dart
+++ b/pkg/analyzer/lib/src/generated/ast.dart
@@ -3477,7 +3477,12 @@
   }
 
   @override
-  Token get beginToken => _block.beginToken;
+  Token get beginToken {
+    if (keyword != null) {
+      return keyword;
+    }
+    return _block.beginToken;
+  }
 
   /**
    * Return the block representing the body of the function.
diff --git a/pkg/analyzer/lib/src/generated/engine.dart b/pkg/analyzer/lib/src/generated/engine.dart
index 0e8f9c8..5eccbf7 100644
--- a/pkg/analyzer/lib/src/generated/engine.dart
+++ b/pkg/analyzer/lib/src/generated/engine.dart
@@ -556,7 +556,8 @@
    * Perform work until the given [result] has been computed for the given
    * [target]. Return the computed value.
    */
-  Object /*V*/ computeResult(AnalysisTarget target, ResultDescriptor /*<V>*/ result);
+  Object /*V*/ computeResult(
+      AnalysisTarget target, ResultDescriptor /*<V>*/ result);
 
   /**
    * Notifies the context that the client is going to stop using this context.
@@ -729,7 +730,8 @@
    * If the corresponding [target] does not exist, or the [result] is not
    * computed yet, then the default value is returned.
    */
-  Object /*V*/ getResult(AnalysisTarget target, ResultDescriptor /*<V>*/ result);
+  Object /*V*/ getResult(
+      AnalysisTarget target, ResultDescriptor /*<V>*/ result);
 
   /**
    * Return a list of the sources being analyzed in this context whose full path
@@ -5903,6 +5905,11 @@
   bool limitInvalidationInTaskModel = false;
 
   /**
+   * The plugins that are defined outside the `analyzer` package.
+   */
+  List<Plugin> _userDefinedPlugins = <Plugin>[];
+
+  /**
    * The task manager used to manage the tasks used to analyze code.
    */
   TaskManager _taskManager;
@@ -5951,6 +5958,7 @@
         commandLinePlugin,
         optionsPlugin
       ];
+      _supportedPlugins.addAll(_userDefinedPlugins);
     }
     return _supportedPlugins;
   }
@@ -5960,10 +5968,7 @@
    */
   TaskManager get taskManager {
     if (_taskManager == null) {
-      if (enginePlugin.taskExtensionPoint == null) {
-        // The plugin wasn't used, so tasks are not registered.
-        new ExtensionManager().processPlugins([enginePlugin]);
-      }
+      new ExtensionManager().processPlugins(supportedPlugins);
       _taskManager = new TaskManager();
       _taskManager.addTaskDescriptors(enginePlugin.taskDescriptors);
       // TODO(brianwilkerson) Create a way to associate different results with
@@ -5974,6 +5979,18 @@
   }
 
   /**
+   * Set plugins that are defined outside the `analyzer` package.
+   */
+  void set userDefinedPlugins(List<Plugin> plugins) {
+    if (plugins == null) {
+      plugins = <Plugin>[];
+    }
+    _userDefinedPlugins = plugins;
+    _supportedPlugins = null;
+    _taskManager = null;
+  }
+
+  /**
    * Clear any caches holding on to analysis results so that a full re-analysis
    * will be performed the next time an analysis context is created.
    */
diff --git a/pkg/analyzer/lib/src/generated/incremental_resolver.dart b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
index ea1e0fa..5feaaa9 100644
--- a/pkg/analyzer/lib/src/generated/incremental_resolver.dart
+++ b/pkg/analyzer/lib/src/generated/incremental_resolver.dart
@@ -13,8 +13,11 @@
 import 'package:analyzer/src/task/dart.dart'
     show
         HINTS,
+        INFER_STATIC_VARIABLE_TYPES_ERRORS,
+        LIBRARY_UNIT_ERRORS,
         PARSE_ERRORS,
-        RESOLVE_REFERENCES_ERRORS,
+        PARTIALLY_RESOLVE_REFERENCES_ERRORS,
+        RESOLVE_FUNCTION_BODIES_ERRORS,
         RESOLVE_TYPE_NAMES_ERRORS,
         SCAN_ERRORS,
         USED_IMPORTED_ELEMENTS,
@@ -1125,7 +1128,10 @@
 
   void _shiftEntryErrors_NEW() {
     _shiftErrors_NEW(HINTS);
-    _shiftErrors_NEW(RESOLVE_REFERENCES_ERRORS);
+    _shiftErrors_NEW(INFER_STATIC_VARIABLE_TYPES_ERRORS);
+    _shiftErrors_NEW(LIBRARY_UNIT_ERRORS);
+    _shiftErrors_NEW(PARTIALLY_RESOLVE_REFERENCES_ERRORS);
+    _shiftErrors_NEW(RESOLVE_FUNCTION_BODIES_ERRORS);
     _shiftErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS);
     _shiftErrors_NEW(VARIABLE_REFERENCE_ERRORS);
     _shiftErrors_NEW(VERIFY_ERRORS);
@@ -1177,7 +1183,10 @@
   }
 
   void _updateEntry_NEW() {
-    _updateErrors_NEW(RESOLVE_REFERENCES_ERRORS, _resolveErrors);
+    _updateErrors_NEW(INFER_STATIC_VARIABLE_TYPES_ERRORS, _resolveErrors);
+    _updateErrors_NEW(LIBRARY_UNIT_ERRORS, _resolveErrors);
+    _updateErrors_NEW(PARTIALLY_RESOLVE_REFERENCES_ERRORS, _resolveErrors);
+    _updateErrors_NEW(RESOLVE_FUNCTION_BODIES_ERRORS, _resolveErrors);
     _updateErrors_NEW(RESOLVE_TYPE_NAMES_ERRORS, []);
     _updateErrors_NEW(VARIABLE_REFERENCE_ERRORS, []);
     _updateErrors_NEW(VERIFY_ERRORS, _verifyErrors);
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index eb7bacf..4a17411 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -3930,11 +3930,23 @@
   bool visitAsExpression(AsExpression node) => _nodeExits(node.expression);
 
   @override
-  bool visitAssertStatement(AssertStatement node) => _nodeExits(node.condition);
+  bool visitAssertStatement(AssertStatement node) => false;
 
   @override
-  bool visitAssignmentExpression(AssignmentExpression node) =>
-      _nodeExits(node.leftHandSide) || _nodeExits(node.rightHandSide);
+  bool visitAssignmentExpression(AssignmentExpression node) {
+    Expression leftHandSide = node.leftHandSide;
+    if (_nodeExits(leftHandSide)) {
+      return true;
+    }
+    if (node.operator.type == sc.TokenType.QUESTION_QUESTION_EQ) {
+      return false;
+    }
+    if (leftHandSide is PropertyAccess &&
+        leftHandSide.operator.type == sc.TokenType.QUESTION_PERIOD) {
+      return false;
+    }
+    return _nodeExits(node.rightHandSide);
+  }
 
   @override
   bool visitAwaitExpression(AwaitExpression node) =>
@@ -3943,9 +3955,10 @@
   @override
   bool visitBinaryExpression(BinaryExpression node) {
     Expression lhsExpression = node.leftOperand;
+    Expression rhsExpression = node.rightOperand;
     sc.TokenType operatorType = node.operator.type;
-    // If the operator is || and the left hand side is false literal, don't
-    // consider the RHS of the binary expression.
+    // If the operator is ||, then only consider the RHS of the binary
+    // expression if the left hand side is the false literal.
     // TODO(jwren) Do we want to take constant expressions into account,
     // evaluate if(false) {} differently than if(<condition>), when <condition>
     // evaluates to a constant false value?
@@ -3953,21 +3966,27 @@
       if (lhsExpression is BooleanLiteral) {
         BooleanLiteral booleanLiteral = lhsExpression;
         if (!booleanLiteral.value) {
-          return false;
+          return _nodeExits(rhsExpression);
         }
       }
+      return _nodeExits(lhsExpression);
     }
-    // If the operator is && and the left hand side is true literal, don't
-    // consider the RHS of the binary expression.
+    // If the operator is &&, then only consider the RHS of the binary
+    // expression if the left hand side is the true literal.
     if (operatorType == sc.TokenType.AMPERSAND_AMPERSAND) {
       if (lhsExpression is BooleanLiteral) {
         BooleanLiteral booleanLiteral = lhsExpression;
         if (booleanLiteral.value) {
-          return false;
+          return _nodeExits(rhsExpression);
         }
       }
+      return _nodeExits(lhsExpression);
     }
-    Expression rhsExpression = node.rightOperand;
+    // If the operator is ??, then don't consider the RHS of the binary
+    // expression.
+    if (operatorType == sc.TokenType.QUESTION_QUESTION) {
+      return _nodeExits(lhsExpression);
+    }
     return _nodeExits(lhsExpression) || _nodeExits(rhsExpression);
   }
 
@@ -4162,8 +4181,13 @@
   @override
   bool visitMethodInvocation(MethodInvocation node) {
     Expression target = node.realTarget;
-    if (target != null && target.accept(this)) {
-      return true;
+    if (target != null) {
+      if (target.accept(this)) {
+        return true;
+      }
+      if (node.operator.type == sc.TokenType.QUESTION_PERIOD) {
+        return false;
+      }
     }
     return _nodeExits(node.argumentList);
   }
diff --git a/pkg/analyzer/lib/src/plugin/engine_plugin.dart b/pkg/analyzer/lib/src/plugin/engine_plugin.dart
index 93f61d1..65ead96 100644
--- a/pkg/analyzer/lib/src/plugin/engine_plugin.dart
+++ b/pkg/analyzer/lib/src/plugin/engine_plugin.dart
@@ -105,7 +105,8 @@
     registerExtension(taskId, BuildTypeProviderTask.DESCRIPTOR);
     registerExtension(taskId, ComputeConstantDependenciesTask.DESCRIPTOR);
     registerExtension(taskId, ComputeConstantValueTask.DESCRIPTOR);
-    registerExtension(taskId, ComputeInferableStaticVariableDependenciesTask.DESCRIPTOR);
+    registerExtension(
+        taskId, ComputeInferableStaticVariableDependenciesTask.DESCRIPTOR);
     registerExtension(taskId, ContainingLibrariesTask.DESCRIPTOR);
     registerExtension(taskId, DartErrorsTask.DESCRIPTOR);
     registerExtension(taskId, EvaluateUnitConstantsTask.DESCRIPTOR);
@@ -119,9 +120,9 @@
     registerExtension(taskId, LibraryUnitErrorsTask.DESCRIPTOR);
     registerExtension(taskId, ParseDartTask.DESCRIPTOR);
     registerExtension(taskId, PartiallyResolveUnitReferencesTask.DESCRIPTOR);
+    registerExtension(taskId, ResolveFunctionBodiesInUnitTask.DESCRIPTOR);
     registerExtension(taskId, ResolveLibraryReferencesTask.DESCRIPTOR);
     registerExtension(taskId, ResolveLibraryTypeNamesTask.DESCRIPTOR);
-    registerExtension(taskId, ResolveUnitReferencesTask.DESCRIPTOR);
     registerExtension(taskId, ResolveUnitTypeNamesTask.DESCRIPTOR);
     registerExtension(taskId, ResolveVariableReferencesTask.DESCRIPTOR);
     registerExtension(taskId, ScanDartTask.DESCRIPTOR);
diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart
index 65e391f..e81545c 100644
--- a/pkg/analyzer/lib/src/task/dart.dart
+++ b/pkg/analyzer/lib/src/task/dart.dart
@@ -162,6 +162,30 @@
     new ListResultDescriptor<Source>('IMPORT_EXPORT_SOURCE_CLOSURE', null);
 
 /**
+ * The errors produced while inferring the type of a static variable.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for [VariableElement]s, and only when strong
+ * mode is enabled.
+ */
+final ListResultDescriptor<AnalysisError> INFER_STATIC_VARIABLE_ERRORS =
+    new ListResultDescriptor<AnalysisError>(
+        'INFER_STATIC_VARIABLE_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
+ * The errors produced while inferring the types of all of the static variables
+ * in a compilation unit.
+ *
+ * The list will be empty if there were no errors, but will not be `null`.
+ *
+ * The result is only available for [LibrarySpecificUnit]s.
+ */
+final ListResultDescriptor<AnalysisError> INFER_STATIC_VARIABLE_TYPES_ERRORS =
+    new ListResultDescriptor<AnalysisError>(
+        'INFER_STATIC_VARIABLE_TYPES_ERRORS', AnalysisError.NO_ERRORS);
+
+/**
  * A list of the [VariableElement]s whose type should be inferred that another
  * inferable static variable (the target) depends on.
  *
@@ -188,6 +212,9 @@
 /**
  * An inferrable static variable ([VariableElement]) whose type has been
  * inferred.
+ *
+ * The result is only available for [VariableElement]s, and only when strong
+ * mode is enabled.
  */
 final ResultDescriptor<VariableElement> INFERRED_STATIC_VARIABLE =
     new ResultDescriptor<VariableElement>('INFERRED_STATIC_VARIABLE', null,
@@ -303,15 +330,16 @@
     new ResultDescriptor<ReferencedNames>('REFERENCED_NAMES', null);
 
 /**
- * The errors produced while resolving references.
+ * The errors produced while resolving the function bodies within a compilation
+ * unit.
  *
  * The list will be empty if there were no errors, but will not be `null`.
  *
  * The result is only available for [LibrarySpecificUnit]s.
  */
-final ListResultDescriptor<AnalysisError> RESOLVE_REFERENCES_ERRORS =
+final ListResultDescriptor<AnalysisError> RESOLVE_FUNCTION_BODIES_ERRORS =
     new ListResultDescriptor<AnalysisError>(
-        'RESOLVE_REFERENCES_ERRORS', AnalysisError.NO_ERRORS);
+        'RESOLVE_FUNCTION_BODIES_ERRORS', AnalysisError.NO_ERRORS);
 
 /**
  * The errors produced while resolving type names.
@@ -2534,13 +2562,20 @@
   static const String INFERRED_VARIABLES_INPUT = 'INFERRED_VARIABLES_INPUT';
 
   /**
+   * The name of the input whose value is a list of the errors produced while
+   * inferring types for static variables.
+   */
+  static const String INFER_STATIC_VARIABLE_ERRORS_INPUT =
+      'INFER_STATIC_VARIABLE_ERRORS';
+
+  /**
    * The task descriptor describing this kind of task.
    */
   static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
       'InferStaticVariableTypesInUnitTask',
       createTask,
       buildInputs,
-      <ResultDescriptor>[RESOLVED_UNIT6]);
+      <ResultDescriptor>[INFER_STATIC_VARIABLE_TYPES_ERRORS, RESOLVED_UNIT6]);
 
   /**
    * Initialize a newly created task to build a library element for the given
@@ -2559,11 +2594,18 @@
     // Prepare inputs.
     //
     CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    List<List<AnalysisError>> perVariableErrors =
+        getRequiredInput(INFER_STATIC_VARIABLE_ERRORS_INPUT);
+    List<AnalysisError> errors = <AnalysisError>[];
+    for (List<AnalysisError> variableErrors in perVariableErrors) {
+      errors.addAll(variableErrors);
+    }
     //
     // Record outputs. There is no additional work to be done at this time
     // because the work has implicitly been done by virtue of the task model
     // preparing all of the inputs.
     //
+    outputs[INFER_STATIC_VARIABLE_TYPES_ERRORS] = errors;
     outputs[RESOLVED_UNIT6] = unit;
   }
 
@@ -2578,6 +2620,9 @@
       INFERRED_VARIABLES_INPUT: INFERABLE_STATIC_VARIABLES_IN_UNIT
           .of(unit)
           .toListOf(INFERRED_STATIC_VARIABLE),
+      INFER_STATIC_VARIABLE_ERRORS_INPUT: INFERABLE_STATIC_VARIABLES_IN_UNIT
+          .of(unit)
+          .toListOf(INFER_STATIC_VARIABLE_ERRORS),
       UNIT_INPUT: RESOLVED_UNIT5.of(unit)
     };
   }
@@ -2616,8 +2661,10 @@
   static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
       'InferStaticVariableTypeTask',
       createTask,
-      buildInputs,
-      <ResultDescriptor>[INFERRED_STATIC_VARIABLE]);
+      buildInputs, <ResultDescriptor>[
+    INFER_STATIC_VARIABLE_ERRORS,
+    INFERRED_STATIC_VARIABLE
+  ]);
 
   InferStaticVariableTypeTask(
       InternalAnalysisContext context, VariableElement variable)
@@ -2641,6 +2688,7 @@
     VariableElementImpl variable = target;
     CompilationUnit unit = getRequiredInput(UNIT_INPUT);
     TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+    RecordingErrorListener errorListener = new RecordingErrorListener();
     if (dependencyCycle == null) {
       //
       // Re-resolve the variable's initializer so that the inferred types of other
@@ -2654,7 +2702,6 @@
         throw new AnalysisException(
             "NodeLocator failed to find a variable's declaration");
       }
-      RecordingErrorListener errorListener = new RecordingErrorListener();
       Expression initializer = declaration.initializer;
       ResolutionContext resolutionContext =
           ResolutionContextBuilder.contextFor(initializer, errorListener);
@@ -2692,6 +2739,7 @@
     // Record outputs.
     //
     outputs[INFERRED_STATIC_VARIABLE] = variable;
+    outputs[INFER_STATIC_VARIABLE_ERRORS] = errorListener.errors;
   }
 
   /**
@@ -2780,10 +2828,22 @@
   static const String HINTS_INPUT = 'HINTS';
 
   /**
-   * The name of the [RESOLVE_REFERENCES_ERRORS] input.
+   * The name of the [INFER_STATIC_VARIABLE_TYPES_ERRORS] input.
    */
-  static const String RESOLVE_REFERENCES_ERRORS_INPUT =
-      'RESOLVE_REFERENCES_ERRORS';
+  static const String INFER_STATIC_VARIABLE_TYPES_ERRORS_INPUT =
+      'INFER_STATIC_VARIABLE_TYPES_ERRORS';
+
+  /**
+   * The name of the [PARTIALLY_RESOLVE_REFERENCES_ERRORS] input.
+   */
+  static const String PARTIALLY_RESOLVE_REFERENCES_ERRORS_INPUT =
+      'PARTIALLY_RESOLVE_REFERENCES_ERRORS';
+
+  /**
+   * The name of the [RESOLVE_FUNCTION_BODIES_ERRORS] input.
+   */
+  static const String RESOLVE_FUNCTION_BODIES_ERRORS_INPUT =
+      'RESOLVE_FUNCTION_BODIES_ERRORS';
 
   /**
    * The name of the [RESOLVE_TYPE_NAMES_ERRORS] input.
@@ -2824,7 +2884,9 @@
     //
     List<List<AnalysisError>> errorLists = <List<AnalysisError>>[];
     errorLists.add(getRequiredInput(HINTS_INPUT));
-    errorLists.add(getRequiredInput(RESOLVE_REFERENCES_ERRORS_INPUT));
+    errorLists.add(getRequiredInput(INFER_STATIC_VARIABLE_TYPES_ERRORS_INPUT));
+    errorLists.add(getRequiredInput(PARTIALLY_RESOLVE_REFERENCES_ERRORS_INPUT));
+    errorLists.add(getRequiredInput(RESOLVE_FUNCTION_BODIES_ERRORS_INPUT));
     errorLists.add(getRequiredInput(RESOLVE_TYPE_NAMES_ERRORS_INPUT));
     errorLists.add(getRequiredInput(VARIABLE_REFERENCE_ERRORS_INPUT));
     errorLists.add(getRequiredInput(VERIFY_ERRORS_INPUT));
@@ -2843,7 +2905,12 @@
     LibrarySpecificUnit unit = target;
     return <String, TaskInput>{
       HINTS_INPUT: HINTS.of(unit),
-      RESOLVE_REFERENCES_ERRORS_INPUT: RESOLVE_REFERENCES_ERRORS.of(unit),
+      INFER_STATIC_VARIABLE_TYPES_ERRORS_INPUT:
+          INFER_STATIC_VARIABLE_TYPES_ERRORS.of(unit),
+      PARTIALLY_RESOLVE_REFERENCES_ERRORS_INPUT:
+          PARTIALLY_RESOLVE_REFERENCES_ERRORS.of(unit),
+      RESOLVE_FUNCTION_BODIES_ERRORS_INPUT:
+          RESOLVE_FUNCTION_BODIES_ERRORS.of(unit),
       RESOLVE_TYPE_NAMES_ERRORS_INPUT: RESOLVE_TYPE_NAMES_ERRORS.of(unit),
       VARIABLE_REFERENCE_ERRORS_INPUT: VARIABLE_REFERENCE_ERRORS.of(unit),
       VERIFY_ERRORS_INPUT: VERIFY_ERRORS.of(unit)
@@ -3295,6 +3362,110 @@
 }
 
 /**
+ * A task that resolves the bodies of top-level functions, constructors, and
+ * methods within a single compilation unit.
+ */
+class ResolveFunctionBodiesInUnitTask extends SourceBasedAnalysisTask {
+  /**
+   * The name of the [TYPE_PROVIDER] input.
+   */
+  static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
+
+  /**
+   * The name of the [RESOLVED_UNIT7] input.
+   */
+  static const String UNIT_INPUT = 'UNIT_INPUT';
+
+  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
+      'ResolveFunctionBodiesInUnitTask',
+      createTask,
+      buildInputs,
+      <ResultDescriptor>[RESOLVE_FUNCTION_BODIES_ERRORS, RESOLVED_UNIT8]);
+
+  ResolveFunctionBodiesInUnitTask(
+      InternalAnalysisContext context, LibrarySpecificUnit compilationUnit)
+      : super(context, compilationUnit);
+
+  @override
+  TaskDescriptor get descriptor => DESCRIPTOR;
+
+  @override
+  void internalPerform() {
+    //
+    // Prepare inputs.
+    //
+    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
+    TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
+    //
+    // Resolve function bodies.
+    //
+    CompilationUnitElement unitElement = unit.element;
+    RecordingErrorListener errorListener = new RecordingErrorListener();
+    for (CompilationUnitMember unitMember in unit.declarations) {
+      if (unitMember is FunctionDeclaration) {
+        _resolveFunctionBody(unitMember.functionExpression.body, unitElement,
+            typeProvider, errorListener);
+      } else if (unitMember is ClassDeclaration) {
+        for (ClassMember classMember in unitMember.members) {
+          if (classMember is ConstructorDeclaration) {
+            _resolveFunctionBody(
+                classMember.body, unitElement, typeProvider, errorListener);
+          } else if (classMember is MethodDeclaration) {
+            _resolveFunctionBody(
+                classMember.body, unitElement, typeProvider, errorListener);
+          }
+        }
+      }
+    }
+    //
+    // Record outputs.
+    //
+    outputs[RESOLVE_FUNCTION_BODIES_ERRORS] = errorListener.errors;
+    outputs[RESOLVED_UNIT8] = unit;
+  }
+
+  void _resolveFunctionBody(
+      FunctionBody functionBody,
+      CompilationUnitElement unitElement,
+      TypeProvider typeProvider,
+      RecordingErrorListener errorListener) {
+    ResolutionContext resolutionContext =
+        ResolutionContextBuilder.contextFor(functionBody, errorListener);
+    ResolverVisitor visitor = new ResolverVisitor(
+        unitElement.library, unitElement.source, typeProvider, errorListener,
+        nameScope: resolutionContext.scope);
+    if (resolutionContext.enclosingClassDeclaration != null) {
+      visitor.prepareToResolveMembersInClass(
+          resolutionContext.enclosingClassDeclaration);
+    }
+    visitor.initForIncrementalResolution();
+    functionBody.accept(visitor);
+  }
+
+  /**
+   * Return a map from the names of the inputs of this kind of task to the task
+   * input descriptors describing those inputs for a task with the given
+   * [target].
+   */
+  static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
+    LibrarySpecificUnit unit = target;
+    return <String, TaskInput>{
+      TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request),
+      UNIT_INPUT: RESOLVED_UNIT7.of(unit)
+    };
+  }
+
+  /**
+   * Create a [ResolveFunctionBodiesInUnitTask] based on the given [target] in
+   * the given [context].
+   */
+  static ResolveFunctionBodiesInUnitTask createTask(
+      AnalysisContext context, AnalysisTarget target) {
+    return new ResolveFunctionBodiesInUnitTask(context, target);
+  }
+}
+
+/**
  * A task that finishes resolution by requesting [RESOLVED_UNIT_NO_CONSTANTS] for every
  * unit in the libraries closure and produces [LIBRARY_ELEMENT].
  */
@@ -3430,95 +3601,6 @@
 }
 
 /**
- * A task that builds [RESOLVED_UNIT8] for a unit.
- */
-class ResolveUnitReferencesTask extends SourceBasedAnalysisTask {
-  /**
-   * The name of the [LIBRARY_ELEMENT5] input.
-   */
-  static const String LIBRARY_INPUT = 'LIBRARY_INPUT';
-
-  /**
-   * The name of the [RESOLVED_UNIT4] input.
-   */
-  static const String UNIT_INPUT = 'UNIT_INPUT';
-
-  /**
-   * The name of the [TYPE_PROVIDER] input.
-   */
-  static const String TYPE_PROVIDER_INPUT = 'TYPE_PROVIDER_INPUT';
-
-  /**
-   * The task descriptor describing this kind of task.
-   */
-  static final TaskDescriptor DESCRIPTOR = new TaskDescriptor(
-      'ResolveUnitReferencesTask',
-      createTask,
-      buildInputs,
-      <ResultDescriptor>[RESOLVE_REFERENCES_ERRORS, RESOLVED_UNIT8]);
-
-  ResolveUnitReferencesTask(
-      InternalAnalysisContext context, AnalysisTarget target)
-      : super(context, target);
-
-  @override
-  TaskDescriptor get descriptor => DESCRIPTOR;
-
-  @override
-  void internalPerform() {
-    RecordingErrorListener errorListener = new RecordingErrorListener();
-    //
-    // Prepare inputs.
-    //
-    LibraryElement libraryElement = getRequiredInput(LIBRARY_INPUT);
-    CompilationUnit unit = getRequiredInput(UNIT_INPUT);
-    CompilationUnitElement unitElement = unit.element;
-    TypeProvider typeProvider = getRequiredInput(TYPE_PROVIDER_INPUT);
-    //
-    // Resolve references.
-    //
-    InheritanceManager inheritanceManager =
-        new InheritanceManager(libraryElement);
-    AstVisitor visitor = new ResolverVisitor(
-        libraryElement, unitElement.source, typeProvider, errorListener,
-        inheritanceManager: inheritanceManager);
-    unit.accept(visitor);
-    //
-    // Record outputs.
-    //
-    outputs[RESOLVE_REFERENCES_ERRORS] =
-        removeDuplicateErrors(errorListener.errors);
-    outputs[RESOLVED_UNIT8] = unit;
-  }
-
-  /**
-   * Return a map from the names of the inputs of this kind of task to the task
-   * input descriptors describing those inputs for a task with the
-   * given [target].
-   */
-  static Map<String, TaskInput> buildInputs(AnalysisTarget target) {
-    LibrarySpecificUnit unit = target;
-    return <String, TaskInput>{
-      'fullyBuiltLibraryElements': IMPORT_EXPORT_SOURCE_CLOSURE
-          .of(unit.library)
-          .toListOf(LIBRARY_ELEMENT5),
-      LIBRARY_INPUT: LIBRARY_ELEMENT5.of(unit.library),
-      UNIT_INPUT: RESOLVED_UNIT4.of(unit),
-      TYPE_PROVIDER_INPUT: TYPE_PROVIDER.of(AnalysisContextTarget.request)
-    };
-  }
-
-  /**
-   * Create a [ResolveUnitReferencesTask] based on the given [target] in
-   * the given [context].
-   */
-  static ResolveUnitReferencesTask createTask(
-      AnalysisContext context, AnalysisTarget target) {
-    return new ResolveUnitReferencesTask(context, target);
-  }
-}
-
-/**
  * A task that builds [RESOLVED_UNIT3] for a unit.
  */
 class ResolveUnitTypeNamesTask extends SourceBasedAnalysisTask {
diff --git a/pkg/analyzer/lib/src/task/dart_work_manager.dart b/pkg/analyzer/lib/src/task/dart_work_manager.dart
index f813fe9..5b66e0b 100644
--- a/pkg/analyzer/lib/src/task/dart_work_manager.dart
+++ b/pkg/analyzer/lib/src/task/dart_work_manager.dart
@@ -19,10 +19,9 @@
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer/src/generated/utilities_collection.dart';
 import 'package:analyzer/src/task/dart.dart';
-import 'package:analyzer/src/task/driver.dart';
+import 'package:analyzer/src/task/html.dart';
 import 'package:analyzer/task/dart.dart';
 import 'package:analyzer/task/model.dart';
-import 'package:analyzer/src/task/html.dart';
 
 /**
  * The manager for Dart specific analysis.
@@ -43,7 +42,10 @@
    */
   static final List<ResultDescriptor> _UNIT_ERRORS = <ResultDescriptor>[
     HINTS,
-    RESOLVE_REFERENCES_ERRORS,
+    INFER_STATIC_VARIABLE_TYPES_ERRORS,
+    LIBRARY_UNIT_ERRORS,
+    PARTIALLY_RESOLVE_REFERENCES_ERRORS,
+    RESOLVE_FUNCTION_BODIES_ERRORS,
     RESOLVE_TYPE_NAMES_ERRORS,
     VARIABLE_REFERENCE_ERRORS,
     VERIFY_ERRORS
diff --git a/pkg/analyzer/test/generated/all_the_rest_test.dart b/pkg/analyzer/test/generated/all_the_rest_test.dart
index ec4b9c0..9765af9 100644
--- a/pkg/analyzer/test/generated/all_the_rest_test.dart
+++ b/pkg/analyzer/test/generated/all_the_rest_test.dart
@@ -7351,7 +7351,7 @@
   }
 
   void test_assertStatement_throw() {
-    _assertTrue("assert((throw 0));");
+    _assertFalse("assert((throw 0));");
   }
 
   void test_assignmentExpression() {
@@ -7383,15 +7383,31 @@
   }
 
   void test_binaryExpression_and_rhs() {
-    _assertTrue("a && (throw '');");
+    _assertFalse("a && (throw '');");
   }
 
   void test_binaryExpression_and_rhs2() {
-    _assertTrue("false && (throw '');");
+    _assertFalse("false && (throw '');");
   }
 
   void test_binaryExpression_and_rhs3() {
-    _assertFalse("true && (throw '');");
+    _assertTrue("true && (throw '');");
+  }
+
+  void test_binaryExpression_ifNull() {
+    _assertFalse("a ?? b;");
+  }
+
+  void test_binaryExpression_ifNull_lhs() {
+    _assertTrue("throw '' ?? b;");
+  }
+
+  void test_binaryExpression_ifNull_rhs() {
+    _assertFalse("a ?? (throw '');");
+  }
+
+  void test_binaryExpression_ifNull_rhs2() {
+    _assertFalse("null ?? (throw '');");
   }
 
   void test_binaryExpression_or() {
@@ -7403,15 +7419,15 @@
   }
 
   void test_binaryExpression_or_rhs() {
-    _assertTrue("a || (throw '');");
+    _assertFalse("a || (throw '');");
   }
 
   void test_binaryExpression_or_rhs2() {
-    _assertTrue("true || (throw '');");
+    _assertFalse("true || (throw '');");
   }
 
   void test_binaryExpression_or_rhs3() {
-    _assertFalse("false || (throw '');");
+    _assertTrue("false || (throw '');");
   }
 
   void test_block_empty() {
@@ -7462,6 +7478,62 @@
     _assertFalse("c ? throw '' : j;");
   }
 
+  void test_conditionalAccess() {
+    _assertFalse("a?.b;");
+  }
+
+  void test_conditionalAccess_lhs() {
+    _assertTrue("(throw '')?.b;");
+  }
+
+  void test_conditionalAccessAssign() {
+    _assertFalse("a?.b = c;");
+  }
+
+  void test_conditionalAccessAssign_lhs() {
+    _assertTrue("(throw '')?.b = c;");
+  }
+
+  void test_conditionalAccessAssign_rhs() {
+    _assertFalse("a?.b = throw '';");
+  }
+
+  void test_conditionalAccessAssign_rhs2() {
+    _assertFalse("null?.b = throw '';");
+  }
+
+  void test_conditionalAccessIfNullAssign() {
+    _assertFalse("a?.b ??= c;");
+  }
+
+  void test_conditionalAccessIfNullAssign_lhs() {
+    _assertTrue("(throw '')?.b ??= c;");
+  }
+
+  void test_conditionalAccessIfNullAssign_rhs() {
+    _assertFalse("a?.b ??= throw '';");
+  }
+
+  void test_conditionalAccessIfNullAssign_rhs2() {
+    _assertFalse("null?.b ??= throw '';");
+  }
+
+  void test_conditionalCall() {
+    _assertFalse("a?.b(c);");
+  }
+
+  void test_conditionalCall_lhs() {
+    _assertTrue("(throw '')?.b(c);");
+  }
+
+  void test_conditionalCall_rhs() {
+    _assertFalse("a?.b(throw '');");
+  }
+
+  void test_conditionalCall_rhs2() {
+    _assertFalse("null?.b(throw '');");
+  }
+
   void test_creation() {
     expect(new ExitDetector(), isNotNull);
   }
@@ -7618,6 +7690,14 @@
     _assertFalse("if (c) return 0; else j++;");
   }
 
+  void test_ifNullAssign() {
+    _assertFalse("a ??= b;");
+  }
+
+  void test_ifNullAssign_rhs() {
+    _assertFalse("a ??= throw '';");
+  }
+
   void test_indexExpression() {
     _assertFalse("a[b];");
   }
diff --git a/pkg/analyzer/test/src/task/dart_test.dart b/pkg/analyzer/test/src/task/dart_test.dart
index b3c6036..95a9ff5 100644
--- a/pkg/analyzer/test/src/task/dart_test.dart
+++ b/pkg/analyzer/test/src/task/dart_test.dart
@@ -22,6 +22,7 @@
 import 'package:analyzer/task/model.dart';
 import 'package:unittest/unittest.dart';
 
+import '../../generated/resolver_test.dart';
 import '../../generated/test_support.dart';
 import '../../reflective_tests.dart';
 import '../../utils.dart';
@@ -54,8 +55,9 @@
   runReflectiveTests(LibraryUnitErrorsTaskTest);
   runReflectiveTests(ParseDartTaskTest);
   runReflectiveTests(PartiallyResolveUnitReferencesTaskTest);
+  runReflectiveTests(ResolveFunctionBodiesInUnitTaskTest);
   runReflectiveTests(ResolveLibraryTypeNamesTaskTest);
-  runReflectiveTests(ResolveUnitReferencesTaskTest);
+//  runReflectiveTests(ResolveUnitReferencesTaskTest);
   runReflectiveTests(ResolveUnitTypeNamesTaskTest);
   runReflectiveTests(ResolveVariableReferencesTaskTest);
   runReflectiveTests(ScanDartTaskTest);
@@ -109,10 +111,10 @@
 isInstanceOf isParseDartTask = new isInstanceOf<ParseDartTask>();
 isInstanceOf isPartiallyResolveUnitReferencesTask =
     new isInstanceOf<PartiallyResolveUnitReferencesTask>();
+isInstanceOf isResolveFunctionBodiesInUnitTask =
+    new isInstanceOf<ResolveFunctionBodiesInUnitTask>();
 isInstanceOf isResolveLibraryTypeNamesTask =
     new isInstanceOf<ResolveLibraryTypeNamesTask>();
-isInstanceOf isResolveUnitReferencesTask =
-    new isInstanceOf<ResolveUnitReferencesTask>();
 isInstanceOf isResolveUnitTypeNamesTask =
     new isInstanceOf<ResolveUnitTypeNamesTask>();
 isInstanceOf isResolveVariableReferencesTask =
@@ -125,45 +127,6 @@
   Source source;
   LibrarySpecificUnit target;
 
-  test_buildInputs() {
-    LibrarySpecificUnit target =
-        new LibrarySpecificUnit(emptySource, emptySource);
-    Map<String, TaskInput> inputs =
-        BuildCompilationUnitElementTask.buildInputs(target);
-    expect(inputs, isNotNull);
-    expect(
-        inputs.keys,
-        unorderedEquals(
-            [BuildCompilationUnitElementTask.PARSED_UNIT_INPUT_NAME]));
-  }
-
-  test_constructor() {
-    BuildCompilationUnitElementTask task =
-        new BuildCompilationUnitElementTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    BuildCompilationUnitElementTask task =
-        BuildCompilationUnitElementTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    BuildCompilationUnitElementTask task =
-        new BuildCompilationUnitElementTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = BuildCompilationUnitElementTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform_find_constants() {
     _performBuildTask('''
 const x = 1;
@@ -242,33 +205,6 @@
 
 @reflectiveTest
 class BuildDirectiveElementsTaskTest extends _AbstractDartTaskTest {
-  test_constructor() {
-    BuildDirectiveElementsTask task =
-        new BuildDirectiveElementsTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    BuildDirectiveElementsTask task =
-        BuildDirectiveElementsTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    BuildDirectiveElementsTask task =
-        new BuildDirectiveElementsTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = BuildDirectiveElementsTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform() {
     List<Source> sources = newSources({
       '/libA.dart': '''
@@ -696,33 +632,6 @@
 
   LibraryElement libraryElement;
 
-  test_constructor() {
-    BuildLibraryElementTask task =
-        new BuildLibraryElementTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    BuildLibraryElementTask task =
-        BuildLibraryElementTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    BuildLibraryElementTask task =
-        new BuildLibraryElementTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = BuildLibraryElementTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform() {
     _performBuildTask({
       '/lib.dart': '''
@@ -957,41 +866,6 @@
 
 @reflectiveTest
 class BuildPublicNamespaceTaskTest extends _AbstractDartTaskTest {
-  test_buildInputs() {
-    Map<String, TaskInput> inputs =
-        BuildPublicNamespaceTask.buildInputs(emptySource);
-    expect(inputs, isNotNull);
-    expect(
-        inputs.keys, unorderedEquals([BuildPublicNamespaceTask.LIBRARY_INPUT]));
-  }
-
-  test_constructor() {
-    BuildPublicNamespaceTask task =
-        new BuildPublicNamespaceTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    BuildPublicNamespaceTask task =
-        BuildPublicNamespaceTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    BuildPublicNamespaceTask task =
-        new BuildPublicNamespaceTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = BuildPublicNamespaceTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform() {
     List<Source> sources = newSources({
       '/lib.dart': '''
@@ -1574,40 +1448,6 @@
 
 @reflectiveTest
 class ContainingLibrariesTaskTest extends _AbstractDartTaskTest {
-  test_buildInputs() {
-    Map<String, TaskInput> inputs =
-        ContainingLibrariesTask.buildInputs(emptySource);
-    expect(inputs, isNotNull);
-    expect(inputs, isEmpty);
-  }
-
-  test_constructor() {
-    ContainingLibrariesTask task =
-        new ContainingLibrariesTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    ContainingLibrariesTask task =
-        ContainingLibrariesTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    ContainingLibrariesTask task =
-        new ContainingLibrariesTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = ContainingLibrariesTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform_definingCompilationUnit() {
     AnalysisTarget library = newSource('/test.dart', 'library test;');
     computeResult(library, INCLUDED_PARTS);
@@ -1650,44 +1490,6 @@
 
 @reflectiveTest
 class DartErrorsTaskTest extends _AbstractDartTaskTest {
-  test_buildInputs() {
-    Map<String, TaskInput> inputs = DartErrorsTask.buildInputs(emptySource);
-    expect(inputs, isNotNull);
-    expect(
-        inputs.keys,
-        unorderedEquals([
-          DartErrorsTask.BUILD_DIRECTIVES_ERRORS_INPUT,
-          DartErrorsTask.BUILD_LIBRARY_ERRORS_INPUT,
-          DartErrorsTask.PARSE_ERRORS_INPUT,
-          DartErrorsTask.SCAN_ERRORS_INPUT,
-          DartErrorsTask.LIBRARY_UNIT_ERRORS_INPUT
-        ]));
-  }
-
-  test_constructor() {
-    DartErrorsTask task = new DartErrorsTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    DartErrorsTask task = DartErrorsTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    DartErrorsTask task = new DartErrorsTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = DartErrorsTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform_definingCompilationUnit() {
     AnalysisTarget library =
         newSource('/test.dart', 'library test; import "dart:math";');
@@ -2204,6 +2006,7 @@
     InterfaceType stringType = context.typeProvider.stringType;
     expect(topLevel.type, stringType);
     expect(field.type, stringType);
+    expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(0));
   }
 
   void test_perform_cycle() {
@@ -2226,6 +2029,23 @@
     expect(piFirst.type, context.typeProvider.boolType);
     expect(pi.type.isDynamic, isTrue);
     expect(tau.type.isDynamic, isTrue);
+    expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(0));
+  }
+
+  void test_perform_error() {
+    AnalysisTarget source = newSource(
+        '/test.dart',
+        '''
+var a = '' / null;
+''');
+    computeResult(new LibrarySpecificUnit(source, source), RESOLVED_UNIT5);
+    CompilationUnit unit = outputs[RESOLVED_UNIT5];
+    VariableElement a = getTopLevelVariable(unit, 'a').name.staticElement;
+
+    computeResult(a, INFERRED_STATIC_VARIABLE,
+        matcher: isInferStaticVariableTypeTask);
+    expect(a.type.isDynamic, isTrue);
+    expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(1));
   }
 
   void test_perform_null() {
@@ -2241,6 +2061,7 @@
     computeResult(a, INFERRED_STATIC_VARIABLE,
         matcher: isInferStaticVariableTypeTask);
     expect(a.type.isDynamic, isTrue);
+    expect(outputs[INFER_STATIC_VARIABLE_ERRORS], hasLength(0));
   }
 }
 
@@ -2280,47 +2101,6 @@
 
 @reflectiveTest
 class LibraryUnitErrorsTaskTest extends _AbstractDartTaskTest {
-  test_buildInputs() {
-    Map<String, TaskInput> inputs = LibraryUnitErrorsTask
-        .buildInputs(new LibrarySpecificUnit(emptySource, emptySource));
-    expect(inputs, isNotNull);
-    expect(
-        inputs.keys,
-        unorderedEquals([
-          LibraryUnitErrorsTask.HINTS_INPUT,
-          LibraryUnitErrorsTask.RESOLVE_REFERENCES_ERRORS_INPUT,
-          LibraryUnitErrorsTask.RESOLVE_TYPE_NAMES_ERRORS_INPUT,
-          LibraryUnitErrorsTask.VARIABLE_REFERENCE_ERRORS_INPUT,
-          LibraryUnitErrorsTask.VERIFY_ERRORS_INPUT
-        ]));
-  }
-
-  test_constructor() {
-    LibraryUnitErrorsTask task =
-        new LibraryUnitErrorsTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    LibraryUnitErrorsTask task =
-        LibraryUnitErrorsTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    LibraryUnitErrorsTask task = new LibraryUnitErrorsTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = LibraryUnitErrorsTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform_definingCompilationUnit() {
     AnalysisTarget library =
         newSource('/test.dart', 'library test; import "dart:math";');
@@ -2348,42 +2128,6 @@
 class ParseDartTaskTest extends _AbstractDartTaskTest {
   Source source;
 
-  test_buildInputs() {
-    Map<String, TaskInput> inputs = ParseDartTask.buildInputs(emptySource);
-    expect(inputs, isNotNull);
-    expect(
-        inputs.keys,
-        unorderedEquals([
-          ParseDartTask.LINE_INFO_INPUT_NAME,
-          ParseDartTask.MODIFICATION_TIME_INPUT_NAME,
-          ParseDartTask.TOKEN_STREAM_INPUT_NAME
-        ]));
-  }
-
-  test_constructor() {
-    ParseDartTask task = new ParseDartTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    ParseDartTask task = ParseDartTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    ParseDartTask task = new ParseDartTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = ParseDartTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform() {
     _performParseTask(r'''
 part of lib;
@@ -2585,6 +2329,42 @@
 }
 
 @reflectiveTest
+class ResolveFunctionBodiesInUnitTaskTest extends _AbstractDartTaskTest {
+  void test_perform() {
+    AnalysisTarget source = newSource(
+        '/test.dart',
+        '''
+void f() {
+  var c = new C();
+  c.m();
+}
+class C {
+  void m() {
+    f();
+  }
+}
+''');
+    computeResult(new LibrarySpecificUnit(source, source), RESOLVED_UNIT8,
+        matcher: isResolveFunctionBodiesInUnitTask);
+    CompilationUnit unit = outputs[RESOLVED_UNIT8];
+
+    FunctionDeclaration f = unit.declarations[0];
+    _assertResolved(f.functionExpression.body);
+
+    MethodDeclaration m = (unit.declarations[1] as ClassDeclaration).members[0];
+    _assertResolved(m.body);
+
+    expect(outputs[RESOLVE_FUNCTION_BODIES_ERRORS], hasLength(0));
+  }
+
+  void _assertResolved(FunctionBody body) {
+    ResolutionVerifier verifier = new ResolutionVerifier();
+    body.accept(verifier);
+    verifier.assertResolved();
+  }
+}
+
+@reflectiveTest
 class ResolveLibraryTypeNamesTaskTest extends _AbstractDartTaskTest {
   test_perform() {
     Source sourceLib = newSource(
@@ -2645,92 +2425,6 @@
 }
 
 @reflectiveTest
-class ResolveUnitReferencesTaskTest extends _AbstractDartTaskTest {
-  test_perform() {
-    Source source = newSource(
-        '/test.dart',
-        '''
-class A {
-  m() {}
-}
-main(A a) {
-  a.m();
-}
-''');
-    LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
-    // prepare unit and "a.m()" invocation
-    computeResult(target, RESOLVED_UNIT8, matcher: isResolveUnitReferencesTask);
-    CompilationUnit unit = outputs[RESOLVED_UNIT8];
-    // walk the AST
-    FunctionDeclaration function = unit.declarations[1];
-    BlockFunctionBody body = function.functionExpression.body;
-    ExpressionStatement statement = body.block.statements[0];
-    MethodInvocation invocation = statement.expression;
-    expect(unit, same(outputs[RESOLVED_UNIT8]));
-    // a.m() is resolved now
-    expect(invocation.methodName.staticElement, isNotNull);
-  }
-
-  test_perform_errors() {
-    Source source = newSource(
-        '/test.dart',
-        '''
-class A {
-}
-main(A a) {
-  a.unknownMethod();
-}
-''');
-    LibrarySpecificUnit target = new LibrarySpecificUnit(source, source);
-    computeResult(target, RESOLVED_UNIT8, matcher: isResolveUnitReferencesTask);
-    // validate
-    _fillErrorListener(RESOLVE_REFERENCES_ERRORS);
-    errorListener.assertErrorsWithCodes(
-        <ErrorCode>[StaticTypeWarningCode.UNDEFINED_METHOD]);
-  }
-
-  test_perform_importExport() {
-    newSource(
-        '/a.dart',
-        '''
-library a;
-class A<T> {
-  T m() {}
-}
-''');
-    newSource(
-        '/b.dart',
-        '''
-library b;
-export 'a.dart';
-''');
-    Source sourceC = newSource(
-        '/c.dart',
-        '''
-library c;
-import 'b.dart';
-main() {
-  new A<int>().m();
-}
-''');
-    computeResult(new LibrarySpecificUnit(sourceC, sourceC), RESOLVED_UNIT8,
-        matcher: isResolveUnitReferencesTask);
-    // validate
-    CompilationUnit unit = outputs[RESOLVED_UNIT8];
-    expect(unit, isNotNull);
-    FunctionDeclaration functionDeclaration = unit.declarations[0];
-    BlockFunctionBody body = functionDeclaration.functionExpression.body;
-    List<Statement> statements = body.block.statements;
-    ExpressionStatement statement = statements[0];
-    MethodInvocation invocation = statement.expression;
-    MethodElement methodElement = invocation.methodName.staticElement;
-    expect(methodElement, isNotNull);
-    expect(methodElement.type, isNotNull);
-    expect(methodElement.returnType.toString(), 'int');
-  }
-}
-
-@reflectiveTest
 class ResolveUnitTypeNamesTaskTest extends _AbstractDartTaskTest {
   test_perform() {
     Source source = newSource(
@@ -2900,36 +2594,6 @@
 
 @reflectiveTest
 class ScanDartTaskTest extends _AbstractDartTaskTest {
-  test_buildInputs() {
-    Map<String, TaskInput> inputs = ScanDartTask.buildInputs(emptySource);
-    expect(inputs, isNotNull);
-    expect(inputs.keys, unorderedEquals([ScanDartTask.CONTENT_INPUT_NAME]));
-  }
-
-  test_constructor() {
-    ScanDartTask task = new ScanDartTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_createTask() {
-    ScanDartTask task = ScanDartTask.createTask(context, emptySource);
-    expect(task, isNotNull);
-    expect(task.context, context);
-    expect(task.target, emptySource);
-  }
-
-  test_description() {
-    ScanDartTask task = new ScanDartTask(null, emptySource);
-    expect(task.description, isNotNull);
-  }
-
-  test_descriptor() {
-    TaskDescriptor descriptor = ScanDartTask.DESCRIPTOR;
-    expect(descriptor, isNotNull);
-  }
-
   test_perform_errors() {
     _performScanTask('import "');
     expect(outputs, hasLength(3));
diff --git a/pkg/compiler/lib/src/common/backend_api.dart b/pkg/compiler/lib/src/common/backend_api.dart
index e715f29..42ad090 100644
--- a/pkg/compiler/lib/src/common/backend_api.dart
+++ b/pkg/compiler/lib/src/common/backend_api.dart
@@ -343,6 +343,9 @@
   /// times, but [onQueueClosed] is only called once.
   void onQueueClosed() {}
 
+  /// Called when the compiler starts running the codegen enqueuer.
+  void onCodegenStart() {}
+
   /// Called after [element] has been resolved.
   // TODO(johnniwinther): Change [TreeElements] to [Registry] or a dependency
   // node. [elements] is currently unused by the implementation.
diff --git a/pkg/compiler/lib/src/common/names.dart b/pkg/compiler/lib/src/common/names.dart
index 967e873..7051ffe 100644
--- a/pkg/compiler/lib/src/common/names.dart
+++ b/pkg/compiler/lib/src/common/names.dart
@@ -87,6 +87,8 @@
 
   static final Selector compareTo =
       new Selector.call(const PublicName("compareTo"), CallStructure.ONE_ARG);
+
+  static final Selector equals = new Selector.binaryOperator('==');
 }
 
 /// [Uri]s commonly used.
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index e04c88a..db180aa 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -1153,6 +1153,7 @@
 
     log('Compiling...');
     phase = PHASE_COMPILING;
+    backend.onCodegenStart();
     // TODO(johnniwinther): Move these to [CodegenEnqueuer].
     if (hasIsolateSupport) {
       backend.enableIsolateSupport(enqueuer.codegen);
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
index 712e5cd..8ad289f 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_builder_task.dart
@@ -777,10 +777,6 @@
       ast.Send node,
       MethodElement function,
       _) {
-    // TODO(karlklose): support foreign functions.
-    if (compiler.backend.isForeign(function)) {
-      return giveup(node, 'handleStaticFunctionGet: foreign: $function');
-    }
     return irBuilder.buildStaticFunctionGet(function);
   }
 
@@ -1183,9 +1179,6 @@
       ast.NodeList arguments,
       CallStructure callStructure,
       _) {
-    if (compiler.backend.isForeign(getter)) {
-      return giveup(node, 'handleStaticGetterInvoke: foreign: $getter');
-    }
     ir.Primitive target = irBuilder.buildStaticGetterGet(
         getter, sourceInformationBuilder.buildGet(node));
     return irBuilder.buildCallInvocation(target,
@@ -2261,12 +2254,12 @@
   }
 
   internalError(ast.Node node, String message) {
-    giveup(node, message);
+    compiler.internalError(node, message);
   }
 
   @override
   visitNode(ast.Node node) {
-    internalError(node, "Unhandled node");
+    giveup(node, "Unhandled node");
   }
 
   dynamic giveup(ast.Node node, [String reason]) {
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
index b685c45..d93de7b 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes.dart
@@ -3,7 +3,8 @@
 // BSD-style license that can be found in the LICENSE file.
 library dart2js.ir_nodes;
 
-import '../constants/values.dart' as values show ConstantValue;
+import 'dart:collection';
+import '../constants/values.dart' as values;
 import '../dart_types.dart' show DartType, InterfaceType, TypeVariableType;
 import '../elements/elements.dart';
 import '../io/source_information.dart' show SourceInformation;
@@ -119,6 +120,41 @@
   }
 }
 
+class EffectiveUseIterator extends Iterator<Reference<Primitive>> {
+  Reference<Primitive> current;
+  Reference<Primitive> next;
+  final List<Refinement> stack = <Refinement>[];
+
+  EffectiveUseIterator(Primitive prim) : next = prim.firstRef;
+
+  bool moveNext() {
+    Reference<Primitive> ref = next;
+    while (true) {
+      if (ref == null) {
+        if (stack.isNotEmpty) {
+          ref = stack.removeLast().firstRef;
+        } else {
+          current = null;
+          return false;
+        }
+      } else if (ref.parent is Refinement) {
+        stack.add(ref.parent);
+        ref = ref.next;
+      } else {
+        current = ref;
+        next = current.next;
+        return true;
+      }
+    }
+  }
+}
+
+class EffectiveUseIterable extends IterableBase<Reference<Primitive>> {
+  Primitive primitive;
+  EffectiveUseIterable(this.primitive);
+  EffectiveUseIterator get iterator => new EffectiveUseIterator(primitive);
+}
+
 /// A named value.
 ///
 /// The identity of the [Primitive] object is the name of the value.
@@ -153,6 +189,34 @@
   /// The source information associated with this primitive.
   // TODO(johnniwinther): Require source information for all primitives.
   SourceInformation get sourceInformation => null;
+
+  /// If this is a [Refinement] node, returns the value being refined.
+  Primitive get effectiveDefinition => this;
+
+  /// True if the two primitives are (refinements of) the same value.
+  bool sameValue(Primitive other) {
+    return effectiveDefinition == other.effectiveDefinition;
+  }
+
+  /// Iterates all non-refinement uses of the primitive and all uses of
+  /// a [Refinement] of this primitive (transitively).
+  ///
+  /// Notes regarding concurrent modification:
+  /// - The current reference may safely be unlinked.
+  /// - Yet unvisited references may not be unlinked.
+  /// - References to this primitive created during iteration will not be seen.
+  /// - References to a refinement of this primitive may not be created during
+  ///   iteration.
+  EffectiveUseIterable get effectiveUses => new EffectiveUseIterable(this);
+
+  bool get hasMultipleEffectiveUses {
+    Iterator it = effectiveUses.iterator;
+    return it.moveNext() && it.moveNext();
+  }
+
+  bool get hasNoEffectiveUses {
+    return effectiveUses.isEmpty;
+  }
 }
 
 /// Operands to invocations and primitives are always variables.  They point to
@@ -439,6 +503,28 @@
   accept(Visitor visitor) => visitor.visitInvokeConstructor(this);
 }
 
+/// An alias for [value] in a context where the value is known to satisfy
+/// [type].
+///
+/// Refinement nodes are inserted before the type propagator pass and removed
+/// afterwards, so as not to complicate passes that don't reason about types,
+/// but need to reason about value references being identical (i.e. referring
+/// to the same primitive).
+class Refinement extends Primitive {
+  Reference<Primitive> value;
+  final TypeMask type;
+
+  Refinement(Primitive value, this.type)
+    : value = new Reference<Primitive>(value);
+
+  bool get isSafeForElimination => true;
+  bool get isSafeForReordering => false;
+
+  accept(Visitor visitor) => visitor.visitRefinement(this);
+
+  Primitive get effectiveDefinition => value.definition.effectiveDefinition;
+}
+
 /// An "is" type test.
 ///
 /// Returns `true` if [value] is an instance of [type].
@@ -883,6 +969,18 @@
   final Set<ClassElement> interceptedClasses = new Set<ClassElement>();
   final SourceInformation sourceInformation;
 
+  /// If non-null, all uses of this the interceptor call are guaranteed to
+  /// see this value.
+  ///
+  /// The interceptor call is not immediately replaced by the constant, because
+  /// that might prevent the interceptor from being shared.
+  ///
+  /// The precise input type is not known when sharing interceptors, because
+  /// refinement nodes have been removed by then. So this field carries the
+  /// known constant until we know if it should be shared or replaced by
+  /// the constant.
+  values.InterceptorConstantValue constantValue;
+
   Interceptor(Primitive input, this.sourceInformation)
       : this.input = new Reference<Primitive>(input);
 
@@ -1201,6 +1299,7 @@
   T visitGetLength(GetLength node);
   T visitGetIndex(GetIndex node);
   T visitSetIndex(SetIndex node);
+  T visitRefinement(Refinement node);
 
   // Support for literal foreign code.
   T visitForeignCode(ForeignCode node);
@@ -1504,6 +1603,12 @@
     processReference(node.index);
     processReference(node.value);
   }
+
+  processRefinement(Refinement node) {}
+  visitRefinement(Refinement node) {
+    processRefinement(node);
+    processReference(node.value);
+  }
 }
 
 typedef void StackAction();
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart
index 988ee69..1131ce8 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_nodes_sexpr.dart
@@ -361,6 +361,12 @@
     String continuation = access(node.continuation);
     return '(Await $value $continuation)';
   }
+
+  @override
+  String visitRefinement(Refinement node) {
+    String value = access(node.value);
+    return '(Refinement $value ${node.type})';
+  }
 }
 
 class ConstantStringifier extends ConstantValueVisitor<String, Null> {
diff --git a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart
index ecbe343..6b56330 100644
--- a/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart
+++ b/pkg/compiler/lib/src/cps_ir/cps_ir_tracer.dart
@@ -393,6 +393,12 @@
     String continuation = formatReference(node.continuation);
     return 'Await $value $continuation';
   }
+
+  @override
+  visitRefinement(cps_ir.Refinement node) {
+    String value = formatReference(node.value);
+    return 'Refinement $value ${node.type}';
+  }
 }
 
 /**
@@ -669,4 +675,8 @@
   visitAwait(cps_ir.Await node) {
     unexpectedNode(node);
   }
+
+  visitRefinement(cps_ir.Refinement node) {
+    unexpectedNode(node);
+  }
 }
diff --git a/pkg/compiler/lib/src/cps_ir/insert_refinements.dart b/pkg/compiler/lib/src/cps_ir/insert_refinements.dart
new file mode 100644
index 0000000..03cafdf
--- /dev/null
+++ b/pkg/compiler/lib/src/cps_ir/insert_refinements.dart
@@ -0,0 +1,243 @@
+// 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 cps_ir.optimization.insert_refinements;
+
+import 'optimizers.dart' show Pass;
+import 'shrinking_reductions.dart' show ParentVisitor;
+import 'cps_ir_nodes.dart';
+import '../types/types.dart';
+import '../types/constants.dart';
+import '../constants/values.dart';
+import '../world.dart';
+import '../common/names.dart';
+import '../universe/universe.dart';
+import '../elements/elements.dart';
+
+/// Inserts [Refinement] nodes in the IR to allow for sparse path-sensitive
+/// type analysis in the [TypePropagator] pass.
+///
+/// Refinement nodes are inserted at the arms of a [Branch] node with a
+/// condition of form `x is T` or `x == null`.
+///
+/// Refinement nodes are inserted after a method invocation to refine the
+/// receiver to the types that can respond to the given selector.
+class InsertRefinements extends RecursiveVisitor implements Pass {
+  String get passName => 'Insert refinement nodes';
+
+  final TypesTask types;
+  final World world;
+  final TypeMask nonNullType;
+  final TypeMask nullType;
+
+  /// Maps unrefined primitives to its refinement currently in scope (if any).
+  final Map<Primitive, Refinement> refinementFor = <Primitive, Refinement>{};
+
+  InsertRefinements(this.types, World world)
+    : this.world = world,
+      nonNullType = new TypeMask.nonNullSubtype(world.objectClass, world),
+      nullType = new TypeMask.empty();
+
+  void rewrite(FunctionDefinition node) {
+    new ParentVisitor().visit(node);
+    visit(node.body);
+  }
+
+  /// Updates references to refer to the refinement currently in scope.
+  void processReference(Reference node) {
+    Refinement refined = refinementFor[node.definition];
+    if (refined != null) {
+      node.changeTo(refined);
+    }
+  }
+
+  /// Sinks the binding of [cont] to immediately above [use].
+  ///
+  /// This is used to ensure that everything in scope at [use] is also in scope
+  /// inside [cont], so refinements can be inserted inside [cont] without
+  /// accidentally referencing a primitive out of scope.
+  ///
+  /// It is always safe to do this for single-use continuations, because
+  /// strictly more things are in scope at the use site, and there can't be any
+  /// other use of [cont] that might fall out of scope since there is only
+  /// that single use.
+  void sinkContinuationToUse(Continuation cont, Expression use) {
+    assert(cont.hasExactlyOneUse && cont.firstRef.parent == use);
+    assert(!cont.isRecursive);
+    LetCont let = cont.parent;
+    InteriorNode useParent = use.parent;
+    if (useParent == let) return;
+    if (let.continuations.length > 1) {
+      // Create a new LetCont binding only this continuation.
+      let.continuations.remove(cont);
+      let = new LetCont(cont, null);
+      cont.parent = let;
+    } else {
+      // Remove LetCont from current position.
+      InteriorNode letParent = let.parent;
+      letParent.body = let.body;
+      let.body.parent = letParent;
+    }
+
+    // Insert LetCont before use.
+    useParent.body = let;
+    let.body = use;
+    use.parent = let;
+    let.parent = useParent;
+  }
+
+  Primitive unfoldInterceptor(Primitive prim) {
+    return prim is Interceptor ? prim.input.definition : prim;
+  }
+
+  /// Enqueues [cont] for processing in a context where [refined] is the
+  /// current refinement for its value.
+  void pushRefinement(Continuation cont, Refinement refined) {
+    Primitive value = refined.effectiveDefinition;
+    Primitive currentRefinement = refinementFor[value];
+    pushAction(() {
+      refinementFor[value] = currentRefinement;
+      if (refined.hasNoUses) {
+        // Clean up refinements that are not used.
+        refined.value.unlink();
+      } else {
+        cont.body = new LetPrim(refined, cont.body);
+        refined.parent = cont.body;
+        refined.value.parent = refined;
+      }
+    });
+    push(cont);
+    pushAction(() {
+      refinementFor[value] = refined;
+    });
+  }
+
+  void visitInvokeMethod(InvokeMethod node) {
+    Continuation cont = node.continuation.definition;
+
+    // Update references to their current refined values.
+    processReference(node.receiver);
+    node.arguments.forEach(processReference);
+
+    // If the call is intercepted, we want to refine the actual receiver,
+    // not the interceptor.
+    Primitive receiver = unfoldInterceptor(node.receiver.definition);
+
+    // Sink the continuation to the call to ensure everything in scope
+    // here is also in scope inside the continuations.
+    sinkContinuationToUse(cont, node);
+
+    if (node.selector.isClosureCall) {
+      // Do not try to refine the receiver of closure calls; the class world
+      // does not know about closure classes.
+      push(cont);
+    } else {
+      // Filter away receivers that throw on this selector.
+      TypeMask type = world.allFunctions.receiverType(node.selector, node.mask);
+      pushRefinement(cont, new Refinement(receiver, type));
+    }
+  }
+
+  CallExpression getCallWithResult(Primitive prim) {
+    if (prim is Parameter && prim.parent is Continuation) {
+      Continuation cont = prim.parent;
+      if (cont.hasExactlyOneUse && cont.firstRef.parent is CallExpression) {
+        return cont.firstRef.parent;
+      }
+    }
+    return null;
+  }
+
+  bool isTrue(Primitive prim) {
+    return prim is Constant && prim.value.isTrue;
+  }
+
+  TypeMask getTypeOf(ConstantValue constant) {
+    return computeTypeMask(types.compiler, constant);
+  }
+
+  void visitBranch(Branch node) {
+    processReference(node.condition);
+    Primitive condition = node.condition.definition;
+    CallExpression call = getCallWithResult(condition);
+
+    Continuation trueCont = node.trueContinuation.definition;
+    Continuation falseCont = node.falseContinuation.definition;
+
+    // Sink both continuations to the Branch to ensure everything in scope
+    // here is also in scope inside the continuations.
+    sinkContinuationToUse(trueCont, node);
+    sinkContinuationToUse(falseCont, node);
+
+    // If the condition is an 'is' check, promote the checked value.
+    if (condition is TypeTest && condition.type.element is ClassElement) {
+      Primitive value = condition.value.definition;
+      ClassElement classElement = condition.type.element;
+      TypeMask type = new TypeMask.nonNullSubtype(classElement, world);
+      Primitive refinedValue = new Refinement(value, type);
+      pushRefinement(trueCont, refinedValue);
+      push(falseCont);
+      return;
+    }
+
+    // If the condition is comparison with a constant, promote the other value.
+    // This can happen either for calls to `==` or `identical` calls, such
+    // as the ones inserted by the unsugaring pass.
+
+    void refineEquality(Primitive first,
+                        Primitive second,
+                        Continuation trueCont,
+                        Continuation falseCont) {
+      if (second is Constant && second.value.isNull) {
+        Refinement refinedTrue = new Refinement(first, nullType);
+        Refinement refinedFalse = new Refinement(first, nonNullType);
+        pushRefinement(trueCont, refinedTrue);
+        pushRefinement(falseCont, refinedFalse);
+      } else if (first is Constant && first.value.isNull) {
+        Refinement refinedTrue = new Refinement(second, nullType);
+        Refinement refinedFalse = new Refinement(second, nonNullType);
+        pushRefinement(trueCont, refinedTrue);
+        pushRefinement(falseCont, refinedFalse);
+      } else {
+        push(trueCont);
+        push(falseCont);
+      }
+    }
+
+    if (call is InvokeMethod && call.selector == Selectors.equals) {
+      refineEquality(call.arguments[0].definition,
+                     call.arguments[1].definition,
+                     trueCont,
+                     falseCont);
+      return;
+    }
+
+    if (condition is ApplyBuiltinOperator &&
+        condition.operator == BuiltinOperator.Identical) {
+      refineEquality(condition.arguments[0].definition,
+                     condition.arguments[1].definition,
+                     trueCont,
+                     falseCont);
+      return;
+    }
+
+    push(trueCont);
+    push(falseCont);
+  }
+
+  @override
+  Expression traverseLetCont(LetCont node) {
+    for (Continuation cont in node.continuations) {
+      if (cont.hasExactlyOneUse &&
+          (cont.firstRef.parent is InvokeMethod ||
+           cont.firstRef.parent is Branch)) {
+        // Do not push the continuation here.
+        // visitInvokeMethod and visitBranch will do that.
+      } else {
+        push(cont);
+      }
+    }
+    return node.body;
+  }
+}
diff --git a/pkg/compiler/lib/src/cps_ir/let_sinking.dart b/pkg/compiler/lib/src/cps_ir/let_sinking.dart
deleted file mode 100644
index 3b46d9a..0000000
--- a/pkg/compiler/lib/src/cps_ir/let_sinking.dart
+++ /dev/null
@@ -1,119 +0,0 @@
-// 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.cps_ir.let_sinking;
-
-import 'cps_ir_nodes.dart';
-import 'optimizers.dart';
-import 'loop_hierarchy.dart';
-
-/// Sinks single-use primitives to the use when this is safe and profitable.
-///
-/// To avoid sinking non-constant primitives into loops, this pass performs a
-/// control-flow analysis to determine the effective nesting of loops.
-///
-/// In the example below, the value 'p' can be sunk to its use site in the
-/// 'else' branch because that branch is not effectively part of a loop,
-/// despite being lexically nested in a recursive continuation.
-///
-///   let prim p = getInterceptor(<something>)
-///   let rec kont x =
-///     if (<loop condition>)
-///       <loop body>
-///       InvokeContinuation kont x'
-///     else
-///       <after loop>
-///       return p.foo()
-///
-class LetSinker extends RecursiveVisitor implements Pass {
-  String get passName => 'Let sinking';
-
-  LoopHierarchy loopHierarchy;
-  List<Continuation> stack = <Continuation>[];
-
-  /// Maps a sinkable primitive to its loop header.
-  Map<Primitive, Continuation> loopHeaderForPrimitive =
-      <Primitive, Continuation>{};
-
-  Continuation currentLoopHeader;
-
-  void rewrite(FunctionDefinition node) {
-    new ParentVisitor().visit(node);
-    loopHierarchy = new LoopHierarchy(node);
-    visit(node.body);
-  }
-
-  @override
-  Expression traverseLetPrim(LetPrim node) {
-    Primitive prim = node.primitive;
-    if (prim.hasExactlyOneUse && prim.isSafeForReordering) {
-      // This can potentially be sunk. Register the loop header, so when we
-      // find the use site, we can check if they are in the same loop.
-      loopHeaderForPrimitive[prim] = currentLoopHeader;
-      pushAction(() {
-        if (node.primitive != null) {
-          // The primitive could not be sunk. Try to sink dependencies here.
-          visit(node.primitive);
-        } else {
-          // The primitive was sunk. Destroy the old LetPrim.
-          InteriorNode parent = node.parent;
-          parent.body = node.body;
-          node.body.parent = parent;
-        }
-      });
-    } else {
-      visit(node.primitive);
-    }
-
-    // Visit the body, wherein this primitive may be sunk to its use site.
-    return node.body;
-  }
-
-  @override
-  Expression traverseContinuation(Continuation cont) {
-    Continuation oldLoopHeader = currentLoopHeader;
-    pushAction(() {
-      currentLoopHeader = oldLoopHeader;
-    });
-    currentLoopHeader = loopHierarchy.getLoopHeader(cont);
-    return cont.body;
-  }
-
-  void processReference(Reference ref) {
-    Definition definition = ref.definition;
-    if (definition is Primitive && 
-        definition is! Parameter &&
-        definition.hasExactlyOneUse &&
-        definition.isSafeForReordering) {
-      // Check if use is in the same loop.
-      Continuation bindingLoop = loopHeaderForPrimitive.remove(definition);
-      if (bindingLoop == currentLoopHeader || definition is Constant) {
-        // Sink the definition.
-
-        Expression use = getEnclosingExpression(ref.parent);
-        LetPrim binding = definition.parent;
-        binding.primitive = null;  // Mark old binding for deletion.
-        LetPrim newBinding = new LetPrim(definition);
-        definition.parent = newBinding;
-        InteriorNode useParent = use.parent;
-        useParent.body = newBinding;
-        newBinding.body = use;
-        use.parent = newBinding;
-        newBinding.parent = useParent;
-
-        // Now that the final binding location has been found, sink the
-        // dependencies of the definition down here as well.
-        visit(definition); 
-      }
-    }
-  }
-
-  Expression getEnclosingExpression(Node node) {
-    while (node is! Expression) {
-      node = node.parent;
-    }
-    return node;
-  }
-}
-
diff --git a/pkg/compiler/lib/src/cps_ir/optimizers.dart b/pkg/compiler/lib/src/cps_ir/optimizers.dart
index 11c9fae..d10823a 100644
--- a/pkg/compiler/lib/src/cps_ir/optimizers.dart
+++ b/pkg/compiler/lib/src/cps_ir/optimizers.dart
@@ -13,7 +13,8 @@
 export 'redundant_join.dart' show RedundantJoinEliminator;
 export 'shrinking_reductions.dart' show ShrinkingReducer, ParentVisitor;
 export 'mutable_ssa.dart' show MutableVariableEliminator;
-export 'let_sinking.dart' show LetSinker;
+export 'insert_refinements.dart' show InsertRefinements;
+export 'remove_refinements.dart' show RemoveRefinements;
 export 'loop_invariant_code_motion.dart' show LoopInvariantCodeMotion;
 export 'share_interceptors.dart' show ShareInterceptors;
 
diff --git a/pkg/compiler/lib/src/cps_ir/remove_refinements.dart b/pkg/compiler/lib/src/cps_ir/remove_refinements.dart
new file mode 100644
index 0000000..f5088ae2
--- /dev/null
+++ b/pkg/compiler/lib/src/cps_ir/remove_refinements.dart
@@ -0,0 +1,35 @@
+// 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 cps_ir.optimization.remove_refinements;
+
+import 'optimizers.dart' show Pass;
+import 'shrinking_reductions.dart' show ParentVisitor;
+import 'cps_ir_nodes.dart';
+
+/// Removes all [Refinement] nodes from the IR.
+///
+/// This simplifies subsequent passes that don't rely on path-sensitive
+/// type information but depend on equality between primitives.
+class RemoveRefinements extends RecursiveVisitor implements Pass {
+  String get passName => 'Remove refinement nodes';
+
+  void rewrite(FunctionDefinition node) {
+    new ParentVisitor().visit(node);
+    visit(node);
+  }
+
+  @override
+  Expression traverseLetPrim(LetPrim node) {
+    if (node.primitive is Refinement) {
+      Refinement refinement = node.primitive;
+      refinement.value.definition.substituteFor(refinement);
+      refinement.value.unlink();
+      InteriorNode parent = node.parent;
+      parent.body = node.body;
+      node.body.parent = parent;
+    }
+    return node.body;
+  }
+}
diff --git a/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart b/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart
index 114d8a0..81df956 100644
--- a/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart
+++ b/pkg/compiler/lib/src/cps_ir/scalar_replacement.dart
@@ -1,6 +1,7 @@
 // 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.cps_ir.scalar_replacement;
 
 import 'optimizers.dart';
 
diff --git a/pkg/compiler/lib/src/cps_ir/share_interceptors.dart b/pkg/compiler/lib/src/cps_ir/share_interceptors.dart
index 7b360d4..27afeb5 100644
--- a/pkg/compiler/lib/src/cps_ir/share_interceptors.dart
+++ b/pkg/compiler/lib/src/cps_ir/share_interceptors.dart
@@ -6,6 +6,7 @@
 
 import 'cps_ir_nodes.dart';
 import 'optimizers.dart';
+import '../constants/values.dart';
 
 /// Merges calls to `getInterceptor` when one call dominates the other.
 /// 
@@ -14,8 +15,11 @@
 class ShareInterceptors extends RecursiveVisitor implements Pass {
   String get passName => 'Share interceptors';
 
-  final Map<Primitive, Interceptor> interceptorFor = 
-      <Primitive, Interceptor>{};
+  final Map<Primitive, Primitive> interceptorFor =
+      <Primitive, Primitive>{};
+
+  final Map<ConstantValue, Primitive> sharedConstantFor =
+      <ConstantValue, Primitive>{};
 
   void rewrite(FunctionDefinition node) {
     visit(node.body);
@@ -26,10 +30,49 @@
     if (node.primitive is Interceptor) {
       Interceptor interceptor = node.primitive;
       Primitive input = interceptor.input.definition;
-      Interceptor existing = interceptorFor[input];
+      Primitive existing = interceptorFor[input];
       if (existing != null) {
-        existing.interceptedClasses.addAll(interceptor.interceptedClasses);
+        if (existing is Interceptor) {
+          existing.interceptedClasses.addAll(interceptor.interceptedClasses);
+        }
         existing.substituteFor(interceptor);
+      } else if (interceptor.constantValue != null) {
+        InterceptorConstantValue value = interceptor.constantValue;
+        // There is no interceptor obtained from this particular input, but
+        // there might one obtained from another input that is known to
+        // have the same result, so try to reuse that.
+        Primitive shared = sharedConstantFor[value];
+        if (shared != null) {
+          shared.substituteFor(interceptor);
+        } else {
+          Constant constant = new Constant(value);
+          constant.hint = interceptor.hint;
+          node.primitive = constant;
+          constant.parent = node;
+          interceptor.input.unlink();
+          constant.substituteFor(interceptor);
+          interceptorFor[input] = constant;
+          sharedConstantFor[value] = constant;
+          pushAction(() {
+            interceptorFor.remove(input);
+            sharedConstantFor.remove(value);
+
+            if (constant.hasExactlyOneUse) {
+              // As a heuristic, always sink single-use interceptor constants
+              // to their use, even if it is inside a loop.
+              Expression use = getEnclosingExpression(constant.firstRef.parent);
+              InteriorNode parent = node.parent;
+              parent.body = node.body;
+              node.body.parent = parent;
+
+              InteriorNode useParent = use.parent;
+              useParent.body = node;
+              node.body = use;
+              use.parent = node;
+              node.parent = useParent;
+            }
+          });
+        }
       } else {
         interceptorFor[input] = interceptor;
         pushAction(() {
@@ -39,4 +82,11 @@
     }
     return node.body;
   }
+
+  Expression getEnclosingExpression(Node node) {
+    while (node is! Expression) {
+      node = node.parent;
+    }
+    return node;
+  }
 }
diff --git a/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart b/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart
index 011af0f..0e61782 100644
--- a/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart
+++ b/pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart
@@ -693,6 +693,10 @@
     node.continuation.parent = node;
     node.input.parent = node;
   }
+
+  processRefinement(Refinement node) {
+    node.value.parent = node;
+  }
 }
 
 class _ReductionKind {
diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
index 3212f94..5ef491b 100644
--- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart
+++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart
@@ -1,6 +1,7 @@
 // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
+library dart2js.cps_ir.type_propagation;
 
 import 'optimizers.dart';
 
@@ -700,6 +701,7 @@
   void visitLetPrim(LetPrim node) {
     AbstractValue value = getValue(node.primitive);
     if (node.primitive is! Constant &&
+        node.primitive is! Refinement &&
         node.primitive.isSafeForElimination &&
         value.isConstant) {
       // If the value is a known constant, compile it as a constant.
@@ -1132,16 +1134,17 @@
   /// This is a short-term solution to avoid inserting a lot of bounds checks,
   /// since there is currently no optimization for eliminating them.
   bool hasTooManyIndexAccesses(Primitive receiver) {
+    receiver = receiver.effectiveDefinition;
     int count = 0;
-    for (Reference ref = receiver.firstRef; ref != null; ref = ref.next) {
+    for (Reference ref in receiver.effectiveUses) {
       Node use = ref.parent;
       if (use is InvokeMethod &&
           (use.selector.isIndex || use.selector.isIndexSet) &&
-          getDartReceiver(use) == receiver) {
+          getDartReceiver(use).sameValue(receiver)) {
         ++count;
-      } else if (use is GetIndex && use.object.definition == receiver) {
+      } else if (use is GetIndex && use.object.definition.sameValue(receiver)) {
         ++count;
-      } else if (use is SetIndex && use.object.definition == receiver) {
+      } else if (use is SetIndex && use.object.definition.sameValue(receiver)) {
         ++count;
       }
       if (count > 2) return true;
@@ -1322,7 +1325,7 @@
         // Check that all uses of the iterator are 'moveNext' and 'current'.
         assert(!isInterceptedSelector(Selectors.moveNext));
         assert(!isInterceptedSelector(Selectors.current));
-        for (Reference ref = iterator.firstRef; ref != null; ref = ref.next) {
+        for (Reference ref in iterator.effectiveUses) {
           if (ref.parent is! InvokeMethod) return false;
           InvokeMethod use = ref.parent;
           if (ref != use.receiver) return false;
@@ -1339,8 +1342,8 @@
         MutableVariable current = new MutableVariable(new LoopItemEntity());
 
         // Rewrite all uses of the iterator.
-        while (iterator.firstRef != null) {
-          InvokeMethod use = iterator.firstRef.parent;
+        for (Reference ref in iterator.effectiveUses) {
+          InvokeMethod use = ref.parent;
           Continuation useCont = use.continuation.definition;
           if (use.selector == Selectors.current) {
             // Rewrite iterator.current to a use of the 'current' variable.
@@ -1446,6 +1449,9 @@
           }
         }
 
+        // All effective uses have been rewritten.
+        destroyRefinementsOfDeadPrimitive(iterator);
+
         // Rewrite the iterator call to initializers for 'index' and 'current'.
         CpsFragment cps = new CpsFragment();
         cps.letMutable(index, cps.makeZero());
@@ -1488,7 +1494,8 @@
     while (true) {
       Node parent = node.parent;
       if (parent is LetCont ||
-          parent is LetPrim && parent.primitive.isSafeForReordering) {
+          parent is LetPrim && parent.primitive.isSafeForReordering ||
+          parent is LetPrim && parent.primitive is Refinement) {
         node = parent;
       } else {
         return parent;
@@ -1510,7 +1517,7 @@
     assert(!isInterceptedSelector(call));
     assert(call.argumentCount == node.arguments.length);
 
-    Primitive tearOff = node.receiver.definition;
+    Primitive tearOff = node.receiver.definition.effectiveDefinition;
     // Note: We don't know if [tearOff] is actually a tear-off.
     // We name variables based on the pattern we are trying to match.
 
@@ -1545,9 +1552,6 @@
 
       Continuation getterCont = tearOffInvoke.continuation.definition;
 
-      // TODO(asgerf): Support torn-off intercepted methods.
-      if (isInterceptedSelector(getter)) return false;
-
       Primitive object = tearOffInvoke.receiver.definition;
 
       // Ensure that the object actually has a foo member, since we might
@@ -1561,7 +1565,7 @@
 
       // If there are multiple uses, we cannot eliminate the getter call and
       // therefore risk duplicating its side effects.
-      if (!isPure && tearOff.hasMultipleUses) return false;
+      if (!isPure && tearOff.hasMultipleEffectiveUses) return false;
 
       // If the getter call is impure, we risk reordering side effects.
       if (!isPure && getEffectiveParent(node) != getterCont) {
@@ -1578,10 +1582,11 @@
       node.receiver.unlink();
       replaceSubtree(node, invoke, unlink: false);
 
-      if (tearOff.hasNoUses) {
+      if (tearOff.hasNoEffectiveUses) {
         // Eliminate the getter call if it has no more uses.
         // This cannot be delegated to other optimizations because we need to
         // avoid duplication of side effects.
+        destroyRefinementsOfDeadPrimitive(tearOff);
         getterCont.parameters.clear();
         replaceSubtree(tearOffInvoke, new InvokeContinuation(getterCont, []));
       } else {
@@ -1597,6 +1602,18 @@
     return false;
   }
 
+  void destroyRefinementsOfDeadPrimitive(Primitive prim) {
+    while (prim.firstRef != null) {
+      Refinement refine = prim.firstRef.parent;
+      destroyRefinementsOfDeadPrimitive(refine);
+      LetPrim letPrim = refine.parent;
+      InteriorNode parent = letPrim.parent;
+      parent.body = letPrim.body;
+      letPrim.body.parent = parent;
+      prim.firstRef.unlink();
+    }
+  }
+
   /// Inlines a single-use closure if it leaves the closure object with only
   /// field accesses.  This is optimized later by [ScalarReplacer].
   bool specializeSingleUseClosureCall(InvokeMethod node) {
@@ -1628,6 +1645,11 @@
     Selector targetSelector = new Selector.fromElement(functionElement);
     if (call.callStructure != targetSelector.callStructure) return false;
 
+    // Don't inline if [target] contains try-catch or try-finally. JavaScript
+    // engines typically do poor optimization of the entire function containing
+    // the 'try'.
+    if (functionElement.resolvedAst.elements.containsTryStatement) return false;
+
     FunctionDefinition target =
         functionCompiler.compileToCpsIR(functionElement);
 
@@ -1645,11 +1667,6 @@
       return false;
     }
 
-    // Don't inline if [target] contains try-catch or try-finally. JavaScript
-    // engines typically do poor optimization of the entire function containing
-    // the 'try'.
-    if (ContainsTry.analyze(target)) return false;
-
     node.receiver.definition.substituteFor(target.thisParameter);
     for (int i = 0; i < node.arguments.length; ++i) {
       node.arguments[i].definition.substituteFor(target.parameters[i]);
@@ -1692,7 +1709,7 @@
     node.receiverIsNotNull = receiver.isDefinitelyNotNull;
 
     if (isInterceptedSelector(node.selector) &&
-        node.receiver.definition == node.arguments[0].definition) {
+        node.receiver.definition.sameValue(node.arguments[0].definition)) {
       // The receiver and first argument are the same; that means we already
       // determined in visitInterceptor that we are targeting a non-interceptor.
 
@@ -1833,9 +1850,8 @@
 
     ast.Statement body = node.target.node.body;
     bool shouldInline() {
-      if (backend.annotations.noInline(node.target)) {
-        return false;
-      }
+      if (backend.annotations.noInline(node.target)) return false;
+      if (node.target.resolvedAst.elements.containsTryStatement) return false;
 
       // Inline functions that are a single return statement, expression
       // statement, or block containing a return statement or expression
@@ -1986,7 +2002,7 @@
       if (parent is LetPrim && parent.primitive is ApplyBuiltinMethod) {
         ApplyBuiltinMethod previous = parent.primitive;
         if (previous.method == BuiltinMethod.Push &&
-            previous.receiver.definition == node.receiver.definition) {
+            previous.receiver.definition.sameValue(node.receiver.definition)) {
           // We found two consecutive pushes.
           // Move all arguments from the first push onto the second one.
           List<Reference<Primitive>> arguments = previous.arguments;
@@ -2063,16 +2079,9 @@
     } else {
       singleClass = typeSystem.singleClass(value.type);
     }
-    if (singleClass != null) {
-      if (singleClass.isSubclassOf(backend.jsInterceptorClass)) {
-        Primitive constant = makeConstantPrimitive(
-            new InterceptorConstantValue(singleClass.rawType));
-        constant.hint = node.hint;
-        return constant;
-      } else {
-        node.input.definition.substituteFor(node);
-        return null;
-      }
+    if (singleClass != null &&
+        singleClass.isSubclassOf(backend.jsInterceptorClass)) {
+      node.constantValue = new InterceptorConstantValue(singleClass.rawType);
     }
     // Filter out intercepted classes that do not match the input type.
     node.interceptedClasses.retainWhere((ClassElement clazz) {
@@ -2120,6 +2129,8 @@
 
   JavaScriptBackend get backend => typeSystem.backend;
 
+  World get classWorld => typeSystem.classWorld;
+
   AbstractValue get nothing => lattice.nothing;
 
   AbstractValue nonConstant([TypeMask type]) => lattice.nonConstant(type);
@@ -2560,9 +2571,15 @@
         break; // Cast fails. Continuation should remain unreachable.
 
       case AbstractBool.Maybe:
-        // TODO(asgerf): Narrow type of output to those that survive the cast.
         setReachable(cont);
-        setValue(cont.parameters.single, input);
+        TypeMask type = input.type;
+        if (node.type.element is ClassElement) {
+          // Narrow type of output to those that survive the cast.
+          type = type.intersection(
+              new TypeMask.subtype(node.type.element, classWorld),
+              classWorld);
+        }
+        setValue(cont.parameters.single, nonConstant(type));
         break;
     }
   }
@@ -2676,7 +2693,7 @@
   }
 
   @override
-  visitTypeExpression(TypeExpression node) {
+  void visitTypeExpression(TypeExpression node) {
     // TODO(karlklose): come up with a type marker for JS entities or switch to
     // real constants of type [Type].
     setValue(node, nonConstant());
@@ -2688,7 +2705,7 @@
   }
 
   @override
-  visitForeignCode(ForeignCode node) {
+  void visitForeignCode(ForeignCode node) {
     if (node.continuation != null) {
       Continuation continuation = node.continuation.definition;
       setReachable(continuation);
@@ -2715,10 +2732,23 @@
   }
 
   @override
-  visitAwait(Await node) {
+  void visitAwait(Await node) {
     Continuation continuation = node.continuation.definition;
     setReachable(continuation);
   }
+
+  @override
+  void visitRefinement(Refinement node) {
+    AbstractValue value = getValue(node.value.definition);
+    if (value.isNothing || typeSystem.areDisjoint(value.type, node.type)) {
+      setValue(node, nothing);
+    } else if (value.isConstant) {
+      setValue(node, value);
+    } else {
+      setValue(node,
+          nonConstant(value.type.intersection(node.type, classWorld)));
+    }
+  }
 }
 
 /// Represents the abstract value of a primitive value at some point in the
@@ -2837,25 +2867,3 @@
     values.remove(node.variable);
   }
 }
-
-
-class ContainsTry extends RecursiveVisitor {
-  bool _found = false;
-  ContainsTry._();
-
-  /// Scans [root] for evidence of try-catch and try-finally.
-  static bool analyze(Node root) {
-    ContainsTry visitor = new ContainsTry._();
-    visitor.visit(root);
-    return visitor._found;
-  }
-
-  visit(Node node) {
-    if (_found) return;  // Early exit if we know the answer.
-    super.visit(node);
-  }
-
-  processLetHandler(LetHandler node) {
-    _found = true;
-  }
-}
diff --git a/pkg/compiler/lib/src/diagnostics/messages.dart b/pkg/compiler/lib/src/diagnostics/messages.dart
index 99aa824..b812fd3 100644
--- a/pkg/compiler/lib/src/diagnostics/messages.dart
+++ b/pkg/compiler/lib/src/diagnostics/messages.dart
@@ -421,6 +421,7 @@
   UNIMPLEMENTED_SETTER,
   UNIMPLEMENTED_SETTER_ONE,
   UNMATCHED_TOKEN,
+  UNRECOGNIZED_VERSION_OF_LOOKUP_MAP,
   UNSUPPORTED_BANG_EQ_EQ,
   UNSUPPORTED_EQ_EQ_EQ,
   UNSUPPORTED_LITERAL_SYMBOL,
@@ -3295,6 +3296,10 @@
           "more code and prevents the compiler from doing some optimizations.",
           howToFix: "Consider removing this 'noSuchMethod' implementation."),
 
+      MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP:
+        const MessageTemplate(MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP,
+          "Unsupported version of package:lookup_map.",
+          howToFix: DONT_KNOW_HOW_TO_FIX),
 
   }; // End of TEMPLATES.
 
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index f8ba241..e031100 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -989,12 +989,8 @@
     } else if (constant.isType) {
       enqueueInResolution(getCreateRuntimeType(), registry);
       registry.registerInstantiation(typeImplementation.rawType);
-      TypeConstantValue typeConstant = constant;
-      DartType representedType = typeConstant.representedType;
-      if (representedType != const DynamicType()) {
-        lookupMapAnalysis.registerTypeConstant(representedType.element);
-      }
     }
+    lookupMapAnalysis.registerConstantKey(constant);
   }
 
   void registerInstantiatedConstantType(DartType type, Registry registry) {
@@ -2163,7 +2159,7 @@
       } else if (uri == Uris.dart_html) {
         htmlLibraryIsLoaded = true;
       } else if (uri == PACKAGE_LOOKUP_MAP) {
-        lookupMapAnalysis.initRuntimeClass(find(library, 'LookupMap'));
+        lookupMapAnalysis.init(library);
       }
       annotations.onLibraryScanned(library);
     });
@@ -2609,7 +2605,13 @@
     return true;
   }
 
-  void onQueueClosed() => lookupMapAnalysis.onQueueClosed();
+  void onQueueClosed() {
+    lookupMapAnalysis.onQueueClosed();
+  }
+
+  void onCodegenStart() {
+    lookupMapAnalysis.onCodegenStart();
+  }
 
   void onElementResolved(Element element, TreeElements elements) {
     if ((element.isFunction || element.isGenerativeConstructor) &&
diff --git a/pkg/compiler/lib/src/js_backend/codegen/task.dart b/pkg/compiler/lib/src/js_backend/codegen/task.dart
index 16c882a..93a2320 100644
--- a/pkg/compiler/lib/src/js_backend/codegen/task.dart
+++ b/pkg/compiler/lib/src/js_backend/codegen/task.dart
@@ -170,18 +170,22 @@
       assert(checkCpsIntegrity(cpsNode));
     }
 
+    applyCpsPass(new RedundantJoinEliminator());
+    applyCpsPass(new RedundantPhiEliminator());
+    applyCpsPass(new InsertRefinements(compiler.typesTask, compiler.world));
     TypePropagator typePropagator = new TypePropagator(compiler, this);
     applyCpsPass(typePropagator);
     dumpTypedIR(cpsNode, typePropagator);
-    applyCpsPass(new LoopInvariantCodeMotion());
-    applyCpsPass(new ShareInterceptors());
-    applyCpsPass(new ScalarReplacer(compiler));
+    applyCpsPass(new RemoveRefinements());
     applyCpsPass(new ShrinkingReducer());
+    applyCpsPass(new ScalarReplacer(compiler));
     applyCpsPass(new MutableVariableEliminator());
     applyCpsPass(new RedundantJoinEliminator());
     applyCpsPass(new RedundantPhiEliminator());
     applyCpsPass(new ShrinkingReducer());
-    applyCpsPass(new LetSinker());
+    applyCpsPass(new LoopInvariantCodeMotion());
+    applyCpsPass(new ShareInterceptors());
+    applyCpsPass(new ShrinkingReducer());
 
     return cpsNode;
   }
diff --git a/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart b/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
index 9d44af1..7be572b 100644
--- a/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
+++ b/pkg/compiler/lib/src/js_backend/lookup_map_analysis.dart
@@ -7,15 +7,24 @@
 
 import '../common/registry.dart' show Registry;
 import '../compiler.dart' show Compiler;
+import '../diagnostics/messages.dart' show MessageKind;
 import '../constants/values.dart' show
      ConstantValue,
      ConstructedConstantValue,
      ListConstantValue,
      NullConstantValue,
+     StringConstantValue,
      TypeConstantValue;
 import '../dart_types.dart' show DartType;
-import '../elements/elements.dart' show Elements, Element, ClassElement,
-     FieldElement, FunctionElement, FunctionSignature;
+import '../elements/elements.dart' show
+    ClassElement,
+    Element,
+    Elements,
+    FieldElement,
+    FunctionElement,
+    FunctionSignature,
+    LibraryElement,
+    VariableElement;
 import '../enqueue.dart' show Enqueuer;
 import 'js_backend.dart' show JavaScriptBackend;
 import '../dart_types.dart' show DynamicType, InterfaceType;
@@ -63,6 +72,13 @@
   /// discover that a key in a map is potentially used.
   final JavaScriptBackend backend;
 
+  /// The resolved [VariableElement] associated with the top-level `_version`.
+  VariableElement lookupMapVersionVariable;
+
+  /// The resolved [LibraryElement] associated with
+  /// `package:lookup_map/lookup_map.dart`.
+  LibraryElement lookupMapLibrary;
+
   /// The resolved [ClassElement] associated with `LookupMap`.
   ClassElement typeLookupMapClass;
 
@@ -79,18 +95,29 @@
   /// this analysis.
   final Map<ConstantValue, _LookupMapInfo> _lookupMaps = {};
 
-  /// Types that we have discovered to be in use in the program.
-  final _inUse = new Set<ClassElement>();
+  /// Keys that we have discovered to be in use in the program.
+  final _inUse = new Set<ConstantValue>();
 
-  /// Pending work to do if we discover that a new type is in use. For each type
-  /// that we haven't seen, we record the list of lookup-maps that use such type
-  /// as a key.
-  final _pending = <ClassElement, List<_LookupMapInfo>>{};
+  /// Internal helper to memoize the mapping between class elements and their
+  /// corresponding type constants.
+  final _typeConstants = <ClassElement, TypeConstantValue>{};
+
+  /// Internal helper to memoize which classes (ignoring Type) override equals.
+  ///
+  /// Const keys of these types will not be tree-shaken because we can't
+  /// statically guarantee that the program doesn't produce an equivalent key at
+  /// runtime. Technically if we limit lookup-maps to check for identical keys,
+  /// we could allow const instances of these types.  However, we internally use
+  /// a hash map within lookup-maps today, so we need this restriction.
+  final _typesWithEquals = <ClassElement, bool>{};
+
+  /// Pending work to do if we discover that a new key is in use. For each key
+  /// that we haven't seen, we record the list of lookup-maps that contain an
+  /// entry with that key.
+  final _pending = <ConstantValue, List<_LookupMapInfo>>{};
 
   /// Whether the backend is currently processing the codegen queue.
-  // TODO(sigmund): is there a better way to do this. Do we need to plumb the
-  // enqueuer on each callback?
-  bool get _inCodegen => backend.compiler.phase == Compiler.PHASE_COMPILING;
+  bool _inCodegen = false;
 
   LookupMapAnalysis(this.backend);
 
@@ -101,14 +128,53 @@
     return typeLookupMapClass != null;
   }
 
-  /// Initializes this analysis by providing the resolver information of
-  /// `LookupMap`.
-  void initRuntimeClass(ClassElement cls) {
+  /// Initializes this analysis by providing the resolved library. This is
+  /// invoked during resolution when the `lookup_map` library is discovered.
+  void init(LibraryElement library) {
+    lookupMapLibrary = library;
+    // We will enable the lookupMapAnalysis as long as we get a known version of
+    // the lookup_map package. We otherwise produce a warning.
+    lookupMapVersionVariable = library.implementation.findLocal('_version');
+    if (lookupMapVersionVariable == null) {
+      backend.compiler.reportInfo(library,
+          MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
+    } else {
+      backend.compiler.enqueuer.resolution.addToWorkList(
+          lookupMapVersionVariable);
+    }
+  }
+
+  /// Checks if the version of lookup_map is valid, and if so, enable this
+  /// analysis during codegen.
+  void onCodegenStart() {
+    _inCodegen = true;
+    if (lookupMapVersionVariable == null) return;
+
+    // At this point, the lookupMapVersionVariable should be resolved and it's
+    // constant value should be available.
+    StringConstantValue value =
+        backend.constants.getConstantValueForVariable(lookupMapVersionVariable);
+    if (value == null) {
+      backend.compiler.reportInfo(lookupMapVersionVariable,
+          MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
+      return;
+    }
+
+    // TODO(sigmund): add proper version resolution using the pub_semver package
+    // when we introduce the next version.
+    String version = value.primitiveValue.slowToString();
+    if (version != '0.0.1') {
+      backend.compiler.reportInfo(lookupMapVersionVariable,
+          MessageKind.UNRECOGNIZED_VERSION_OF_LOOKUP_MAP);
+      return;
+    }
+
+    ClassElement cls = lookupMapLibrary.findLocal('LookupMap');
     cls.computeType(backend.compiler);
     entriesField = cls.lookupMember('_entries');
     keyField = cls.lookupMember('_key');
     valueField = cls.lookupMember('_value');
-    // TODO(sigmund): Maybe inline nested maps make the output code smaller?
+    // TODO(sigmund): Maybe inline nested maps to make the output code smaller?
     typeLookupMapClass = cls;
   }
 
@@ -126,12 +192,46 @@
         () => new _LookupMapInfo(lookupMap, this).._updateUsed());
   }
 
-  /// Records that [type] is used in the program, and updates every map that
-  /// has it as a key.
-  void _addUse(ClassElement type) {
-    if (_inUse.add(type)) {
-      _pending[type]?.forEach((info) => info._markUsed(type));
-      _pending.remove(type);
+  /// Whether [key] is a constant value whose type overrides equals.
+  bool _overridesEquals(ConstantValue key) {
+    if (key is ConstructedConstantValue) {
+      ClassElement element = key.type.element;
+      return _typesWithEquals.putIfAbsent(element, () =>
+          element.lookupMember('==').enclosingClass !=
+          backend.compiler.objectClass);
+    }
+    return false;
+  }
+
+  /// Whether we need to preserve [key]. This is true for keys that are not
+  /// candidates for tree-shaking in the first place (primitives and non-type
+  /// const values overriding equals) and keys that we have seen in the program.
+  bool _shouldKeep(ConstantValue key) =>
+      key.isPrimitive || _inUse.contains(key) || _overridesEquals(key);
+
+  void _addClassUse(ClassElement cls) {
+    ConstantValue key = _typeConstants.putIfAbsent(cls,
+        () => backend.constantSystem.createType(backend.compiler, cls.rawType));
+    _addUse(key);
+  }
+
+  /// Record that [key] is used and update every lookup map that contains it.
+  void _addUse(ConstantValue key) {
+    if (_inUse.add(key)) {
+      _pending[key]?.forEach((info) => info._markUsed(key));
+      _pending.remove(key);
+    }
+  }
+
+  /// If [key] is a type, cache it in [_typeConstants].
+  _registerTypeKey(ConstantValue key) {
+    if (key is TypeConstantValue) {
+      ClassElement cls = key.representedType.element;
+      if (cls == null || !cls.isClass) {
+        // TODO(sigmund): report error?
+        return;
+      }
+      _typeConstants[cls] = key;
     }
   }
 
@@ -139,14 +239,14 @@
   void registerInstantiatedClass(ClassElement element) {
     if (!_isEnabled || !_inCodegen) return;
     // TODO(sigmund): only add if .runtimeType is ever used
-    _addUse(element);
+    _addClassUse(element);
   }
 
   /// Callback from the enqueuer, invoked when [type] is instantiated.
   void registerInstantiatedType(InterfaceType type, Registry registry) {
     if (!_isEnabled || !_inCodegen) return;
     // TODO(sigmund): only add if .runtimeType is ever used
-    _addUse(type.element);
+    _addClassUse(type.element);
     // TODO(sigmund): only do this when type-argument expressions are used?
     _addGenerics(type, registry);
   }
@@ -157,7 +257,7 @@
     if (!type.isGeneric) return;
     for (var arg in type.typeArguments) {
       if (arg is InterfaceType) {
-        _addUse(arg.element);
+        _addClassUse(arg.element);
         // Note: this call was needed to generate correct code for
         // type_lookup_map/generic_type_test
         // TODO(sigmund): can we get rid of this?
@@ -168,12 +268,17 @@
     }
   }
 
-  /// Callback from the codegen enqueuer, invoked when a type constant
-  /// corresponding to the [element] is used in the program.
-  void registerTypeConstant(Element element) {
+  /// Callback from the codegen enqueuer, invoked when a constant (which is
+  /// possibly a const key or a type literal) is used in the program.
+  void registerTypeConstant(ClassElement element) {
     if (!_isEnabled || !_inCodegen) return;
-    assert(element.isClass);
-    _addUse(element);
+    _addClassUse(element);
+  }
+
+  void registerConstantKey(ConstantValue constant) {
+    if (!_isEnabled || !_inCodegen) return;
+    if (constant.isPrimitive || _overridesEquals(constant)) return;
+    _addUse(constant);
   }
 
   /// Callback from the backend, invoked when reaching the end of the enqueuing
@@ -206,6 +311,11 @@
           ? 'lookup-map: nothing was tree-shaken'
           : 'lookup-map: found $count unused keys ($sb)');
     }
+
+    // Release resources.
+    _lookupMaps.clear();
+    _pending.clear();
+    _inUse.clear();
   }
 }
 
@@ -235,17 +345,12 @@
 
   /// Entries in the lookup map whose keys have not been seen in the rest of the
   /// program.
-  Map<ClassElement, ConstantValue> unusedEntries =
-      <ClassElement, ConstantValue>{};
+  Map<ConstantValue, ConstantValue> unusedEntries =
+      <ConstantValue, ConstantValue> {};
 
   /// Entries that have been used, and thus will be part of the generated code.
-  Map<ClassElement, ConstantValue> usedEntries =
-      <ClassElement, ConstantValue>{};
-
-  /// Internal helper to memoize the mapping between map class elements and
-  /// their corresponding type constants.
-  Map<ClassElement, TypeConstantValue> _typeConstants =
-      <ClassElement, TypeConstantValue>{};
+  Map<ConstantValue, ConstantValue> usedEntries =
+      <ConstantValue, ConstantValue> {};
 
   /// Creates and initializes the information containing all keys of the
   /// original map marked as unused.
@@ -254,10 +359,7 @@
     singlePair = !key.isNull;
 
     if (singlePair) {
-      TypeConstantValue typeKey = key;
-      ClassElement cls = typeKey.representedType.element;
-      _typeConstants[cls] = typeKey;
-      unusedEntries[cls] = original.fields[analysis.valueField];
+      unusedEntries[key] = original.fields[analysis.valueField];
 
       // Note: we modify the constant in-place, see comment in [original].
       original.fields[analysis.keyField] = new NullConstantValue();
@@ -266,14 +368,8 @@
       ListConstantValue list = original.fields[analysis.entriesField];
       List<ConstantValue> keyValuePairs = list.entries;
       for (int i = 0; i < keyValuePairs.length; i += 2) {
-        TypeConstantValue type = keyValuePairs[i];
-        ClassElement cls = type.representedType.element;
-        if (cls == null || !cls.isClass) {
-          // TODO(sigmund): report an error
-          continue;
-        }
-        _typeConstants[cls] = type;
-        unusedEntries[cls] = keyValuePairs[i + 1];
+        ConstantValue key = keyValuePairs[i];
+        unusedEntries[key] = keyValuePairs[i + 1];
       }
 
       // Note: we modify the constant in-place, see comment in [original].
@@ -288,23 +384,24 @@
   /// we call [_markUsed] on each individual key as it gets discovered.
   void _updateUsed() {
     // Note: we call toList because `_markUsed` modifies the map.
-    for (ClassElement type in unusedEntries.keys.toList()) {
-      if (analysis._inUse.contains(type)) {
-        _markUsed(type);
+    for (ConstantValue key in unusedEntries.keys.toList()) {
+      analysis._registerTypeKey(key);
+      if (analysis._shouldKeep(key)) {
+        _markUsed(key);
       } else {
-        analysis._pending.putIfAbsent(type, () => []).add(this);
+        analysis._pending.putIfAbsent(key, () => []).add(this);
       }
     }
   }
 
-  /// Marks that [type] is a key that has been seen, and thus, the corresponding
-  /// entry in this map should be considered reachable.
-  void _markUsed(ClassElement type) {
+  /// Marks that [key] has been seen, and thus, the corresponding entry in this
+  /// map should be considered reachable.
+  _markUsed(ConstantValue key) {
     assert(!emitted);
-    assert(unusedEntries.containsKey(type));
-    assert(!usedEntries.containsKey(type));
-    ConstantValue constant = unusedEntries.remove(type);
-    usedEntries[type] = constant;
+    assert(unusedEntries.containsKey(key));
+    assert(!usedEntries.containsKey(key));
+    ConstantValue constant = unusedEntries.remove(key);
+    usedEntries[key] = constant;
     analysis.backend.registerCompileTimeConstant(constant,
         analysis.backend.compiler.globalDependencies,
         addForEmission: false);
@@ -316,7 +413,7 @@
     DartType listType = originalEntries.type;
     List<ConstantValue> keyValuePairs = <ConstantValue>[];
     usedEntries.forEach((key, value) {
-      keyValuePairs.add(_typeConstants[key]);
+      keyValuePairs.add(key);
       keyValuePairs.add(value);
     });
 
diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart
index 5b7558b..dbfdd13 100644
--- a/pkg/compiler/lib/src/resolution/members.dart
+++ b/pkg/compiler/lib/src/resolution/members.dart
@@ -4563,6 +4563,10 @@
   }
 
   ResolutionResult visitTryStatement(TryStatement node) {
+    // TODO(karlklose): also track the information about mutated variables,
+    // catch, and finally-block.
+    registry.registerTryStatement();
+
     visit(node.tryBlock);
     if (node.catchBlocks.isEmpty && node.finallyBlock == null) {
       error(node.getEndToken().next, MessageKind.NO_CATCH_NOR_FINALLY);
diff --git a/pkg/compiler/lib/src/resolution/registry.dart b/pkg/compiler/lib/src/resolution/registry.dart
index 1c294aa..6a40754 100644
--- a/pkg/compiler/lib/src/resolution/registry.dart
+++ b/pkg/compiler/lib/src/resolution/registry.dart
@@ -609,4 +609,8 @@
   void registerAsyncForIn(AsyncForIn node) {
     backend.resolutionCallbacks.onAsyncForIn(node, this);
   }
+
+  void registerTryStatement() {
+    mapping.containsTryStatement = true;
+  }
 }
diff --git a/pkg/compiler/lib/src/resolution/tree_elements.dart b/pkg/compiler/lib/src/resolution/tree_elements.dart
index f69e8c2..3fed5c3 100644
--- a/pkg/compiler/lib/src/resolution/tree_elements.dart
+++ b/pkg/compiler/lib/src/resolution/tree_elements.dart
@@ -122,6 +122,9 @@
 
   /// Returns the label that [node] targets.
   LabelDefinition getTargetLabel(GotoStatement node);
+
+  /// `true` if the [analyzedElement]'s source code contains a [TryStatement].
+  bool get containsTryStatement;
 }
 
 class TreeElementMapping extends TreeElements {
@@ -140,6 +143,7 @@
   Setlet<Send> _asserts;
   Maplet<Send, SendStructure> _sendStructureMap;
   Setlet<DartType> _requiredTypes;
+  bool containsTryStatement = false;
 
   /// Map from nodes to the targets they define.
   Map<Node, JumpTarget> _definedTargets;
diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
index f9d87f9..209e151 100644
--- a/pkg/compiler/lib/src/ssa/builder.dart
+++ b/pkg/compiler/lib/src/ssa/builder.dart
@@ -1623,6 +1623,22 @@
             sourceInformation: sourceInformationBuilder.buildIf(function.body));
       }
     }
+    if (const bool.fromEnvironment('unreachable-throw') == true) {
+      var emptyParameters = parameters.values.where((p) =>
+          p.instructionType.isEmpty && !p.instructionType.isNullable);
+      if (emptyParameters.length > 0) {
+        String message = compiler.enableMinification
+            ? 'unreachable'
+            : 'unreachable: ${functionElement} because: ${emptyParameters}';
+        // TODO(sra): Use a library function that throws a proper error object.
+        push(new HForeignCode(
+            js.js.parseForeignJS('throw "$message"'),
+            backend.dynamicType,
+            <HInstruction>[],
+            isStatement: true));
+        return closeFunction();
+      }
+    }
     function.body.accept(this);
     return closeFunction();
   }
@@ -8449,6 +8465,8 @@
                            int maxInliningNodes,
                            bool useMaxInliningNodes,
                            {bool allowLoops: false}) {
+    if (function.resolvedAst.elements.containsTryStatement) return false;
+
     InlineWeeder weeder =
         new InlineWeeder(maxInliningNodes, useMaxInliningNodes, allowLoops);
     ast.FunctionExpression functionExpression = function.node;
@@ -8531,11 +8549,6 @@
     seenReturn = true;
   }
 
-  void visitTryStatement(ast.Node node) {
-    if (!registerNode()) return;
-    tooDifficult = true;
-  }
-
   void visitThrow(ast.Throw node) {
     if (!registerNode()) return;
     // For now, we don't want to handle throw after a return even if
diff --git a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
index 4949bc9..f6b6a72 100644
--- a/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
+++ b/pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart
@@ -358,6 +358,8 @@
     return exp is Constant ||
            exp is This ||
            exp is CreateInvocationMirror ||
+           exp is CreateInstance ||
+           exp is CreateBox ||
            exp is GetStatic && exp.element.isFunction ||
            exp is Interceptor ||
            exp is ApplyBuiltinOperator ||
diff --git a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
index 89b8b19..9ad632c 100644
--- a/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
+++ b/pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart
@@ -675,6 +675,11 @@
     return makeCallExpression(node, value);
   }
 
+  @override
+  Expression visitRefinement(cps_ir.Refinement node) {
+    throw 'Unexpected Refinement node in tree builder';
+  }
+
   /********** UNUSED VISIT METHODS *************/
 
   unexpectedNode(cps_ir.Node node) {
diff --git a/pkg/lookup_map/README.md b/pkg/lookup_map/README.md
index 73f5a81..ecab6b5 100644
--- a/pkg/lookup_map/README.md
+++ b/pkg/lookup_map/README.md
@@ -14,6 +14,8 @@
 that primitives, Strings, and constant objects that override the `==` operator
 cannot be tree-shaken.
 
+**Note**: this feature is currently experimental in dart2js, we recommend trying
+other alternatives before relying on this feature.
 
 ## Examples
 
diff --git a/pkg/lookup_map/lib/lookup_map.dart b/pkg/lookup_map/lib/lookup_map.dart
index 609f854..0c03094 100644
--- a/pkg/lookup_map/lib/lookup_map.dart
+++ b/pkg/lookup_map/lib/lookup_map.dart
@@ -91,3 +91,10 @@
 /// An expando that stores a flatten version of a [LookupMap], this is
 /// computed and stored the first time the map is accessed.
 final _flatMap = new Expando('_flat_map');
+
+/// Internal constant that matches the version in the pubspec. This is used by
+/// dart2js to ensure that optimizations are only enabled on known versions of
+/// this code.
+// Note: this needs to be kept in sync with the pubspec, otherwise
+// test/version_check_test would fail.
+final _version = '0.0.1';
diff --git a/pkg/lookup_map/pubspec.yaml b/pkg/lookup_map/pubspec.yaml
index fdc8946..8c420d0 100644
--- a/pkg/lookup_map/pubspec.yaml
+++ b/pkg/lookup_map/pubspec.yaml
@@ -1,3 +1,10 @@
 name: lookup_map
 description: a lookup-only map that can be tree-shaken by dart2js
+# Note: the version needs to be kept in sync with the string in
+# lib/lookup_map.dart, otherwise test/version_check_test would fail.
 version: 0.0.1
+author: "Dart Team <misc@dartlang.org>"
+homepage: https://github.com/dart-lang/sdk/blob/master/pkg/lookup_map/README.md
+dev_dependencies:
+  test: ^0.12.3+8
+  yaml: ^2.1.2
diff --git a/pkg/lookup_map/test/lookup_map_test.dart b/pkg/lookup_map/test/lookup_map_test.dart
new file mode 100644
index 0000000..ce001dd
--- /dev/null
+++ b/pkg/lookup_map/test/lookup_map_test.dart
@@ -0,0 +1,74 @@
+// 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:lookup_map/lookup_map.dart';
+
+import 'package:test/test.dart';
+
+class Key {
+  final int id;
+  const Key(this.id);
+}
+
+class A{}
+const B = const Key(1);
+class C{}
+
+main() {
+  test('entries constructor', () {
+    var m = const LookupMap(const [
+        A, "the-text-for-A",
+        B, "the-text-for-B",
+        1.2, "the-text-for-1.2"]);
+    expect(m[A], 'the-text-for-A');
+    expect(m[B], 'the-text-for-B');
+    expect(m[1.2], 'the-text-for-1.2');
+    expect(m[C], null);
+    expect(m[1.3], null);
+  });
+
+  test('pair constructor', () {
+    var m = const LookupMap.pair(A, "the-text-for-A");
+    expect(m[A], 'the-text-for-A');
+    expect(m[B], null);
+  });
+
+  test('nested lookup', () {
+    var m = const LookupMap(const [],
+        const [const LookupMap.pair(A, "the-text-for-A")]);
+    expect(m[A], 'the-text-for-A');
+    expect(m[B], null);
+  });
+
+  test('entry shadows nested maps', () {
+    var m = const LookupMap(const [
+      A, "the-text-for-A2",
+    ], const [
+      const LookupMap.pair(A, "the-text-for-A1"),
+    ]);
+    expect(m[A], 'the-text-for-A2');
+  });
+
+  test('nested maps shadow in order', () {
+    var m = const LookupMap(const [ ], const [
+      const LookupMap.pair(A, "the-text-for-A1"),
+      const LookupMap.pair(B, "the-text-for-B2"),
+      const LookupMap.pair(A, "the-text-for-A2"),
+      const LookupMap.pair(B, "the-text-for-B1"),
+    ]);
+    expect(m[A], 'the-text-for-A2');
+    expect(m[B], 'the-text-for-B1');
+  });
+
+  // This test would fail if dart2js has a bug, but we keep it here for our
+  // sanity.
+  test('reachable lookups are not tree-shaken', () {
+    var m = const LookupMap(const [
+      A, B,
+      B, C,
+      C, 3.4,
+    ]);
+    expect(m[m[m[A]]], 3.4);
+  });
+}
diff --git a/pkg/lookup_map/test/version_check_test.dart b/pkg/lookup_map/test/version_check_test.dart
new file mode 100644
index 0000000..2da0873
--- /dev/null
+++ b/pkg/lookup_map/test/version_check_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:io';
+import 'dart:mirrors';
+import 'package:lookup_map/lookup_map.dart'; // accessed via mirrors;
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
+
+/// This dartdoc helps remove a warning for the unused import on [LookupMap].
+main() {
+  test('validate version number matches', () {
+    var pubspecPath = Platform.script.resolve('../pubspec.yaml').path;
+    var yaml = loadYaml(new File(pubspecPath).readAsStringSync());
+    var version1 = yaml['version'];
+    var library = currentMirrorSystem().findLibrary(#lookup_map);
+    var version2 = library.getField(new Symbol('_version')).reflectee;
+    expect(version1, version2);
+  });
+}
diff --git a/pkg/pkg.status b/pkg/pkg.status
index 5877a42..ef4d6f7d 100644
--- a/pkg/pkg.status
+++ b/pkg/pkg.status
@@ -20,6 +20,7 @@
 [ $compiler == none && ($runtime == drt || $runtime == dartium || $runtime == ContentShellOnAndroid) ]
 mutation_observer: Skip # Issue 21149
 unittest/*: Skip # Issue 21949
+lookup_map/*: SkipByDesign
 
 [ $runtime == vm && $mode == debug]
 analysis_server/test/analysis_server_test: SkipSlow  # Times out
@@ -107,6 +108,7 @@
 analyzer/test/src/task/inputs_test: Pass, Slow # Issue 21628
 analyzer/test/src/task/manager_test: Pass, Slow # Issue 21628
 analyzer/test/src/task/model_test: Pass, Slow # Issue 21628
+lookup_map/test/version_check_test: SkipByDesign # Only meant to run in vm.
 
 [ $runtime == jsshell ]
 async/test/stream_zip_test: RuntimeError, OK # Timers are not supported.
@@ -194,3 +196,4 @@
 analyzer/test/src/task/inputs_test: Crash # (Iterable<JavaFile> ...  cannot handle sync*/async* functions
 analyzer/test/src/task/manager_test: Crash # (Iterable<JavaFile> ...  cannot handle sync*/async* functions
 analyzer/test/src/task/model_test: Crash # (Iterable<JavaFile> ...  cannot handle sync*/async* functions
+lookup_map/test/lookup_map_test: RuntimeError # $async$temp1.get$tests is not a function
diff --git a/runtime/bin/main.cc b/runtime/bin/main.cc
index 45fe8b4..ee7b80d 100644
--- a/runtime/bin/main.cc
+++ b/runtime/bin/main.cc
@@ -85,7 +85,7 @@
 extern const char* kPrecompiledSymbolName;
 static const char* kPrecompiledVmIsolateName = "precompiled.vmisolate";
 static const char* kPrecompiledIsolateName = "precompiled.isolate";
-static const char* kPrecompiledInstructionsName = "precompiled.instructions";
+static const char* kPrecompiledInstructionsName = "precompiled.S";
 
 
 // Global flag that is used to indicate that we want to trace resolution of
diff --git a/runtime/bin/socket_patch.dart b/runtime/bin/socket_patch.dart
index 94285aa..be459ba 100644
--- a/runtime/bin/socket_patch.dart
+++ b/runtime/bin/socket_patch.dart
@@ -280,9 +280,6 @@
   static const Duration _RETRY_DURATION_LOOPBACK =
       const Duration(milliseconds: 25);
 
-  // Use default Map so we keep order.
-  static Map<int, _NativeSocket> _sockets = new Map<int, _NativeSocket>();
-
   // Socket close state
   bool isClosed = false;
   bool isClosing = false;
@@ -317,13 +314,9 @@
   bool writeAvailable = false;
 
   static final Stopwatch sw = new Stopwatch()..start();
-  // Statistics.
-  int totalRead = 0;
-  int totalWritten = 0;
-  int readCount = 0;
-  int writeCount = 0;
-  double lastRead;
-  double lastWrite;
+
+  static bool connectedResourceHandler = false;
+  _ReadWriteResourceInfo resourceInfo;
 
   // The owner object is the object that the Socket is being used by, e.g.
   // a HttpServer, a WebSocket connection, a process pipe, etc.
@@ -441,6 +434,8 @@
                   _RETRY_DURATION_LOOPBACK :
                   _RETRY_DURATION;
               var timer = new Timer(duration, connectNext);
+              setupResourceInfo(socket);
+
               connecting[socket] = timer;
               // Setup handlers for receiving the first write event which
               // indicate that the socket is fully connected.
@@ -492,7 +487,6 @@
         .then((address) {
           var socket = new _NativeSocket.listen();
           socket.localAddress = address;
-
           var result = socket.nativeCreateBindListen(address._in_addr,
                                                      port,
                                                      backlog,
@@ -505,11 +499,16 @@
                                       port: port);
           }
           if (port != 0) socket.localPort = port;
+          setupResourceInfo(socket);
           socket.connectToEventHandler();
           return socket;
         });
   }
 
+  static void setupResourceInfo(_NativeSocket socket) {
+    socket.resourceInfo = new _SocketResourceInfo(socket);
+  }
+
   static Future<_NativeSocket> bindDatagram(
       host, int port, bool reuseAddress) {
     return new Future.value(host)
@@ -534,6 +533,7 @@
                                       port: port);
           }
           if (port != 0) socket.localPort = port;
+          setupResourceInfo(socket);
           return socket;
         });
   }
@@ -575,10 +575,18 @@
     }
     if (result != null) {
       available -= result.length;
-      totalRead += result.length;
+      // TODO(ricow): Remove when we track internal and pipe uses.
+      assert(resourceInfo != null || isPipe || isInternal);
+      if (resourceInfo != null) {
+        resourceInfo.totalRead += result.length;
+      }
     }
-    readCount++;
-    lastRead = timestamp;
+    // TODO(ricow): Remove when we track internal and pipe uses.
+    assert(resourceInfo != null || isPipe || isInternal);
+    if (resourceInfo != null) {
+      resourceInfo.readCount++;
+      resourceInfo.lastRead = timestamp;
+    }
     return result;
   }
 
@@ -595,10 +603,18 @@
       // receive. If available becomes > 0, the _NativeSocket will continue to
       // emit read events.
       available = nativeAvailable();
-      totalRead += result.data.length;
+      // TODO(ricow): Remove when we track internal and pipe uses.
+      assert(resourceInfo != null || isPipe || isInternal);
+      if (resourceInfo != null) {
+        resourceInfo.totalRead += result.data.length;
+      }
     }
-    readCount++;
-    lastRead = timestamp;
+    // TODO(ricow): Remove when we track internal and pipe uses.
+    assert(resourceInfo != null || isPipe || isInternal);
+    if (resourceInfo != null) {
+      resourceInfo.readCount++;
+      resourceInfo.lastRead = timestamp;
+    }
     return result;
   }
 
@@ -637,9 +653,13 @@
     }
     // Negate the result, as stated above.
     if (result < 0) result = -result;
-    totalWritten += result;
-    writeCount++;
-    lastWrite = timestamp;
+    // TODO(ricow): Remove when we track internal and pipe uses.
+    assert(resourceInfo != null || isPipe || isInternal);
+    if (resourceInfo != null) {
+      resourceInfo.totalWritten += result;
+      resourceInfo.writeCount++;
+      resourceInfo.lastWrite = timestamp;
+    }
     return result;
   }
 
@@ -656,9 +676,13 @@
       scheduleMicrotask(() => reportError(result, "Send failed"));
       result = 0;
     }
-    totalWritten += result;
-    writeCount++;
-    lastWrite = timestamp;
+    // TODO(ricow): Remove when we track internal and pipe uses.
+    assert(resourceInfo != null || isPipe || isInternal);
+    if (resourceInfo != null) {
+      resourceInfo.totalWritten += result;
+      resourceInfo.writeCount++;
+      resourceInfo.lastWrite = timestamp;
+    }
     return result;
   }
 
@@ -673,8 +697,13 @@
     if (nativeAccept(socket) != true) return null;
     socket.localPort = localPort;
     socket.localAddress = address;
-    totalRead += 1;
-    lastRead = timestamp;
+    setupResourceInfo(socket);
+    // TODO(ricow): Remove when we track internal and pipe uses.
+    assert(resourceInfo != null || isPipe || isInternal);
+    if (resourceInfo != null) {
+      resourceInfo.totalRead += 1;
+      resourceInfo.lastRead = timestamp;
+    }
     return socket;
   }
 
@@ -787,6 +816,11 @@
         if (i == DESTROYED_EVENT) {
           assert(isClosing);
           assert(!isClosed);
+          // TODO(ricow): Remove/update when we track internal and pipe uses.
+          assert(resourceInfo != null || isPipe || isInternal);
+          if (resourceInfo != null) {
+            _SocketResourceInfo.SocketClosed(resourceInfo);
+          }
           isClosed = true;
           closeCompleter.complete();
           disconnectFromEventHandler();
@@ -904,7 +938,14 @@
     assert(!isClosed);
     if (eventPort == null) {
       eventPort = new RawReceivePort(multiplex);
-      _sockets[_serviceId] = this;
+    }
+    if (!connectedResourceHandler) {
+      registerExtension('__getOpenSockets',
+                        _SocketResourceInfo.getOpenSockets);
+      registerExtension('__getSocketByID',
+                        _SocketResourceInfo.getSocketInfoMapByID);
+
+      connectedResourceHandler = true;
     }
   }
 
@@ -912,7 +953,6 @@
     assert(eventPort != null);
     eventPort.close();
     eventPort = null;
-    _sockets.remove(_serviceId);
     // Now that we don't track this Socket anymore, we can clear the owner
     // field.
     owner = null;
@@ -1017,123 +1057,6 @@
     if (result is OSError) throw result;
   }
 
-  String get _serviceTypePath => 'io/sockets';
-  String get _serviceTypeName => 'Socket';
-
-  String _JSONKind() {
-    return isListening ? "Listening" :
-           isPipe ? "Pipe" :
-           isInternal ? "Internal" : "Normal";
-  }
-
-  Map _toJSONPipe(bool ref) {
-    var name = 'Anonymous Pipe';
-    var r = {
-      'id': _servicePath,
-      'type': _serviceType(ref),
-      'name': name,
-      'user_name': name,
-      'kind': _JSONKind(),
-    };
-    if (ref) {
-      return r;
-    }
-    r['readClosed'] = isClosedRead;
-    r['writeClosed'] = isClosedWrite;
-    r['closing'] = isClosing;
-    r['fd'] = nativeGetSocketId();
-    if (owner != null) {
-      r['owner'] = owner._toJSON(true);
-    }
-    return r;
-  }
-
-  Map _toJSONInternal(bool ref) {
-    var name = 'Internal';
-    var r = {
-      'id': _servicePath,
-      'type': _serviceType(ref),
-      'name': name,
-      'user_name': name,
-      'kind': _JSONKind(),
-    };
-    if (ref) {
-      return r;
-    }
-    r['closing'] = isClosing;
-    r['fd'] = nativeGetSocketId();
-    if (owner != null) {
-      r['owner'] = owner._toJSON(true);
-    }
-    return r;
-  }
-
-  Map _toJSONNetwork(bool ref) {
-    var name = '${address.host}:$port';
-    if (isTcp && !isListening) name += " <-> ${remoteAddress.host}:$remotePort";
-    var r = {
-      'id': _servicePath,
-      'type': _serviceType(ref),
-      'name': name,
-      'user_name': name,
-      'kind': _JSONKind(),
-    };
-    if (ref) {
-      return r;
-    }
-    var protocol = isTcp ? "TCP" : isUdp ? "UDP" : null;
-    var localAddress;
-    var localPort;
-    var rAddress;
-    var rPort;
-    try {
-      localAddress = address.address;
-    } catch (e) { }
-    try {
-      localPort = port;
-    } catch (e) { }
-    try {
-      rAddress = this.remoteAddress.address;
-    } catch (e) { }
-    try {
-      rPort = remotePort;
-    } catch (e) { }
-    r['localAddress'] = localAddress;
-    r['localPort'] = localPort;
-    r['remoteAddress'] = rAddress;
-    r['remotePort'] = rPort;
-    r['protocol'] = protocol;
-    r['readClosed'] = isClosedRead;
-    r['writeClosed'] = isClosedWrite;
-    r['closing'] = isClosing;
-    r['listening'] = isListening;
-    r['fd'] = nativeGetSocketId();
-    if (owner != null) {
-      r['owner'] = owner._toJSON(true);
-    }
-    return r;
-  }
-
-  Map _toJSON(bool ref) {
-    var map;
-    if (isPipe) {
-      map =  _toJSONPipe(ref);
-    } else if (isInternal) {
-      map = _toJSONInternal(ref);
-    } else {
-      map = _toJSONNetwork(ref);
-    }
-    if (!ref) {
-      map['available'] = available;
-      map['totalRead'] = totalRead;
-      map['totalWritten'] = totalWritten;
-      map['readCount'] = totalWritten;
-      map['writeCount'] = writeCount;
-      map['lastRead'] = lastRead;
-      map['lastWrite'] = lastWrite;
-    }
-    return map;
-  }
 
   void nativeSetSocketId(int id) native "Socket_SetSocketId";
   nativeAvailable() native "Socket_Available";
@@ -1286,8 +1209,6 @@
     return new _RawServerSocketReference(_referencePort.sendPort);
   }
 
-  Map _toJSON(bool ref) => _socket._toJSON(ref);
-
   void set _owner(owner) { _socket.owner = owner; }
 }
 
@@ -1465,7 +1386,6 @@
     }
   }
 
-  Map _toJSON(bool ref) => _socket._toJSON(ref);
   void set _owner(owner) { _socket.owner = owner; }
 }
 
@@ -1528,8 +1448,6 @@
     return new _ServerSocketReference(_socket.reference);
   }
 
-  Map _toJSON(bool ref) => _socket._toJSON(ref);
-
   void set _owner(owner) { _socket._owner = owner; }
 }
 
@@ -1843,7 +1761,6 @@
     }
   }
 
-  Map _toJSON(bool ref) => _raw._toJSON(ref);
   void set _owner(owner) { _raw._owner = owner; }
 }
 
@@ -1999,4 +1916,3 @@
       port);
 }
 
-String _socketsStats() => _SocketsObservatory.toJSON();
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index 1ee5b25..b1ca62c 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -261,7 +261,7 @@
   }
   if (value.IsSmi()) {
     const Smi& smi_value = Smi::Cast(value);
-    return smi_value.ShiftOp(kind, amount, silent);
+    return smi_value.ShiftOp(kind, amount, Heap::kNew, silent);
   }
   if (value.IsMint()) {
     const int64_t mint_value = value.AsInt64Value();
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index c1a869a..65f7c62 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -602,7 +602,7 @@
   #define CHECK_KIND_SHIFT(name)                                               \
     field = cls.LookupField(String::Handle(String::New(#name)));               \
     ASSERT(!field.IsNull());                                                   \
-    value ^= field.value();                                                    \
+    value ^= field.StaticValue();                                              \
     ASSERT(value.Value() == Mirrors::name);
   MIRRORS_KIND_SHIFT_LIST(CHECK_KIND_SHIFT)
   #undef CHECK_KIND_SHIFT
@@ -677,7 +677,7 @@
     }
   } else {
     if (!field.IsUninitialized()) {
-      return field.value();
+      return field.StaticValue();
     }
     // An uninitialized field was found.  Check for a getter in the field's
     // owner classs.
@@ -754,7 +754,7 @@
         DartEntry::InvokeFunction(getter, Object::empty_array()));
     return ReturnResult(result);
   }
-  return field.value();
+  return field.StaticValue();
 }
 
 
@@ -1620,7 +1620,7 @@
     UNREACHABLE();
   }
 
-  field.set_value(value);
+  field.SetStaticValue(value);
   return value.raw();
 }
 
@@ -1917,7 +1917,7 @@
     UNREACHABLE();
   }
 
-  field.set_value(value);
+  field.SetStaticValue(value);
   return value.raw();
 }
 
diff --git a/runtime/lib/regexp.cc b/runtime/lib/regexp.cc
index 6fd9d6f..14bf14f 100644
--- a/runtime/lib/regexp.cc
+++ b/runtime/lib/regexp.cc
@@ -83,6 +83,7 @@
 
 
 DEFINE_NATIVE_ENTRY(JSSyntaxRegExp_ExecuteMatch, 3) {
+  // This function is intrinsified. See Intrinsifier::JSRegExp_ExecuteMatch.
   const JSRegExp& regexp = JSRegExp::CheckedHandle(arguments->NativeArgAt(0));
   ASSERT(!regexp.IsNull());
   GET_NON_NULL_NATIVE_ARGUMENT(String, subject, arguments->NativeArgAt(1));
@@ -93,15 +94,7 @@
                                                    zone);
   }
 
-  // This function is intrinsified. See Intrinsifier::JSRegExp_ExecuteMatch.
-  const intptr_t cid = subject.GetClassId();
-
-  // Retrieve the cached function.
-  const Function& fn = Function::Handle(regexp.function(cid));
-  ASSERT(!fn.IsNull());
-
-  // And finally call the generated code.
-  return IRRegExpMacroAssembler::Execute(fn, subject, start_index, zone);
+  return IRRegExpMacroAssembler::Execute(regexp, subject, start_index, zone);
 }
 
 }  // namespace dart
diff --git a/runtime/observatory/tests/service/coverage_test.dart b/runtime/observatory/tests/service/coverage_test.dart
index 4b44b37..5223092 100644
--- a/runtime/observatory/tests/service/coverage_test.dart
+++ b/runtime/observatory/tests/service/coverage_test.dart
@@ -11,7 +11,7 @@
 int globalVar = 100;
 
 class MyClass {
-  static void myFunction(int value) {
+       static       void myFunction(int value) {
     if (value < 0) {
       print("negative");
     } else {
@@ -22,9 +22,9 @@
 
   static void otherFunction(int value) {
     if (value < 0) {
-      print("otherFunction <");
+               print("otherFunction <");
     } else {
-      print("otherFunction >=");
+         print("otherFunction >=");
     }
   }
 }
@@ -79,8 +79,8 @@
   expect(coverage['coverage'][0]['hits'],
          equals([15, 1, 16, 0, 18, 1, 20, 1,
                  24, 1, 25, 1, 27, 0]));
-  expect(coverage['coverage'][1]['hits'].take(12),
-         equals([33, 1, 34, 1, 32, 1, 105, 2, 105, 1]));
+  expect(coverage['coverage'][1]['hits'],
+         equals([33, 1, 34, 1, 105, 2]));
 
   // Script
   await cls.load();
@@ -91,8 +91,8 @@
   expect(coverage['coverage'][0]['hits'],
          equals([15, 1, 16, 0, 18, 1, 20, 1,
                  24, 1, 25, 1, 27, 0]));
-  expect(coverage['coverage'][1]['hits'].take(12),
-         equals([33, 1, 34, 1, 32, 1, 105, 2, 105, 1]));
+  expect(coverage['coverage'][1]['hits'],
+         equals([33, 1, 34, 1, 105, 2]));
 
   // Isolate
   coverage = await isolate.invokeRpcNoUpgrade('_getCoverage', {});
diff --git a/runtime/observatory/tests/service/file_service_test.dart b/runtime/observatory/tests/service/file_service_test.dart
new file mode 100644
index 0000000..dbb8e53
--- /dev/null
+++ b/runtime/observatory/tests/service/file_service_test.dart
@@ -0,0 +1,101 @@
+// 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 'dart:convert';
+import 'dart:developer';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+Future setupFiles() async {
+  var dir = await io.Directory.systemTemp.createTemp('file_service');
+  var writingFile;
+  var readingFile;
+
+  void closeDown() {
+    if (writingFile != null) {
+      writingFile.closeSync();
+    }
+    if (readingFile != null) {
+      readingFile.closeSync();
+    }
+    dir.deleteSync(recursive: true);
+  }
+
+  Future<ServiceExtensionResponse> cleanup(ignored_a, ignored_b) {
+    closeDown();
+    var result = JSON.encode({'type' : 'foobar'});
+    return new Future.value(new ServiceExtensionResponse.result(result));
+  }
+
+  Future<ServiceExtensionResponse> setup(ignored_a, ignored_b) async {
+    try {
+      var filePath = dir.path + io.Platform.pathSeparator + "file";
+      var f = new io.File(filePath);
+      writingFile = await f.open(mode: io.FileMode.WRITE);
+      await writingFile.writeByte(42);
+      await writingFile.writeByte(42);
+      await writingFile.writeByte(42);
+
+      var file = new io.File.fromUri(io.Platform.script);
+      readingFile = await file.open();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+      await readingFile.readByte();
+
+      // The utility functions should close the files after them, so we
+      // don't expect the calls below to result in open files.
+      var writeTemp = dir.path + io.Platform.pathSeparator + "other_file";
+      var utilFile = new io.File(writeTemp);
+      await utilFile.writeAsString('foobar');
+      var readTemp = new io.File(writeTemp);
+      var result = await readTemp.readAsString();
+      expect(result, equals('foobar'));
+
+    } catch (e) {
+      closeDown();
+      throw e;
+    }
+    var result = JSON.encode({'type' : 'foobar'});
+    return new Future.value(new ServiceExtensionResponse.result(result));
+  }
+  registerExtension('__cleanup', cleanup);
+  registerExtension('__setup', setup);
+}
+
+var fileTests = [
+  (Isolate isolate) async {
+    await isolate.invokeRpcNoUpgrade('__setup', {});
+    try {
+      var result = await isolate.invokeRpcNoUpgrade('__getOpenFiles', {});
+      expect(result['type'], equals('_openfiles'));
+
+      expect(result['data'].length, equals(2));
+      var writing = await isolate.invokeRpcNoUpgrade(
+           '__getFileByID', { 'id' : result['data'][0]['id'] });
+
+      expect(writing['total_read'], equals(0));
+      expect(writing['read_count'], equals(0));
+      expect(writing['write_count'], equals(3));
+      expect(writing['total_written'], equals(3));
+
+      var reading = await isolate.invokeRpcNoUpgrade(
+           '__getFileByID', { 'id' : result['data'][1]['id'] });
+
+
+      expect(reading['total_read'], equals(5));
+      expect(reading['read_count'], equals(5));
+      expect(reading['write_count'], equals(0));
+      expect(reading['total_written'], equals(0));
+    } finally {
+      await isolate.invokeRpcNoUpgrade('__cleanup', {});
+    }
+  },
+];
+
+main(args) async => runIsolateTests(args, fileTests, testeeBefore:setupFiles);
diff --git a/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart b/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart
new file mode 100644
index 0000000..aef5571
--- /dev/null
+++ b/runtime/observatory/tests/service/tcp_socket_closing_service_test.dart
@@ -0,0 +1,74 @@
+// 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 'dart:convert';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+
+/// Test that we correctly remove sockets that have been closed from the list
+/// of open sockets. We explictly leave one socket open.
+
+Future setup() async {
+  var serverSocket = await io.ServerSocket.bind('127.0.0.1', 0);
+  serverSocket.listen((s) {
+    s.drain();
+    s.close();
+  });
+  var socket = await io.Socket.connect("127.0.0.1", serverSocket.port);
+  socket.write("foobar");
+  socket.write("foobar");
+
+  await socket.flush();
+  await socket.close();
+  await socket.drain();
+
+  var socket2 = await io.Socket.connect("127.0.0.1", serverSocket.port);
+  socket2.write("foobarfoobar");
+  await socket2.flush();
+  await socket2.close();
+  await socket2.drain();
+  await serverSocket.close();
+
+  var server = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+  server.listen((io.RawSocketEvent event) {
+    if(event == io.RawSocketEvent.READ) {
+      io.Datagram dg = server.receive();
+      dg.data.forEach((x) => true);
+      server.close();
+    }
+  });
+  var client = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+  client.send(UTF8.encoder.convert('foobar'),
+              new io.InternetAddress('127.0.0.1'), server.port);
+  client.close();
+
+  // The one socket to expect.
+  await io.ServerSocket.bind('127.0.0.1', 0);
+}
+
+var tests = [
+  // Initial.
+  (Isolate isolate) async {
+    var result = await isolate.invokeRpcNoUpgrade('__getOpenSockets', {});
+    expect(result['type'], equals('_opensockets'));
+    // We expect only one socket to be open, the server socket create at the
+    // end of test.
+    expect(result['data'].length, equals(1));
+    var server = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][0]['id'] });
+    expect(server['listening'], isTrue);
+    expect(server['last_read'], equals(0));
+    expect(server['total_read'], equals(0));
+    expect(server['last_write'], equals(0));
+    expect(server['total_written'], equals(0));
+    expect(server['write_count'], equals(0));
+    expect(server['read_count'], equals(0));
+  },
+];
+
+main(args) async => runIsolateTests(args, tests, testeeBefore:setup);
diff --git a/runtime/observatory/tests/service/tcp_socket_service_test.dart b/runtime/observatory/tests/service/tcp_socket_service_test.dart
new file mode 100644
index 0000000..0a85611
--- /dev/null
+++ b/runtime/observatory/tests/service/tcp_socket_service_test.dart
@@ -0,0 +1,175 @@
+// 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 'dart:convert';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+Future setupTCP() async {
+  // Note that we don't close after us, by design we leave the sockets opens
+  // to allow us to query them from the other isolate.
+  var serverSocket = await io.ServerSocket.bind('127.0.0.1', 0);
+  serverSocket.listen((s) {
+    s.transform(UTF8.decoder).listen(print);
+    s.close();
+  });
+  var socket = await io.Socket.connect("127.0.0.1", serverSocket.port);
+  socket.write("foobar");
+  socket.write("foobar");
+  await socket.flush();
+
+  var socket2 = await io.Socket.connect("127.0.0.1", serverSocket.port);
+  socket2.write("foobarfoobar");
+  await socket2.flush();
+}
+
+void expectTimeBiggerThanZero(time) {
+   // Stopwatch resolution on windows makes us sometimes report 0;
+  if (io.Platform.isWindows) {
+    expect(time, greaterThanOrEqualTo(0));
+  } else {
+    expect(time, greaterThan(0));
+  }
+}
+
+var tcpTests = [
+  // Initial.
+  (Isolate isolate) async {
+    var result = await isolate.invokeRpcNoUpgrade('__getOpenSockets', {});
+    expect(result['type'], equals('_opensockets'));
+    // We expect 3 sockets to be open (in this order):
+    //   The server socket accepting connections, on port X
+    //   The accepted connection on the client, on port Y
+    //   The client connection, on port X
+    expect(result['data'].length, equals(5));
+    // The first socket will have a name like listening:127.0.0.1:X
+    // The second will have a name like 127.0.0.1:Y
+    // The third will have a name like 127.0.0.1:X
+    expect(result['data'][0]['name'].startsWith('listening:127.0.0.1'), isTrue);
+    expect(result['data'][1]['name'].startsWith('127.0.0.1:'), isTrue);
+    expect(result['data'][2]['name'].startsWith('127.0.0.1:'), isTrue);
+
+    var listening = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][0]['id'] });
+    expect(listening['id'], equals(result['data'][0]['id']));
+    expect(listening['listening'], isTrue);
+    expect(listening['socket_type'], equals('TCP'));
+    expect(listening['port'], greaterThanOrEqualTo(1024));
+    expectTimeBiggerThanZero(listening['last_read']);
+
+    expect(listening['total_read'], equals(2));
+    expect(listening['last_write'], equals(0));
+    expect(listening['total_written'], equals(0));
+    expect(listening['write_count'], equals(0));
+    expect(listening['read_count'], equals(0));
+    expect(listening['remote_host'], equals('NA'));
+    expect(listening['remote_port'], equals('NA'));
+
+    var client = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][1]['id'] });
+    expect(client['id'], equals(result['data'][1]['id']));
+
+    var server = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][2]['id'] });
+    expect(server['id'], equals(result['data'][2]['id']));
+
+    // We expect the client to be connected on the port and
+    // host of the listening socket.
+    expect(client['remote_port'], equals(listening['port']));
+    expect(client['remote_host'], equals(listening['host']));
+    // We expect the third socket (accepted server) to be connected to the
+    // same port and host as the listening socket (the listening one).
+    expect(server['port'], equals(listening['port']));
+    expect(server['host'], equals(listening['host']));
+
+    expect(client['listening'], isFalse);
+    expect(server['listening'], isFalse);
+
+    expect(client['socket_type'], equals('TCP'));
+    expect(server['socket_type'], equals('TCP'));
+
+    // We are using no reserved ports.
+    expect(client['port'], greaterThanOrEqualTo(1024));
+    expect(server['port'], greaterThanOrEqualTo(1024));
+
+    // The client and server "mirror" each other in reads and writes, and the
+    // timestamps are in correct order.
+    expect(client['last_read'], equals(0));
+    expectTimeBiggerThanZero(server['last_read']);
+    expect(client['total_read'], equals(0));
+    expect(server['total_read'], equals(12));
+    expect(client['read_count'], equals(0));
+    expect(server['read_count'], greaterThanOrEqualTo(1));
+
+    expectTimeBiggerThanZero(client['last_write']);
+    expect(server['last_write'], equals(0));
+    expect(client['total_written'], equals(12));
+    expect(server['total_written'], equals(0));
+    expect(client['write_count'], greaterThanOrEqualTo(2));
+    expect(server['write_count'], equals(0));
+
+    // Order
+    // Stopwatch resolution on windows can make us have the same timestamp.
+    if (io.Platform.isWindows) {
+      expect(server['last_read'], greaterThanOrEqualTo(client['last_write']));
+    } else {
+      expect(server['last_read'], greaterThan(client['last_write']));
+    }
+
+    var second_client = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][3]['id'] });
+    expect(second_client['id'], equals(result['data'][3]['id']));
+    var second_server = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][4]['id'] });
+    expect(second_server['id'], equals(result['data'][4]['id']));
+
+    // We expect the client to be connected on the port and
+    // host of the listening socket.
+    expect(second_client['remote_port'], equals(listening['port']));
+    expect(second_client['remote_host'], equals(listening['host']));
+    // We expect the third socket (accepted server) to be connected to the
+    // same port and host as the listening socket (the listening one).
+    expect(second_server['port'], equals(listening['port']));
+    expect(second_server['host'], equals(listening['host']));
+
+    expect(second_client['listening'], isFalse);
+    expect(second_server['listening'], isFalse);
+
+    expect(second_client['socket_type'], equals('TCP'));
+    expect(second_server['socket_type'], equals('TCP'));
+
+    // We are using no reserved ports.
+    expect(second_client['port'], greaterThanOrEqualTo(1024));
+    expect(second_server['port'], greaterThanOrEqualTo(1024));
+
+    // The client and server "mirror" each other in reads and writes, and the
+    // timestamps are in correct order.
+    expect(second_client['last_read'], equals(0));
+    expectTimeBiggerThanZero(second_server['last_read']);
+    expect(second_client['total_read'], equals(0));
+    expect(second_server['total_read'], equals(12));
+    expect(second_client['read_count'], equals(0));
+    expect(second_server['read_count'], greaterThanOrEqualTo(1));
+
+    expectTimeBiggerThanZero(second_client['last_write']);
+    expect(second_server['last_write'], equals(0));
+    expect(second_client['total_written'], equals(12));
+    expect(second_server['total_written'], equals(0));
+    expect(second_client['write_count'], greaterThanOrEqualTo(1));
+    expect(second_server['write_count'], equals(0));
+
+    // Order
+    // Stopwatch resolution on windows make us sometimes report the same value.
+    if (io.Platform.isWindows) {
+      expect(server['last_read'], greaterThanOrEqualTo(client['last_write']));
+    } else {
+      expect(server['last_read'], greaterThan(client['last_write']));
+    }
+  },
+];
+
+main(args) async => runIsolateTests(args, tcpTests, testeeBefore:setupTCP);
diff --git a/runtime/observatory/tests/service/udp_socket_service_test.dart b/runtime/observatory/tests/service/udp_socket_service_test.dart
new file mode 100644
index 0000000..6b4b3b9
--- /dev/null
+++ b/runtime/observatory/tests/service/udp_socket_service_test.dart
@@ -0,0 +1,82 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+import 'dart:convert';
+import 'dart:io' as io;
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+
+Future setupUDP() async {
+  var server = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+  server.listen((io.RawSocketEvent event) {
+    if(event == io.RawSocketEvent.READ) {
+      io.Datagram dg = server.receive();
+      dg.data.forEach((x) => true);
+    }
+  });
+  var client = await io.RawDatagramSocket.bind('127.0.0.1', 0);
+  client.send(UTF8.encoder.convert('foobar'),
+              new io.InternetAddress('127.0.0.1'), server.port);
+}
+
+var udpTests = [
+  // Initial.
+  (Isolate isolate) async {
+    var result = await isolate.invokeRpcNoUpgrade('__getOpenSockets', {});
+    expect(result['type'], equals('_opensockets'));
+    // We expect 2 sockets to be open (in this order):
+    //   The server socket accepting connections, on port X
+    //   The client socket on port Y
+    expect(result['data'].length, equals(2));
+    // The first socket will have a name like listening:127.0.0.1:X
+    // The second will have a name like 127.0.0.1:Y
+    // The third will have a name like 127.0.0.1:X
+    expect(result['data'][0]['name'].startsWith('127.0.0.1'), isTrue);
+    expect(result['data'][1]['name'].startsWith('127.0.0.1:'), isTrue);
+
+    var server = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][0]['id'] });
+    expect(server['id'], equals(result['data'][0]['id']));
+    expect(server['remote_port'], equals('NA'));
+    expect(server['remote_host'], equals('NA'));
+    expect(server['listening'], isFalse);
+    expect(server['socket_type'], equals('UDP'));
+    expect(server['port'], greaterThanOrEqualTo(1024));
+    // Stopwatch resolution on windows makes us sometimes report 0;
+    if (io.Platform.isWindows) {
+      expect(server['last_read'], greaterThanOrEqualTo(0));
+    } else {
+      expect(server['last_read'], greaterThan(0));
+    }
+    expect(server['total_read'], equals(6));
+    expect(server['last_write'], equals(0));
+    expect(server['total_written'], equals(0));
+    expect(server['write_count'], equals(0));
+    expect(server['read_count'], greaterThanOrEqualTo(1));
+
+    var client = await isolate.invokeRpcNoUpgrade(
+        '__getSocketByID', { 'id' : result['data'][1]['id'] });
+    expect(client['id'], equals(result['data'][1]['id']));
+    expect(client['remote_port'], equals('NA'));
+    expect(client['remote_host'], equals('NA'));
+    expect(client['listening'], isFalse);
+    expect(client['socket_type'], equals('UDP'));
+    expect(client['port'], greaterThanOrEqualTo(1024));
+    expect(client['last_read'], equals(0));
+    expect(client['total_read'], equals(0));
+    // Stopwatch resolution on windows makes us sometimes report 0;
+    if (io.Platform.isWindows) {
+      expect(client['last_write'], greaterThanOrEqualTo(0));
+    } else {
+      expect(client['last_write'], greaterThan(0));
+    }
+    expect(client['total_written'], equals(6));
+    expect(client['write_count'], greaterThanOrEqualTo(1));
+    expect(client['read_count'], equals(0));
+  },
+];
+
+main(args) async => runIsolateTests(args, udpTests, testeeBefore:setupUDP);
diff --git a/runtime/vm/ast.cc b/runtime/vm/ast.cc
index 26618b5..48bd053 100644
--- a/runtime/vm/ast.cc
+++ b/runtime/vm/ast.cc
@@ -572,9 +572,11 @@
   if (is_super_getter()) {
     ASSERT(receiver() != NULL);
     const String& setter_name =
-        String::ZoneHandle(zone, Field::SetterSymbol(field_name_));
-    Function& setter = Function::ZoneHandle(zone,
-        Resolver::ResolveDynamicAnyArgs(cls(), setter_name));
+        String::ZoneHandle(zone, Field::LookupSetterSymbol(field_name_));
+    Function& setter = Function::ZoneHandle(zone);
+    if (!setter_name.IsNull()) {
+      setter = Resolver::ResolveDynamicAnyArgs(cls(), setter_name);
+    }
     if (setter.IsNull() || setter.is_abstract()) {
       // No instance setter found in super class chain,
       // noSuchMethod will be called at runtime.
@@ -618,15 +620,17 @@
     }
 
     // No field found in prefix. Look for a setter function.
-    const String& setter_name = String::Handle(zone,
-                                               Field::SetterName(field_name_));
-    obj = prefix.LookupObject(setter_name);
-    if (obj.IsFunction()) {
-      const Function& setter = Function::ZoneHandle(zone,
-                                                    Function::Cast(obj).raw());
-      ASSERT(setter.is_static() && setter.IsSetterFunction());
-      return new StaticSetterNode(
-          token_pos(), NULL, field_name_, setter, rhs);
+    const String& setter_name =
+         String::Handle(zone, Field::LookupSetterSymbol(field_name_));
+    if (!setter_name.IsNull()) {
+      obj = prefix.LookupObject(setter_name);
+      if (obj.IsFunction()) {
+        const Function& setter =
+            Function::ZoneHandle(zone, Function::Cast(obj).raw());
+        ASSERT(setter.is_static() && setter.IsSetterFunction());
+        return new StaticSetterNode(
+            token_pos(), NULL, field_name_, setter, rhs);
+      }
     }
 
     // No writeable field and no setter found in the prefix. Return a
@@ -651,14 +655,17 @@
     }
 
     // No field found in library. Look for a setter function.
-    const String& setter_name = String::Handle(zone,
-                                               Field::SetterName(field_name_));
-    obj = library.ResolveName(setter_name);
-    if (obj.IsFunction()) {
-      const Function& setter = Function::ZoneHandle(zone,
-                                                    Function::Cast(obj).raw());
-      ASSERT(setter.is_static() && setter.IsSetterFunction());
-      return new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs);
+    const String& setter_name =
+        String::Handle(zone, Field::LookupSetterSymbol(field_name_));
+    if (!setter_name.IsNull()) {
+      obj = library.ResolveName(setter_name);
+      if (obj.IsFunction()) {
+        const Function& setter =
+            Function::ZoneHandle(zone, Function::Cast(obj).raw());
+        ASSERT(setter.is_static() && setter.IsSetterFunction());
+        return
+            new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs);
+      }
     }
 
     // No writeable field and no setter found in the library. Return a
@@ -685,8 +692,9 @@
       return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs);
     }
 #if defined(DEBUG)
-    const String& getter_name = String::Handle(zone,
-                                               Field::GetterName(field_name_));
+    const String& getter_name =
+        String::Handle(zone, Field::LookupGetterSymbol(field_name_));
+    ASSERT(!getter_name.IsNull());
     const Function& getter =
         Function::Handle(zone, cls().LookupStaticFunction(getter_name));
     ASSERT(!getter.IsNull() &&
@@ -741,7 +749,10 @@
     return NULL;
   }
   const String& getter_name =
-      String::Handle(Field::GetterName(this->field_name()));
+      String::Handle(Field::LookupGetterSymbol(this->field_name()));
+  if (getter_name.IsNull()) {
+    return NULL;
+  }
   const Function& getter_func =
       Function::Handle(this->cls().LookupStaticFunction(getter_name));
   if (getter_func.IsNull() || !getter_func.is_const()) {
diff --git a/runtime/vm/ast.h b/runtime/vm/ast.h
index 7b4ae79..fa4ba21 100644
--- a/runtime/vm/ast.h
+++ b/runtime/vm/ast.h
@@ -1311,7 +1311,7 @@
   virtual const Instance* EvalConstExpr() const {
     ASSERT(field_.is_static());
     return !is_deferred_reference_ && field_.is_const()
-        ? &Instance::ZoneHandle(field_.value())
+        ? &Instance::ZoneHandle(field_.StaticValue())
         : NULL;
   }
 
diff --git a/runtime/vm/ast_transformer.cc b/runtime/vm/ast_transformer.cc
index 76faa89..b4583cf 100644
--- a/runtime/vm/ast_transformer.cc
+++ b/runtime/vm/ast_transformer.cc
@@ -62,7 +62,8 @@
 
 
 LocalVariable* AwaitTransformer::EnsureCurrentTempVar() {
-  String& symbol = String::ZoneHandle(Z, String::NewFormatted("%d", temp_cnt_));
+  String& symbol =
+      String::ZoneHandle(Z, Symbols::NewFormatted("%d", temp_cnt_));
   symbol = Symbols::FromConcat(Symbols::AwaitTempVarPrefix(), symbol);
   ASSERT(!symbol.IsNull());
   // Look up the variable in the scope used for async temp variables.
diff --git a/runtime/vm/class_finalizer.cc b/runtime/vm/class_finalizer.cc
index fc4dfad..494a42d 100644
--- a/runtime/vm/class_finalizer.cc
+++ b/runtime/vm/class_finalizer.cc
@@ -715,8 +715,8 @@
                       arguments.ToCString());
           }
           Error& error = Error::Handle();
-          super_type_arg =
-              super_type_arg.InstantiateFrom(arguments, &error, trail);
+          super_type_arg = super_type_arg.InstantiateFrom(
+              arguments, &error, trail, Heap::kOld);
           if (!error.IsNull()) {
             // InstantiateFrom does not report an error if the type is still
             // uninstantiated. Instead, it will return a new BoundedType so
@@ -818,7 +818,8 @@
       if (declared_bound.IsInstantiated()) {
         instantiated_bound = declared_bound.raw();
       } else {
-        instantiated_bound = declared_bound.InstantiateFrom(arguments, &error);
+        instantiated_bound =
+            declared_bound.InstantiateFrom(arguments, &error, NULL, Heap::kOld);
       }
       if (!instantiated_bound.IsFinalized()) {
         // The bound refers to type parameters, creating a cycle; postpone
@@ -1361,8 +1362,8 @@
       }
     }
     if (field.is_static() &&
-        (field.value() != Object::null()) &&
-        (field.value() != Object::sentinel().raw())) {
+        (field.StaticValue() != Object::null()) &&
+        (field.StaticValue() != Object::sentinel().raw())) {
       // The parser does not preset the value if the type is a type parameter or
       // is parameterized unless the value is null.
       Error& error = Error::Handle(Z);
@@ -1371,7 +1372,8 @@
       } else {
         ASSERT(type.IsInstantiated());
       }
-      const Instance& const_value = Instance::Handle(Z, field.value());
+      const Instance& const_value =
+          Instance::Handle(Z, field.StaticValue());
       if (!error.IsNull() ||
           (!type.IsDynamicType() &&
            !const_value.IsInstanceOf(type,
@@ -1413,7 +1415,7 @@
           getter.set_result_type(type);
           getter.set_is_debuggable(false);
           cls.AddFunction(getter);
-          field.set_value(Object::sentinel());
+          field.SetStaticValue(Object::sentinel(), true);
         }
       }
     }
@@ -2431,8 +2433,9 @@
   const Field& values_field =
       Field::Handle(enum_cls.LookupStaticField(Symbols::Values()));
   ASSERT(!values_field.IsNull());
-  ASSERT(Instance::Handle(values_field.value()).IsArray());
-  Array& values_list = Array::Handle(Array::RawCast(values_field.value()));
+  ASSERT(Instance::Handle(values_field.StaticValue()).IsArray());
+  Array& values_list = Array::Handle(
+      Array::RawCast(values_field.StaticValue()));
 
   const Array& fields = Array::Handle(enum_cls.fields());
   Field& field = Field::Handle();
@@ -2442,7 +2445,7 @@
   for (intptr_t i = 0; i < fields.Length(); i++) {
     field = Field::RawCast(fields.At(i));
     if (!field.is_static()) continue;
-    ordinal_value = field.value();
+    ordinal_value = field.StaticValue();
     // The static fields that need to be initialized with enum instances
     // contain the smi value of the ordinal number, which was stored in
     // the field by the parser. Other fields contain non-smi values.
@@ -2456,7 +2459,7 @@
       UNREACHABLE();
     }
     ASSERT(enum_value.IsCanonical());
-    field.set_value(enum_value);
+    field.SetStaticValue(enum_value, true);
     field.RecordStore(enum_value);
     intptr_t ord = Smi::Cast(ordinal_value).Value();
     ASSERT(ord < values_list.Length());
diff --git a/runtime/vm/compiler.cc b/runtime/vm/compiler.cc
index 8b96530..b02e904 100644
--- a/runtime/vm/compiler.cc
+++ b/runtime/vm/compiler.cc
@@ -724,26 +724,26 @@
         // FinalizeCode.
         const Array& deopt_info_array =
             Array::Handle(zone, graph_compiler.CreateDeoptInfo(&assembler));
-        INC_STAT(isolate, total_code_size,
+        INC_STAT(thread, total_code_size,
                  deopt_info_array.Length() * sizeof(uword));
         const Code& code = Code::Handle(
             Code::FinalizeCode(function, &assembler, optimized));
         code.set_is_optimized(optimized);
 
         const Array& intervals = graph_compiler.inlined_code_intervals();
-        INC_STAT(isolate, total_code_size,
+        INC_STAT(thread, total_code_size,
                  intervals.Length() * sizeof(uword));
         code.SetInlinedIntervals(intervals);
 
         const Array& inlined_id_array =
             Array::Handle(zone, graph_compiler.InliningIdToFunction());
-        INC_STAT(isolate, total_code_size,
+        INC_STAT(thread, total_code_size,
                  inlined_id_array.Length() * sizeof(uword));
         code.SetInlinedIdToFunction(inlined_id_array);
 
         const Array& caller_inlining_id_map_array =
             Array::Handle(zone, graph_compiler.CallerInliningIdMap());
-        INC_STAT(isolate, total_code_size,
+        INC_STAT(thread, total_code_size,
                  caller_inlining_id_map_array.Length() * sizeof(uword));
         code.SetInlinedCallerIdMap(caller_inlining_id_map_array);
 
@@ -1046,9 +1046,18 @@
                 function.token_pos(),
                 (function.end_token_pos() - function.token_pos()));
     }
+    INC_STAT(thread, num_functions_compiled, 1);
+    if (optimized) {
+      INC_STAT(thread, num_functions_optimized, 1);
+    }
     {
       HANDLESCOPE(thread);
+      const int64_t num_tokens_before = STAT_VALUE(thread, num_tokens_consumed);
       pipeline->ParseFunction(parsed_function);
+      const int64_t num_tokens_after = STAT_VALUE(thread, num_tokens_consumed);
+      INC_STAT(thread,
+               num_func_tokens_compiled,
+               num_tokens_after - num_tokens_before);
     }
 
     const bool success = CompileParsedFunctionHelper(pipeline,
@@ -1277,23 +1286,15 @@
 }
 
 
-static void CreateLocalVarDescriptors(const ParsedFunction& parsed_function) {
-  const Function& func = parsed_function.function();
-  LocalVarDescriptors& var_descs = LocalVarDescriptors::Handle();
-  var_descs = parsed_function.node_sequence()->scope()->GetVarDescriptors(func);
-  Code::Handle(func.unoptimized_code()).set_var_descriptors(var_descs);
-}
-
-
 void Compiler::CompileStaticInitializer(const Field& field) {
   ASSERT(field.is_static());
-  if (field.initializer() != Function::null()) {
+  if (field.PrecompiledInitializer() != Function::null()) {
     // TODO(rmacnak): Investigate why this happens for _enum_names.
     OS::Print("Warning: Ignoring repeated request for initializer for %s\n",
               field.ToCString());
     return;
   }
-  ASSERT(field.initializer() == Function::null());
+  ASSERT(field.PrecompiledInitializer() == Function::null());
   Thread* thread = Thread::Current();
   StackZone zone(thread);
 
@@ -1308,7 +1309,7 @@
                               Isolate::kNoDeoptId);
 
   const Function& initializer = parsed_function->function();
-  field.set_initializer(initializer);
+  field.SetPrecompiledInitializer(initializer);
 }
 
 
@@ -1316,16 +1317,15 @@
   ASSERT(field.is_static());
   // The VM sets the field's value to transiton_sentinel prior to
   // evaluating the initializer value.
-  ASSERT(field.value() == Object::transition_sentinel().raw());
+  ASSERT(field.StaticValue() == Object::transition_sentinel().raw());
   LongJumpScope jump;
   if (setjmp(*jump.Set()) == 0) {
-    Function& initializer = Function::Handle(field.initializer());
-
     // Under precompilation, the initializer may have already been compiled, in
     // which case use it. Under lazy compilation or early in precompilation, the
     // initializer has not yet been created, so create it now, but don't bother
     // remembering it because it won't be used again.
-    if (initializer.IsNull()) {
+    Function& initializer = Function::Handle();
+    if (!field.HasPrecompiledInitializer()) {
       Thread* const thread = Thread::Current();
       StackZone zone(thread);
       ParsedFunction* parsed_function =
@@ -1338,15 +1338,12 @@
                                   parsed_function,
                                   false,  // optimized
                                   Isolate::kNoDeoptId);
-      // Eagerly create local var descriptors.
-      CreateLocalVarDescriptors(*parsed_function);
-
       initializer = parsed_function->function().raw();
+    } else {
+      initializer ^= field.PrecompiledInitializer();
     }
     // Invoke the function to evaluate the expression.
-    const Object& result = PassiveObject::Handle(
-        DartEntry::InvokeFunction(initializer, Object::empty_array()));
-    return result.raw();
+    return DartEntry::InvokeFunction(initializer, Object::empty_array());
   } else {
     Thread* const thread = Thread::Current();
     Isolate* const isolate = thread->isolate();
@@ -1410,8 +1407,6 @@
                                 false,
                                 Isolate::kNoDeoptId);
 
-    // Eagerly create local var descriptors.
-    CreateLocalVarDescriptors(*parsed_function);
     const Object& result = PassiveObject::Handle(
         DartEntry::InvokeFunction(func, Object::empty_array()));
     return result.raw();
diff --git a/runtime/vm/compiler_stats.cc b/runtime/vm/compiler_stats.cc
index 59d1dd4..e04c0c0 100644
--- a/runtime/vm/compiler_stats.cc
+++ b/runtime/vm/compiler_stats.cc
@@ -5,6 +5,7 @@
 #include "vm/compiler_stats.h"
 
 #include "vm/flags.h"
+#include "vm/log.h"
 #include "vm/object_graph.h"
 #include "vm/timer.h"
 
@@ -14,16 +15,17 @@
 DEFINE_FLAG(bool, compiler_stats, false, "Compiler stat counters.");
 
 
-class TokenStreamVisitor : public ObjectGraph::Visitor {
+class TokenStreamVisitor : public ObjectVisitor {
  public:
-  explicit TokenStreamVisitor(CompilerStats* compiler_stats)
-      : obj_(Object::Handle()), stats_(compiler_stats) {
+  TokenStreamVisitor(Isolate* isolate, CompilerStats* compiler_stats)
+      : ObjectVisitor(isolate),
+        obj_(Object::Handle()),
+        stats_(compiler_stats) {
   }
 
-  virtual Direction VisitObject(ObjectGraph::StackIterator* it) {
-    RawObject* raw_obj = it->Get();
+  void VisitObject(RawObject* raw_obj) {
     if (raw_obj->IsFreeListElement()) {
-      return kProceed;
+      return;
     }
     obj_ = raw_obj;
     if (obj_.GetClassId() == TokenStream::kClassId) {
@@ -42,7 +44,6 @@
         kind = tkit.CurrentTokenKind();
       }
     }
-    return kProceed;
   }
 
  private:
@@ -71,24 +72,46 @@
       num_literal_tokens_total(0),
       num_ident_tokens_total(0),
       num_tokens_consumed(0),
-      num_token_checks(0),
-      num_tokens_lookahead(0),
       num_cached_consts(0),
       num_const_cache_hits(0),
-      num_classes_compiled(0),
+      num_classes_parsed(0),
+      num_class_tokens(0),
+      num_functions_parsed(0),
       num_functions_compiled(0),
+      num_functions_optimized(0),
+      num_func_tokens_compiled(0),
       num_implicit_final_getters(0),
+      num_method_extractors(0),
       src_length(0),
       total_code_size(0),
       total_instr_size(0),
       pc_desc_size(0),
-      vardesc_size(0) {
+      vardesc_size(0),
+      text(NULL) {
 }
 
 
-void CompilerStats::Print() {
+// This function is used as a callback in the log object to which the
+// compiler stats are printed. It will be called only once, to print
+// the accumulated text when all of the compiler stats values are
+// added to the log.
+static void PrintToStats(const char* format, ...) PRINTF_ATTRIBUTE(1, 2);
+static void PrintToStats(const char* format, ...) {
+  Thread* thread = Thread::Current();
+  Isolate* isolate = thread->isolate();
+  CompilerStats* stats = isolate->compiler_stats();
+  Zone* zone = thread->zone();
+  ASSERT(stats != NULL);
+  va_list args;
+  va_start(args, format);
+  stats->text = zone->VPrint(format, args);
+  va_end(args);
+}
+
+
+char* CompilerStats::PrintToZone() {
   if (!FLAG_compiler_stats) {
-    return;
+    return NULL;
   }
 
   // Traverse the heap and compute number of tokens in all
@@ -96,85 +119,97 @@
   num_tokens_total = 0;
   num_literal_tokens_total = 0;
   num_ident_tokens_total = 0;
-  TokenStreamVisitor visitor(this);
-  ObjectGraph graph(isolate_);
-  graph.IterateObjects(&visitor);
+  TokenStreamVisitor visitor(isolate_, this);
+  isolate_->heap()->IterateObjects(&visitor);
+  Dart::vm_isolate()->heap()->IterateObjects(&visitor);
 
-  OS::Print("==== Compiler Stats for isolate '%s' ====\n",
+  Log log(PrintToStats);
+  LogBlock lb(isolate_, &log);
+
+  log.Print("==== Compiler Stats for isolate '%s' ====\n",
             isolate_->debugger_name());
-  OS::Print("Number of tokens:   %" Pd64 "\n", num_tokens_total);
-  OS::Print("  Literal tokens:   %" Pd64 "\n", num_literal_tokens_total);
-  OS::Print("  Ident tokens:     %" Pd64 "\n", num_ident_tokens_total);
-  OS::Print("Tokens consumed:    %" Pd64 " (%.2f times number of tokens)\n",
-            num_tokens_consumed,
-            (1.0 * num_tokens_consumed) / num_tokens_total);
-  OS::Print("Tokens checked:     %" Pd64 "  (%.2f times tokens consumed)\n",
-            num_token_checks, (1.0 * num_token_checks) / num_tokens_consumed);
-  OS::Print("Token lookahead:    %" Pd64 " (%" Pd64 "%% of tokens checked)\n",
-            num_tokens_lookahead,
-            (100 * num_tokens_lookahead) / num_token_checks);
-  OS::Print("Consts cached:      %" Pd64 "\n", num_cached_consts);
-  OS::Print("Consts cache hits:  %" Pd64 "\n", num_const_cache_hits);
+  log.Print("Number of tokens:        %" Pd64 "\n", num_tokens_total);
+  log.Print("  Literal tokens:        %" Pd64 "\n", num_literal_tokens_total);
+  log.Print("  Ident tokens:          %" Pd64 "\n", num_ident_tokens_total);
+  log.Print("Source length:           %" Pd64 " characters\n", src_length);
 
-  OS::Print("Classes parsed:     %" Pd64 "\n", num_classes_compiled);
-  OS::Print("Functions compiled: %" Pd64 "\n", num_functions_compiled);
-  OS::Print("  Impl getters:     %" Pd64 "\n", num_implicit_final_getters);
+  log.Print("==== Parser stats:\n");
+  log.Print("Total tokens consumed:   %" Pd64 "\n", num_tokens_consumed);
+  log.Print("Classes parsed:          %" Pd64 "\n", num_classes_parsed);
+  log.Print("  Tokens consumed:       %" Pd64 "\n", num_class_tokens);
+  log.Print("Functions parsed:        %" Pd64 "\n", num_functions_parsed);
+  log.Print("  Tokens consumed:       %" Pd64 "\n", num_func_tokens_compiled);
+  log.Print("Impl getter funcs:       %" Pd64 "\n", num_implicit_final_getters);
+  log.Print("Impl method extractors:  %" Pd64 "\n", num_method_extractors);
+  log.Print("Consts cached:           %" Pd64 "\n", num_cached_consts);
+  log.Print("Consts cache hits:       %" Pd64 "\n", num_const_cache_hits);
 
-  OS::Print("Source length:      %" Pd64 " characters\n", src_length);
   int64_t scan_usecs = scanner_timer.TotalElapsedTime();
-  OS::Print("Scanner time:       %" Pd64 " msecs\n",
-            scan_usecs / 1000);
+  log.Print("Scanner time:            %" Pd64 " msecs\n", scan_usecs / 1000);
   int64_t parse_usecs = parser_timer.TotalElapsedTime();
-  OS::Print("Parser time:        %" Pd64 " msecs\n",
-            parse_usecs / 1000);
+  log.Print("Parser time:             %" Pd64 " msecs\n", parse_usecs / 1000);
+  log.Print("Parser speed:            %" Pd64 " tokens per msec\n",
+            1000 * num_tokens_consumed / parse_usecs);
   int64_t codegen_usecs = codegen_timer.TotalElapsedTime();
-  OS::Print("Code gen. time:     %" Pd64 " msecs\n",
+
+  log.Print("==== Backend stats:\n");
+  log.Print("Code gen. time:          %" Pd64 " msecs\n",
             codegen_usecs / 1000);
   int64_t graphbuilder_usecs = graphbuilder_timer.TotalElapsedTime();
-  OS::Print("  Graph builder:    %" Pd64 " msecs\n", graphbuilder_usecs / 1000);
+  log.Print("  Graph builder:         %" Pd64 " msecs\n",
+            graphbuilder_usecs / 1000);
   int64_t ssa_usecs = ssa_timer.TotalElapsedTime();
-  OS::Print("  Graph SSA:        %" Pd64 " msecs\n", ssa_usecs / 1000);
+  log.Print("  Graph SSA:             %" Pd64 " msecs\n", ssa_usecs / 1000);
 
   int64_t graphinliner_usecs = graphinliner_timer.TotalElapsedTime();
-  OS::Print("  Graph inliner:    %" Pd64 " msecs\n", graphinliner_usecs / 1000);
+  log.Print("  Graph inliner:         %" Pd64 " msecs\n",
+            graphinliner_usecs / 1000);
   int64_t graphinliner_parse_usecs =
       graphinliner_parse_timer.TotalElapsedTime();
-  OS::Print("    Parsing:        %" Pd64 " msecs\n",
+  log.Print("    Parsing:             %" Pd64 " msecs\n",
             graphinliner_parse_usecs / 1000);
   int64_t graphinliner_build_usecs =
       graphinliner_build_timer.TotalElapsedTime();
-  OS::Print("    Building:       %" Pd64 " msecs\n",
+  log.Print("    Building:            %" Pd64 " msecs\n",
             graphinliner_build_usecs / 1000);
   int64_t graphinliner_ssa_usecs = graphinliner_ssa_timer.TotalElapsedTime();
-  OS::Print("    SSA:            %" Pd64 " msecs\n",
+  log.Print("    SSA:                 %" Pd64 " msecs\n",
             graphinliner_ssa_usecs / 1000);
   int64_t graphinliner_opt_usecs = graphinliner_opt_timer.TotalElapsedTime();
-  OS::Print("    Optimization:   %" Pd64 " msecs\n",
+  log.Print("    Optimization:        %" Pd64 " msecs\n",
             graphinliner_opt_usecs / 1000);
   int64_t graphinliner_subst_usecs =
       graphinliner_subst_timer.TotalElapsedTime();
-  OS::Print("    Substitution:   %" Pd64 " msecs\n",
+  log.Print("    Substitution:        %" Pd64 " msecs\n",
             graphinliner_subst_usecs / 1000);
-
   int64_t graphoptimizer_usecs = graphoptimizer_timer.TotalElapsedTime();
-  OS::Print("  Graph optimizer:  %" Pd64 " msecs\n",
+  log.Print("  Graph optimizer:       %" Pd64 " msecs\n",
             (graphoptimizer_usecs - graphinliner_usecs) / 1000);
   int64_t graphcompiler_usecs = graphcompiler_timer.TotalElapsedTime();
-  OS::Print("  Graph compiler:   %" Pd64 " msecs\n",
+  log.Print("  Graph compiler:        %" Pd64 " msecs\n",
             graphcompiler_usecs / 1000);
   int64_t codefinalizer_usecs = codefinalizer_timer.TotalElapsedTime();
-  OS::Print("  Code finalizer:   %" Pd64 " msecs\n",
+  log.Print("  Code finalizer:        %" Pd64 " msecs\n",
             codefinalizer_usecs / 1000);
-  OS::Print("Compilation speed:  %" Pd64 " tokens per msec\n",
-            (1000 * num_tokens_total) / (parse_usecs + codegen_usecs));
-  OS::Print("Code density:       %" Pd64 " tokens per KB\n",
-            (num_tokens_total * 1024) / total_instr_size);
-  OS::Print("Instr size:         %" Pd64 " KB\n",
-            total_instr_size / 1024);
-  OS::Print("Pc Desc size:       %" Pd64 " KB\n", pc_desc_size / 1024);
-  OS::Print("VarDesc size:       %" Pd64 " KB\n", vardesc_size / 1024);
 
-  OS::Print("Code size:          %" Pd64 " KB\n", total_code_size / 1024);
+  log.Print("==== Compiled code stats:\n");
+  log.Print("Functions parsed:        %" Pd64 "\n", num_functions_parsed);
+  log.Print("Functions compiled:      %" Pd64 "\n", num_functions_compiled);
+  log.Print("  optimized:             %" Pd64 "\n", num_functions_optimized);
+  log.Print("Tokens compiled:         %" Pd64 "\n", num_func_tokens_compiled);
+  log.Print("Compilation speed:       %" Pd64 " tokens per msec\n",
+            (1000 * num_func_tokens_compiled) / (parse_usecs + codegen_usecs));
+  log.Print("Code density:            %" Pd64 " tokens per KB\n",
+            (num_func_tokens_compiled * 1024) / total_instr_size);
+  log.Print("Code size:               %" Pd64 " KB\n", total_code_size / 1024);
+  log.Print("  Instr size:            %" Pd64 " KB\n",
+            total_instr_size / 1024);
+  log.Print("  Pc Desc size:          %" Pd64 " KB\n", pc_desc_size / 1024);
+  log.Print("  VarDesc size:          %" Pd64 " KB\n", vardesc_size / 1024);
+  log.Flush();
+  char* stats_text = text;
+  text = NULL;
+  return stats_text;
 }
 
 }  // namespace dart
diff --git a/runtime/vm/compiler_stats.h b/runtime/vm/compiler_stats.h
index e6d4162..f0b47f4 100644
--- a/runtime/vm/compiler_stats.h
+++ b/runtime/vm/compiler_stats.h
@@ -11,13 +11,10 @@
 #include "vm/timer.h"
 
 
-
 namespace dart {
 
-DECLARE_FLAG(bool, compiler_stats);
 
-// TODO(hausner): Might want to expose some of these values in the
-// observatory. Use the metrics mechanism (metrics.h) for this.
+DECLARE_FLAG(bool, compiler_stats);
 
 class CompilerStats {
  public:
@@ -44,30 +41,40 @@
   Timer graphcompiler_timer;   // Included in codegen_timer.
   Timer codefinalizer_timer;   // Included in codegen_timer.
 
-  int64_t num_tokens_total;
+  int64_t num_tokens_total;    // Isolate + VM isolate
   int64_t num_literal_tokens_total;
   int64_t num_ident_tokens_total;
   int64_t num_tokens_consumed;
-  int64_t num_token_checks;
-  int64_t num_tokens_lookahead;
   int64_t num_cached_consts;
   int64_t num_const_cache_hits;
 
-  int64_t num_classes_compiled;
-  int64_t num_functions_compiled;
+  int64_t num_classes_parsed;
+  int64_t num_class_tokens;
+  int64_t num_functions_parsed;      // Num parsed functions.
+  int64_t num_functions_compiled;    // Num unoptimized compilations.
+  int64_t num_functions_optimized;   // Num optimized compilations.
+  int64_t num_func_tokens_compiled;
   int64_t num_implicit_final_getters;
+  int64_t num_method_extractors;
 
   int64_t src_length;          // Total number of characters in source.
   int64_t total_code_size;     // Bytes allocated for code and meta info.
   int64_t total_instr_size;    // Total size of generated code in bytes.
   int64_t pc_desc_size;
   int64_t vardesc_size;
+  char* text;
 
-  void Print();
+  char* PrintToZone();
 };
 
-#define INC_STAT(isolate, counter, incr)                                       \
-  if (FLAG_compiler_stats) { (isolate)->compiler_stats()->counter += (incr); }
+// TODO(hausner): make the increment thread-safe.
+#define INC_STAT(thread, counter, incr)                                        \
+  if (FLAG_compiler_stats) {                                                   \
+      (thread)->isolate()->compiler_stats()->counter += (incr); }
+
+#define STAT_VALUE(thread, counter)                                            \
+  ((FLAG_compiler_stats != false) ?                                            \
+      (thread)->isolate()->compiler_stats()->counter : 0)
 
 #define CSTAT_TIMER_SCOPE(thr, t)                                              \
   TimerScope timer(FLAG_compiler_stats,                                        \
diff --git a/runtime/vm/constant_propagator.cc b/runtime/vm/constant_propagator.cc
index 29818e1..5cf2018 100644
--- a/runtime/vm/constant_propagator.cc
+++ b/runtime/vm/constant_propagator.cc
@@ -706,7 +706,7 @@
 void ConstantPropagator::VisitLoadStaticField(LoadStaticFieldInstr* instr) {
   const Field& field = instr->StaticField();
   ASSERT(field.is_static());
-  Instance& obj = Instance::Handle(I, field.value());
+  Instance& obj = Instance::Handle(I, field.StaticValue());
   if (field.is_final() && (obj.raw() != Object::sentinel().raw()) &&
       (obj.raw() != Object::transition_sentinel().raw())) {
     if (obj.IsSmi() || obj.IsOld()) {
diff --git a/runtime/vm/coverage.cc b/runtime/vm/coverage.cc
index 7892e37..d18ce19 100644
--- a/runtime/vm/coverage.cc
+++ b/runtime/vm/coverage.cc
@@ -103,32 +103,33 @@
       if ((token_pos < begin_pos) || (token_pos > end_pos)) {
         continue;
       }
-      intptr_t line = pos_to_line[token_pos];
-#if defined(DEBUG)
-      const Script& script = Script::Handle(zone, function.script());
-      intptr_t test_line = -1;
-      script.GetTokenLocation(token_pos, &test_line, NULL);
-      ASSERT(test_line == line);
-#endif
-      // Merge hit data where possible.
-      if (last_line == line) {
-        last_count += ic_data->AggregateCount();
-      } else {
-        if ((last_line != -1) && !as_call_sites) {
-          hits_or_sites.AddValue(last_line);
-          hits_or_sites.AddValue(last_count);
-        }
-        last_count = ic_data->AggregateCount();
-        last_line = line;
-      }
       if (as_call_sites) {
         bool is_static_call = iter.Kind() == RawPcDescriptors::kUnoptStaticCall;
         ic_data->PrintToJSONArray(hits_or_sites, token_pos, is_static_call);
+      } else {
+        intptr_t line = pos_to_line[token_pos];
+#if defined(DEBUG)
+        const Script& script = Script::Handle(zone, function.script());
+        intptr_t test_line = -1;
+        script.GetTokenLocation(token_pos, &test_line, NULL);
+        ASSERT(test_line == line);
+#endif
+        // Merge hit data where possible.
+        if (last_line == line) {
+          last_count += ic_data->AggregateCount();
+        } else {
+          if ((last_line != -1)) {
+            hits_or_sites.AddValue(last_line);
+            hits_or_sites.AddValue(last_count);
+          }
+          last_count = ic_data->AggregateCount();
+          last_line = line;
+        }
       }
     }
   }
   // Write last hit value if needed.
-  if ((last_line != -1) && !as_call_sites) {
+  if (!as_call_sites && (last_line != -1)) {
     hits_or_sites.AddValue(last_line);
     hits_or_sites.AddValue(last_count);
   }
@@ -164,7 +165,9 @@
       i++;
       continue;
     }
-    ComputeTokenPosToLineNumberMap(script, &pos_to_line);
+    if (!as_call_sites) {
+      ComputeTokenPosToLineNumberMap(script, &pos_to_line);
+    }
     JSONObject jsobj(&jsarr);
     jsobj.AddProperty("source", saved_url.ToCString());
     jsobj.AddProperty("script", script);
@@ -185,10 +188,6 @@
         continue;
       }
       CompileAndAdd(function, hits_or_sites, pos_to_line, as_call_sites);
-      if (function.HasImplicitClosureFunction()) {
-        function = function.ImplicitClosureFunction();
-        CompileAndAdd(function, hits_or_sites, pos_to_line, as_call_sites);
-      }
       i++;
     }
   }
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index dcda02a..f3f1f52 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -4256,7 +4256,7 @@
       return Api::NewHandle(I,
           DartEntry::InvokeFunction(getter, Object::empty_array()));
     } else if (!field.IsNull()) {
-      return Api::NewHandle(I, field.value());
+      return Api::NewHandle(I, field.StaticValue());
     } else {
       return Api::NewError("%s: did not find static field '%s'.",
                            CURRENT_FUNC, field_name.ToCString());
@@ -4323,7 +4323,7 @@
           DartEntry::InvokeFunction(getter, Object::empty_array()));
     }
     if (!field.IsNull()) {
-      return Api::NewHandle(I, field.value());
+      return Api::NewHandle(I, field.StaticValue());
     }
     return Api::NewError("%s: did not find top-level variable '%s'.",
                          CURRENT_FUNC, field_name.ToCString());
@@ -4397,7 +4397,7 @@
         return Api::NewError("%s: cannot set final field '%s'.",
                              CURRENT_FUNC, field_name.ToCString());
       } else {
-        field.set_value(value_instance);
+        field.SetStaticValue(value_instance);
         return Api::Success();
       }
     } else {
@@ -4475,7 +4475,7 @@
         return Api::NewError("%s: cannot set final top-level variable '%s'.",
                              CURRENT_FUNC, field_name.ToCString());
       }
-      field.set_value(value_instance);
+      field.SetStaticValue(value_instance);
       return Api::Success();
     }
     return Api::NewError("%s: did not find top-level variable '%s'.",
@@ -5503,7 +5503,7 @@
   const Field& dirty_bit = Field::Handle(Z,
       libmirrors.LookupLocalField(String::Handle(String::New("dirty"))));
   ASSERT(!dirty_bit.IsNull() && dirty_bit.is_static());
-  dirty_bit.set_value(Bool::True());
+  dirty_bit.SetStaticValue(Bool::True());
 
   if (complete_futures) {
     const Library& corelib = Library::Handle(Z, Library::CoreLibrary());
diff --git a/runtime/vm/datastream.h b/runtime/vm/datastream.h
index bb46e4f..f47e822 100644
--- a/runtime/vm/datastream.h
+++ b/runtime/vm/datastream.h
@@ -10,6 +10,7 @@
 #include "vm/allocation.h"
 #include "vm/exceptions.h"
 #include "vm/globals.h"
+#include "vm/os.h"
 
 namespace dart {
 
@@ -386,6 +387,12 @@
     current_ += len;
   }
 
+  void Print(const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    VPrint(format, args);
+  }
+
  private:
   template<typename T>
   void Write(T value) {
@@ -426,6 +433,28 @@
     ASSERT(end_ > *buffer_);
   }
 
+  void VPrint(const char* format, va_list args) {
+    // Measure.
+    va_list measure_args;
+    va_copy(measure_args, args);
+    intptr_t len = OS::VSNPrint(NULL, 0, format, measure_args);
+    va_end(measure_args);
+
+    // Alloc.
+    if ((end_ - current_) < (len + 1)) {
+      Resize(len + 1);
+    }
+    ASSERT((end_ - current_) >= (len + 1));
+
+    // Print.
+    va_list print_args;
+    va_copy(print_args, args);
+    OS::VSNPrint(reinterpret_cast<char*>(current_),
+                 len + 1, format, print_args);
+    va_end(print_args);
+    current_ += len;  // Not len + 1 to swallow the terminating NUL.
+  }
+
  private:
   uint8_t** const buffer_;
   uint8_t* end_;
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index 0cd16d8..96966d9 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -2138,7 +2138,7 @@
   const Field& fld = Field::Handle(cls.LookupStaticField(field_name));
   if (!fld.IsNull()) {
     // Return the value in the field if it has been initialized already.
-    const Instance& value = Instance::Handle(fld.value());
+    const Instance& value = Instance::Handle(fld.StaticValue());
     ASSERT(value.raw() != Object::transition_sentinel().raw());
     if (value.raw() != Object::sentinel().raw()) {
       return value.raw();
@@ -2235,11 +2235,11 @@
       // If the field is not initialized yet, report the value to be
       // "<not initialized>". We don't want to execute the implicit getter
       // since it may have side effects.
-      if ((field.value() == Object::sentinel().raw()) ||
-          (field.value() == Object::transition_sentinel().raw())) {
+      if ((field.StaticValue() == Object::sentinel().raw()) ||
+          (field.StaticValue() == Object::transition_sentinel().raw())) {
         field_value = Symbols::NotInitialized().raw();
       } else {
-        field_value = field.value();
+        field_value = field.StaticValue();
       }
       if (!prefix.IsNull()) {
         field_name = String::Concat(prefix, field_name);
diff --git a/runtime/vm/flow_graph_builder.cc b/runtime/vm/flow_graph_builder.cc
index 371ec0e..d9188c7 100644
--- a/runtime/vm/flow_graph_builder.cc
+++ b/runtime/vm/flow_graph_builder.cc
@@ -3804,10 +3804,11 @@
 
 void EffectGraphVisitor::VisitLoadStaticFieldNode(LoadStaticFieldNode* node) {
   if (node->field().is_const()) {
-    ASSERT(node->field().value() != Object::sentinel().raw());
-    ASSERT(node->field().value() != Object::transition_sentinel().raw());
-    Definition* result =
-        new(Z) ConstantInstr(Instance::ZoneHandle(Z, node->field().value()));
+    ASSERT(node->field().StaticValue() != Object::sentinel().raw());
+    ASSERT(node->field().StaticValue() !=
+           Object::transition_sentinel().raw());
+    Definition* result = new(Z) ConstantInstr(
+        Instance::ZoneHandle(Z, node->field().StaticValue()));
     return ReturnDefinition(result);
   }
   Value* field_value = Bind(new(Z) ConstantInstr(node->field()));
diff --git a/runtime/vm/flow_graph_compiler.cc b/runtime/vm/flow_graph_compiler.cc
index 4a41786..c1d94b4 100644
--- a/runtime/vm/flow_graph_compiler.cc
+++ b/runtime/vm/flow_graph_compiler.cc
@@ -902,9 +902,12 @@
   const ExceptionHandlers& handlers = ExceptionHandlers::Handle(
       exception_handlers_list_->FinalizeExceptionHandlers(code.EntryPoint()));
   code.set_exception_handlers(handlers);
-  INC_STAT(isolate(), total_code_size,
-      ExceptionHandlers::InstanceSize(handlers.num_entries()));
-  INC_STAT(isolate(), total_code_size, handlers.num_entries() * sizeof(uword));
+  if (FLAG_compiler_stats) {
+    Thread* thread = Thread::Current();
+    INC_STAT(thread, total_code_size,
+        ExceptionHandlers::InstanceSize(handlers.num_entries()));
+    INC_STAT(thread, total_code_size, handlers.num_entries() * sizeof(uword));
+  }
 }
 
 
@@ -1012,7 +1015,9 @@
     }
   }
   code.set_static_calls_target_table(targets);
-  INC_STAT(isolate(), total_code_size, targets.Length() * sizeof(uword));
+  INC_STAT(Thread::Current(),
+           total_code_size,
+           targets.Length() * sizeof(uword));
 }
 
 
diff --git a/runtime/vm/flow_graph_type_propagator.cc b/runtime/vm/flow_graph_type_propagator.cc
index 6af05d6..c80005d 100644
--- a/runtime/vm/flow_graph_type_propagator.cc
+++ b/runtime/vm/flow_graph_type_propagator.cc
@@ -462,9 +462,10 @@
 
   const AbstractType* compile_type = ToAbstractType();
   const AbstractType* other_compile_type = other->ToAbstractType();
-  if (compile_type->IsMoreSpecificThan(*other_compile_type, NULL)) {
+  if (compile_type->IsMoreSpecificThan(*other_compile_type, NULL, Heap::kOld)) {
     type_ = other_compile_type;
-  } else if (other_compile_type->IsMoreSpecificThan(*compile_type, NULL)) {
+  } else if (other_compile_type->
+      IsMoreSpecificThan(*compile_type, NULL, Heap::kOld)) {
   // Nothing to do.
   } else {
   // Can't unify.
@@ -659,7 +660,7 @@
     return false;
   }
 
-  *is_instance = compile_type.IsMoreSpecificThan(type, NULL);
+  *is_instance = compile_type.IsMoreSpecificThan(type, NULL, Heap::kOld);
   return *is_instance;
 }
 
@@ -674,7 +675,7 @@
     return IsNull();
   }
 
-  return ToAbstractType()->IsMoreSpecificThan(other, NULL);
+  return ToAbstractType()->IsMoreSpecificThan(other, NULL, Heap::kOld);
 }
 
 
@@ -753,6 +754,8 @@
     // Set parameter types here in order to prevent unnecessary CheckClassInstr
     // from being generated.
     switch (index()) {
+      case RegExpMacroAssembler::kParamRegExpIndex:
+        return CompileType::FromCid(kJSRegExpCid);
       case RegExpMacroAssembler::kParamStringIndex:
         return CompileType::FromCid(function.string_specialization_cid());
       case RegExpMacroAssembler::kParamStartOffsetIndex:
@@ -996,7 +999,7 @@
   }
   ASSERT(field.is_static());
   if (field.is_final()) {
-    const Instance& obj = Instance::Handle(field.value());
+    const Instance& obj = Instance::Handle(field.StaticValue());
     if ((obj.raw() != Object::sentinel().raw()) &&
         (obj.raw() != Object::transition_sentinel().raw()) &&
         !obj.IsNull()) {
diff --git a/runtime/vm/intermediate_language.cc b/runtime/vm/intermediate_language.cc
index 7f4a862..8754ecf 100644
--- a/runtime/vm/intermediate_language.cc
+++ b/runtime/vm/intermediate_language.cc
@@ -383,8 +383,8 @@
 
 Instruction* InitStaticFieldInstr::Canonicalize(FlowGraph* flow_graph) {
   const bool is_initialized =
-      (field_.value() != Object::sentinel().raw()) &&
-      (field_.value() != Object::transition_sentinel().raw());
+      (field_.StaticValue() != Object::sentinel().raw()) &&
+      (field_.StaticValue() != Object::transition_sentinel().raw());
   return is_initialized ? NULL : this;
 }
 
@@ -398,8 +398,8 @@
   LoadStaticFieldInstr* other_load = other->AsLoadStaticField();
   ASSERT(other_load != NULL);
   // Assert that the field is initialized.
-  ASSERT(StaticField().value() != Object::sentinel().raw());
-  ASSERT(StaticField().value() != Object::transition_sentinel().raw());
+  ASSERT(StaticField().StaticValue() != Object::sentinel().raw());
+  ASSERT(StaticField().StaticValue() != Object::transition_sentinel().raw());
   return StaticField().raw() == other_load->StaticField().raw();
 }
 
@@ -1683,13 +1683,15 @@
     case Token::kSHL:
     case Token::kSHR:
       if (left.IsSmi() && right.IsSmi() && (Smi::Cast(right).Value() >= 0)) {
-        result = Smi::Cast(left).ShiftOp(op_kind(), Smi::Cast(right));
+        result = Smi::Cast(left).ShiftOp(op_kind(),
+                                         Smi::Cast(right),
+                                         Heap::kOld);
       }
       break;
     case Token::kBIT_AND:
     case Token::kBIT_OR:
     case Token::kBIT_XOR: {
-      result = left.BitOp(op_kind(), right);
+      result = left.BitOp(op_kind(), right, Heap::kOld);
       break;
     }
     case Token::kDIV:
@@ -3459,7 +3461,7 @@
         pieces.SetAt(store_index, String::Cast(obj));
       } else if (obj.IsSmi()) {
         const char* cstr = obj.ToCString();
-        pieces.SetAt(store_index, String::Handle(zone, String::New(cstr)));
+        pieces.SetAt(store_index, String::Handle(zone, Symbols::New(cstr)));
       } else if (obj.IsBool()) {
         pieces.SetAt(store_index,
             Bool::Cast(obj).value() ? Symbols::True() : Symbols::False());
diff --git a/runtime/vm/intermediate_language_arm.cc b/runtime/vm/intermediate_language_arm.cc
index c816561..bc4ba3c 100644
--- a/runtime/vm/intermediate_language_arm.cc
+++ b/runtime/vm/intermediate_language_arm.cc
@@ -997,8 +997,7 @@
   const Register char_code = locs()->in(0).reg();
   const Register result = locs()->out(0).reg();
 
-  ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ LoadExternalLabel(result, &label, kNotPatchable);
+  __ ldr(result, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(result, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ ldr(result, Address(result, char_code, LSL, 1));  // Char code is a smi.
 }
@@ -2244,7 +2243,7 @@
 void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   const Register field = locs()->in(0).reg();
   const Register result = locs()->out(0).reg();
-  __ LoadFieldFromOffset(kWord, result, field, Field::value_offset());
+  __ LoadFieldFromOffset(kWord, result, field, Field::static_value_offset());
 }
 
 
@@ -2266,10 +2265,12 @@
   __ LoadObject(temp, field());
   if (this->value()->NeedsStoreBuffer()) {
     __ StoreIntoObject(temp,
-        FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+                       FieldAddress(temp, Field::static_value_offset()),
+                       value,
+                       CanValueBeSmi());
   } else {
     __ StoreIntoObjectNoBarrier(
-        temp, FieldAddress(temp, Field::value_offset()), value);
+        temp, FieldAddress(temp, Field::static_value_offset()), value);
   }
 }
 
@@ -2775,7 +2776,7 @@
   Register temp = locs()->temp(0).reg();
   Label call_runtime, no_call;
 
-  __ ldr(temp, FieldAddress(field, Field::value_offset()));
+  __ ldr(temp, FieldAddress(field, Field::static_value_offset()));
   __ CompareObject(temp, Object::sentinel());
   __ b(&call_runtime, EQ);
 
diff --git a/runtime/vm/intermediate_language_arm64.cc b/runtime/vm/intermediate_language_arm64.cc
index 785d8f7..8c87b54 100644
--- a/runtime/vm/intermediate_language_arm64.cc
+++ b/runtime/vm/intermediate_language_arm64.cc
@@ -850,8 +850,7 @@
   const Register char_code = locs()->in(0).reg();
   const Register result = locs()->out(0).reg();
 
-  ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ LoadExternalLabel(result, &label);
+  __ ldr(result, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(
       result, result, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ SmiUntag(TMP, char_code);  // Untag to use scaled adress mode.
@@ -1968,7 +1967,7 @@
 void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   const Register field = locs()->in(0).reg();
   const Register result = locs()->out(0).reg();
-  __ LoadFieldFromOffset(result, field, Field::value_offset());
+  __ LoadFieldFromOffset(result, field, Field::static_value_offset());
 }
 
 
@@ -1990,9 +1989,11 @@
   __ LoadObject(temp, field());
   if (this->value()->NeedsStoreBuffer()) {
     __ StoreIntoObjectOffset(
-        temp, Field::value_offset(), value, CanValueBeSmi());
+        temp, Field::static_value_offset(), value, CanValueBeSmi());
   } else {
-    __ StoreIntoObjectOffsetNoBarrier(temp, Field::value_offset(), value);
+    __ StoreIntoObjectOffsetNoBarrier(temp,
+                                      Field::static_value_offset(),
+                                      value);
   }
 }
 
@@ -2489,7 +2490,7 @@
   Register temp = locs()->temp(0).reg();
   Label call_runtime, no_call;
 
-  __ ldr(temp, FieldAddress(field, Field::value_offset()));
+  __ ldr(temp, FieldAddress(field, Field::static_value_offset()));
   __ CompareObject(temp, Object::sentinel());
   __ b(&call_runtime, EQ);
 
diff --git a/runtime/vm/intermediate_language_ia32.cc b/runtime/vm/intermediate_language_ia32.cc
index af1185c1..35bc942 100644
--- a/runtime/vm/intermediate_language_ia32.cc
+++ b/runtime/vm/intermediate_language_ia32.cc
@@ -1971,7 +1971,7 @@
 void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   Register field = locs()->in(0).reg();
   Register result = locs()->out(0).reg();
-  __ movl(result, FieldAddress(field, Field::value_offset()));
+  __ movl(result, FieldAddress(field, Field::static_value_offset()));
 }
 
 
@@ -1993,10 +1993,12 @@
   __ LoadObject(temp, field());
   if (this->value()->NeedsStoreBuffer()) {
     __ StoreIntoObject(temp,
-        FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+                       FieldAddress(temp, Field::static_value_offset()),
+                       value,
+                       CanValueBeSmi());
   } else {
     __ StoreIntoObjectNoBarrier(
-        temp, FieldAddress(temp, Field::value_offset()), value);
+        temp, FieldAddress(temp, Field::static_value_offset()), value);
   }
 }
 
@@ -2493,7 +2495,7 @@
 
   Label call_runtime, no_call;
 
-  __ movl(temp, FieldAddress(field, Field::value_offset()));
+  __ movl(temp, FieldAddress(field, Field::static_value_offset()));
   __ CompareObject(temp, Object::sentinel());
   __ j(EQUAL, &call_runtime);
 
diff --git a/runtime/vm/intermediate_language_mips.cc b/runtime/vm/intermediate_language_mips.cc
index e888e84..eb7d932 100644
--- a/runtime/vm/intermediate_language_mips.cc
+++ b/runtime/vm/intermediate_language_mips.cc
@@ -1050,8 +1050,7 @@
 
   __ Comment("StringFromCharCodeInstr");
 
-  ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ LoadExternalLabel(result, &label, kNotPatchable);
+  __ lw(result, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(result, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ sll(TMP, char_code, 1);  // Char code is a smi.
   __ addu(TMP, TMP, result);
@@ -2092,7 +2091,9 @@
   __ Comment("LoadStaticFieldInstr");
   Register field = locs()->in(0).reg();
   Register result = locs()->out(0).reg();
-  __ LoadFromOffset(result, field, Field::value_offset() - kHeapObjectTag);
+  __ LoadFromOffset(result,
+                    field,
+                    Field::static_value_offset() - kHeapObjectTag);
 }
 
 
@@ -2115,10 +2116,12 @@
   __ LoadObject(temp, field());
   if (this->value()->NeedsStoreBuffer()) {
     __ StoreIntoObject(temp,
-        FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+                       FieldAddress(temp, Field::static_value_offset()),
+                       value,
+                       CanValueBeSmi());
   } else {
     __ StoreIntoObjectNoBarrier(
-        temp, FieldAddress(temp, Field::value_offset()), value);
+        temp, FieldAddress(temp, Field::static_value_offset()), value);
   }
 }
 
@@ -2592,7 +2595,7 @@
   Label call_runtime, no_call;
   __ Comment("InitStaticFieldInstr");
 
-  __ lw(temp, FieldAddress(field, Field::value_offset()));
+  __ lw(temp, FieldAddress(field, Field::static_value_offset()));
   __ BranchEqual(temp, Object::sentinel(), &call_runtime);
   __ BranchNotEqual(temp, Object::transition_sentinel(), &no_call);
 
diff --git a/runtime/vm/intermediate_language_x64.cc b/runtime/vm/intermediate_language_x64.cc
index 252bb13..4955df6 100644
--- a/runtime/vm/intermediate_language_x64.cc
+++ b/runtime/vm/intermediate_language_x64.cc
@@ -830,8 +830,7 @@
   Register char_code = locs()->in(0).reg();
   Register result = locs()->out(0).reg();
 
-  ExternalLabel label(reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ LoadExternalLabel(result, &label, kNotPatchable);
+  __ movq(result, Address(THR, Thread::predefined_symbols_address_offset()));
   __ movq(result, Address(result,
                           char_code,
                           TIMES_HALF_WORD_SIZE,  // Char code is a smi.
@@ -1973,7 +1972,7 @@
 void LoadStaticFieldInstr::EmitNativeCode(FlowGraphCompiler* compiler) {
   Register field = locs()->in(0).reg();
   Register result = locs()->out(0).reg();
-  __ movq(result, FieldAddress(field, Field::value_offset()));
+  __ movq(result, FieldAddress(field, Field::static_value_offset()));
 }
 
 
@@ -1995,10 +1994,12 @@
   __ LoadObject(temp, field());
   if (this->value()->NeedsStoreBuffer()) {
     __ StoreIntoObject(temp,
-        FieldAddress(temp, Field::value_offset()), value, CanValueBeSmi());
+                       FieldAddress(temp, Field::static_value_offset()),
+                       value,
+                       CanValueBeSmi());
   } else {
     __ StoreIntoObjectNoBarrier(
-        temp, FieldAddress(temp, Field::value_offset()), value);
+        temp, FieldAddress(temp, Field::static_value_offset()), value);
   }
 }
 
@@ -2489,7 +2490,7 @@
 
   Label call_runtime, no_call;
 
-  __ movq(temp, FieldAddress(field, Field::value_offset()));
+  __ movq(temp, FieldAddress(field, Field::static_value_offset()));
   __ CompareObject(temp, Object::sentinel());
   __ j(EQUAL, &call_runtime);
 
diff --git a/runtime/vm/intrinsifier_arm.cc b/runtime/vm/intrinsifier_arm.cc
index 4d37b8b..2618ef9 100644
--- a/runtime/vm/intrinsifier_arm.cc
+++ b/runtime/vm/intrinsifier_arm.cc
@@ -1536,14 +1536,16 @@
       random_class.LookupStaticField(Symbols::_A()));
   ASSERT(!random_A_field.IsNull());
   ASSERT(random_A_field.is_const());
-  const Instance& a_value = Instance::Handle(random_A_field.value());
+  const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
   const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
   // 'a_int_value' is a mask.
   ASSERT(Utils::IsUint(32, a_int_value));
   int32_t a_int32_value = static_cast<int32_t>(a_int_value);
 
-  __ ldr(R0, Address(SP, 0 * kWordSize));  // Receiver.
-  __ ldr(R1, FieldAddress(R0, state_field.Offset()));  // Field '_state'.
+  // Receiver.
+  __ ldr(R0, Address(SP, 0 * kWordSize));
+  // Field '_state'.
+  __ ldr(R1, FieldAddress(R0, state_field.Offset()));
   // Addresses of _state[0] and _state[1].
 
   const int64_t disp_0 = Instance::DataOffsetFor(kTypedDataUint32ArrayCid);
@@ -1660,14 +1662,7 @@
   __ ldrb(R1, Address(R0, R1));
   __ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
   __ b(&fall_through, GE);
-  const ExternalLabel symbols_label(
-      reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ Push(PP);
-  __ LoadPoolPointer();
-  assembler->set_constant_pool_allowed(true);
-  __ LoadExternalLabel(R0, &symbols_label, kNotPatchable);
-  assembler->set_constant_pool_allowed(false);
-  __ Pop(PP);
+  __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ ldr(R0, Address(R0, R1, LSL, 2));
   __ Ret();
@@ -1680,12 +1675,7 @@
   __ ldrh(R1, Address(R0, R1));
   __ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
   __ b(&fall_through, GE);
-  __ Push(PP);
-  __ LoadPoolPointer();
-  assembler->set_constant_pool_allowed(true);
-  __ LoadExternalLabel(R0, &symbols_label, kNotPatchable);
-  assembler->set_constant_pool_allowed(false);
-  __ Pop(PP);
+  __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ ldr(R0, Address(R0, R1, LSL, 2));
   __ Ret();
@@ -2022,8 +2012,6 @@
 
   // Registers are now set up for the lazy compile stub. It expects the function
   // in R0, the argument descriptor in R4, and IC-Data in R5.
-  static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
-  __ LoadObject(R4, Array::Handle(ArgumentsDescriptor::New(arg_count)));
   __ eor(R5, R5, Operand(R5));
 
   // Tail-call the function.
diff --git a/runtime/vm/intrinsifier_arm64.cc b/runtime/vm/intrinsifier_arm64.cc
index c8b4df7..4524a54 100644
--- a/runtime/vm/intrinsifier_arm64.cc
+++ b/runtime/vm/intrinsifier_arm64.cc
@@ -1617,11 +1617,13 @@
       random_class.LookupStaticField(Symbols::_A()));
   ASSERT(!random_A_field.IsNull());
   ASSERT(random_A_field.is_const());
-  const Instance& a_value = Instance::Handle(random_A_field.value());
+  const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
   const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
 
-  __ ldr(R0, Address(SP, 0 * kWordSize));  // Receiver.
-  __ ldr(R1, FieldAddress(R0, state_field.Offset()));  // Field '_state'.
+  // Receiver.
+  __ ldr(R0, Address(SP, 0 * kWordSize));
+  // Field '_state'.
+  __ ldr(R1, FieldAddress(R0, state_field.Offset()));
 
   // Addresses of _state[0].
   const int64_t disp =
@@ -1736,12 +1738,7 @@
   __ ldr(R1, Address(R0, R1), kUnsignedByte);
   __ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
   __ b(&fall_through, GE);
-  const ExternalLabel symbols_label(
-      reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ TagAndPushPP();
-  __ LoadPoolPointer();
-  __ LoadExternalLabel(R0, &symbols_label);
-  __ PopAndUntagPP();
+  __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(
       R0, R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ ldr(R0, Address(R0, R1, UXTX, Address::Scaled));
@@ -1755,10 +1752,7 @@
   __ ldr(R1, Address(R0, R1), kUnsignedHalfword);
   __ CompareImmediate(R1, Symbols::kNumberOfOneCharCodeSymbols);
   __ b(&fall_through, GE);
-  __ TagAndPushPP();
-  __ LoadPoolPointer();
-  __ LoadExternalLabel(R0, &symbols_label);
-  __ PopAndUntagPP();
+  __ ldr(R0, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(
       R0, R0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ ldr(R0, Address(R0, R1, UXTX, Address::Scaled));
@@ -2097,8 +2091,6 @@
 
   // Registers are now set up for the lazy compile stub. It expects the function
   // in R0, the argument descriptor in R4, and IC-Data in R5.
-  static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
-  __ LoadObject(R4, Array::Handle(ArgumentsDescriptor::New(arg_count)));
   __ eor(R5, R5, Operand(R5));
 
   // Tail-call the function.
diff --git a/runtime/vm/intrinsifier_ia32.cc b/runtime/vm/intrinsifier_ia32.cc
index 1814d20..d39f3b6 100644
--- a/runtime/vm/intrinsifier_ia32.cc
+++ b/runtime/vm/intrinsifier_ia32.cc
@@ -1635,13 +1635,15 @@
       random_class.LookupStaticField(Symbols::_A()));
   ASSERT(!random_A_field.IsNull());
   ASSERT(random_A_field.is_const());
-  const Instance& a_value = Instance::Handle(random_A_field.value());
+  const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
   const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
   // 'a_int_value' is a mask.
   ASSERT(Utils::IsUint(32, a_int_value));
   int32_t a_int32_value = static_cast<int32_t>(a_int_value);
-  __ movl(EAX, Address(ESP, + 1 * kWordSize));  // Receiver.
-  __ movl(EBX, FieldAddress(EAX, state_field.Offset()));  // Field '_state'.
+  // Receiver.
+  __ movl(EAX, Address(ESP, + 1 * kWordSize));
+  // Field '_state'.
+  __ movl(EBX, FieldAddress(EAX, state_field.Offset()));
   // Addresses of _state[0] and _state[1].
   const intptr_t scale = Instance::ElementSizeFor(kTypedDataUint32ArrayCid);
   const intptr_t offset = Instance::DataOffsetFor(kTypedDataUint32ArrayCid);
@@ -2108,8 +2110,6 @@
 
   // Registers are now set up for the lazy compile stub. It expects the function
   // in EAX, the argument descriptor in EDX, and IC-Data in ECX.
-  static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
-  __ LoadObject(EDX, Array::ZoneHandle(ArgumentsDescriptor::New(arg_count)));
   __ xorl(ECX, ECX);
 
   // Tail-call the function.
diff --git a/runtime/vm/intrinsifier_mips.cc b/runtime/vm/intrinsifier_mips.cc
index 73d9ccd..d5df533 100644
--- a/runtime/vm/intrinsifier_mips.cc
+++ b/runtime/vm/intrinsifier_mips.cc
@@ -1634,14 +1634,16 @@
       random_class.LookupStaticField(Symbols::_A()));
   ASSERT(!random_A_field.IsNull());
   ASSERT(random_A_field.is_const());
-  const Instance& a_value = Instance::Handle(random_A_field.value());
+  const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
   const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
   // 'a_int_value' is a mask.
   ASSERT(Utils::IsUint(32, a_int_value));
   int32_t a_int32_value = static_cast<int32_t>(a_int_value);
 
-  __ lw(T0, Address(SP, 0 * kWordSize));  // Receiver.
-  __ lw(T1, FieldAddress(T0, state_field.Offset()));  // Field '_state'.
+  // Receiver.
+  __ lw(T0, Address(SP, 0 * kWordSize));
+  // Field '_state'.
+  __ lw(T1, FieldAddress(T0, state_field.Offset()));
 
   // Addresses of _state[0] and _state[1].
   const intptr_t scale = Instance::ElementSizeFor(kTypedDataUint32ArrayCid);
@@ -1767,16 +1769,7 @@
   __ lbu(T2, FieldAddress(T2, OneByteString::data_offset()));
   __ BranchUnsignedGreaterEqual(
       T2, Immediate(Symbols::kNumberOfOneCharCodeSymbols), &fall_through);
-  const ExternalLabel symbols_label(
-      reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ Push(PP);
-  __ Push(RA);
-  __ LoadPoolPointer();
-  assembler->set_constant_pool_allowed(true);
-  __ LoadExternalLabel(V0, &symbols_label, kNotPatchable);
-  assembler->set_constant_pool_allowed(false);
-  __ Pop(RA);
-  __ Pop(PP);
+  __ lw(V0, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(V0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ sll(T2, T2, 2);
   __ addu(T2, T2, V0);
@@ -1790,14 +1783,7 @@
   __ lhu(T2, FieldAddress(T2, TwoByteString::data_offset()));
   __ BranchUnsignedGreaterEqual(
       T2, Immediate(Symbols::kNumberOfOneCharCodeSymbols), &fall_through);
-  __ Push(PP);
-  __ Push(RA);
-  __ LoadPoolPointer();
-  assembler->set_constant_pool_allowed(true);
-  __ LoadExternalLabel(V0, &symbols_label, kNotPatchable);
-  assembler->set_constant_pool_allowed(false);
-  __ Pop(RA);
-  __ Pop(PP);
+  __ lw(V0, Address(THR, Thread::predefined_symbols_address_offset()));
   __ AddImmediate(V0, Symbols::kNullCharCodeSymbolOffset * kWordSize);
   __ sll(T2, T2, 2);
   __ addu(T2, T2, V0);
@@ -2136,8 +2122,6 @@
 
   // Registers are now set up for the lazy compile stub. It expects the function
   // in T0, the argument descriptor in S4, and IC-Data in S5.
-  static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
-  __ LoadObject(S4, Array::Handle(ArgumentsDescriptor::New(arg_count)));
   __ mov(S5, ZR);
 
   // Tail-call the function.
diff --git a/runtime/vm/intrinsifier_x64.cc b/runtime/vm/intrinsifier_x64.cc
index b911a99..d71abc3 100644
--- a/runtime/vm/intrinsifier_x64.cc
+++ b/runtime/vm/intrinsifier_x64.cc
@@ -1489,10 +1489,12 @@
       random_class.LookupStaticField(Symbols::_A()));
   ASSERT(!random_A_field.IsNull());
   ASSERT(random_A_field.is_const());
-  const Instance& a_value = Instance::Handle(random_A_field.value());
+  const Instance& a_value = Instance::Handle(random_A_field.StaticValue());
   const int64_t a_int_value = Integer::Cast(a_value).AsInt64Value();
-  __ movq(RAX, Address(RSP, + 1 * kWordSize));  // Receiver.
-  __ movq(RBX, FieldAddress(RAX, state_field.Offset()));  // Field '_state'.
+  // Receiver.
+  __ movq(RAX, Address(RSP, + 1 * kWordSize));
+  // Field '_state'.
+  __ movq(RBX, FieldAddress(RAX, state_field.Offset()));
   // Addresses of _state[0] and _state[1].
   const intptr_t scale = Instance::ElementSizeFor(kTypedDataUint32ArrayCid);
   const intptr_t offset = Instance::DataOffsetFor(kTypedDataUint32ArrayCid);
@@ -1608,14 +1610,7 @@
   __ movzxb(RCX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset()));
   __ cmpq(RCX, Immediate(Symbols::kNumberOfOneCharCodeSymbols));
   __ j(GREATER_EQUAL, &fall_through);
-  const ExternalLabel symbols_label(
-      reinterpret_cast<uword>(Symbols::PredefinedAddress()));
-  __ pushq(PP);
-  __ LoadPoolPointer();
-  assembler->set_constant_pool_allowed(true);
-  __ LoadExternalLabel(RAX, &symbols_label, kNotPatchable);
-  assembler->set_constant_pool_allowed(false);
-  __ popq(PP);
+  __ movq(RAX, Address(THR, Thread::predefined_symbols_address_offset()));
   __ movq(RAX, Address(RAX,
                        RCX,
                        TIMES_8,
@@ -1629,12 +1624,7 @@
   __ movzxw(RCX, FieldAddress(RAX, RCX, TIMES_1, OneByteString::data_offset()));
   __ cmpq(RCX, Immediate(Symbols::kNumberOfOneCharCodeSymbols));
   __ j(GREATER_EQUAL, &fall_through);
-  __ pushq(PP);
-  __ LoadPoolPointer();
-  assembler->set_constant_pool_allowed(true);
-  __ LoadExternalLabel(RAX, &symbols_label, kNotPatchable);
-  assembler->set_constant_pool_allowed(false);
-  __ popq(PP);
+  __ movq(RAX, Address(THR, Thread::predefined_symbols_address_offset()));
   __ movq(RAX, Address(RAX,
                        RCX,
                        TIMES_8,
@@ -1970,9 +1960,6 @@
 
   // Registers are now set up for the lazy compile stub. It expects the function
   // in RAX, the argument descriptor in R10, and IC-Data in RCX.
-  static const intptr_t arg_count = RegExpMacroAssembler::kParamCount;
-  __ LoadObject(R10,
-      Array::ZoneHandle(ArgumentsDescriptor::New(arg_count)));
   __ xorq(RCX, RCX);
 
   // Tail-call the function.
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index bb1fe76..06dfad2 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -1531,7 +1531,7 @@
     NoSafepointScope no_safepoint_scope;
 
     if (compiler_stats_ != NULL) {
-      compiler_stats()->Print();
+      OS::Print("%s", compiler_stats()->PrintToZone());
     }
 
     // Notify exit listeners that this isolate is shutting down.
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 86d461a..107ad5d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -1511,7 +1511,7 @@
   field_name = Symbols::New("cid"#clazz);                                      \
   field = Field::New(field_name, true, false, true, false, cls, 0);            \
   value = Smi::New(k##clazz##Cid);                                             \
-  field.set_value(value);                                                      \
+  field.SetStaticValue(value, true);                                           \
   field.set_type(Type::Handle(Type::IntType()));                               \
   cls.AddField(field);                                                         \
 
@@ -2120,7 +2120,8 @@
 
 void Class::AddFunction(const Function& function) const {
   const Array& arr = Array::Handle(functions());
-  const Array& new_arr = Array::Handle(Array::Grow(arr, arr.Length() + 1));
+  const Array& new_arr =
+      Array::Handle(Array::Grow(arr, arr.Length() + 1, Heap::kOld));
   new_arr.SetAt(arr.Length(), function);
   StorePointer(&raw_ptr()->functions_, new_arr.raw());
   // Add to hash table, if any.
@@ -2280,12 +2281,12 @@
   GrowableObjectArray& closures =
       GrowableObjectArray::Handle(raw_ptr()->closure_functions_);
   if (closures.IsNull()) {
-    closures = GrowableObjectArray::New(4);
+    closures = GrowableObjectArray::New(4, Heap::kOld);
     StorePointer(&raw_ptr()->closure_functions_, closures.raw());
   }
   ASSERT(function.IsNonImplicitClosureFunction());
   ASSERT(function.Owner() == this->raw());
-  closures.Add(function);
+  closures.Add(function, Heap::kOld);
 }
 
 
@@ -3585,7 +3586,7 @@
     ASSERT(direct_subclasses.At(i) != subclass.raw());
   }
 #endif
-  direct_subclasses.Add(subclass);
+  direct_subclasses.Add(subclass, Heap::kOld);
 }
 
 
@@ -3734,7 +3735,8 @@
                                  const TypeArguments& type_arguments,
                                  const Class& other,
                                  const TypeArguments& other_type_arguments,
-                                 Error* bound_error) {
+                                 Error* bound_error,
+                                 Heap::Space space) {
   // Use the thsi object as if it was the receiver of this method, but instead
   // of recursing reset it to the super class and loop.
   Isolate* isolate = Isolate::Current();
@@ -3791,7 +3793,8 @@
                                      other_type_arguments,
                                      from_index,
                                      num_type_params,
-                                     bound_error);
+                                     bound_error,
+                                     space);
     }
     const bool other_is_function_class = other.IsFunctionClass();
     if (other.IsSignatureClass() || other_is_function_class) {
@@ -3808,7 +3811,8 @@
                             type_arguments,
                             other_fun,
                             other_type_arguments,
-                            bound_error);
+                            bound_error,
+                            space);
       }
       // Check if type S has a call() method of function type T.
       Function& function =
@@ -3828,7 +3832,8 @@
                               type_arguments,
                               other_fun,
                               other_type_arguments,
-                              bound_error)) {
+                              bound_error,
+                              space)) {
               return true;
             }
       }
@@ -3868,7 +3873,8 @@
         // after the type arguments of the super type of this type.
         // The index of the type parameters is adjusted upon finalization.
         error = Error::null();
-        interface_args = interface_args.InstantiateFrom(type_arguments, &error);
+        interface_args =
+            interface_args.InstantiateFrom(type_arguments, &error, NULL, space);
         if (!error.IsNull()) {
           // Return the first bound error to the caller if it requests it.
           if ((bound_error != NULL) && bound_error->IsNull()) {
@@ -3881,7 +3887,8 @@
                                    interface_args,
                                    other,
                                    other_type_arguments,
-                                   bound_error)) {
+                                   bound_error,
+                                   space)) {
         return true;
       }
     }
@@ -3906,13 +3913,15 @@
                      const TypeArguments& type_arguments,
                      const Class& other,
                      const TypeArguments& other_type_arguments,
-                     Error* bound_error) const {
+                     Error* bound_error,
+                     Heap::Space space) const {
   return TypeTestNonRecursive(*this,
                               test_kind,
                               type_arguments,
                               other,
                               other_type_arguments,
-                              bound_error);
+                              bound_error,
+                              space);
 }
 
 
@@ -4229,13 +4238,15 @@
 
 
 const char* Class::ToCString() const {
-  const char* format = "%s Class: %s";
+  const char* format = "%s %sClass: %s";
   const Library& lib = Library::Handle(library());
   const char* library_name = lib.IsNull() ? "" : lib.ToCString();
+  const char* patch_prefix = is_patch() ? "Patch " : "";
   const char* class_name = String::Handle(Name()).ToCString();
-  intptr_t len = OS::SNPrint(NULL, 0, format, library_name, class_name) + 1;
+  intptr_t len =
+      OS::SNPrint(NULL, 0, format, library_name, patch_prefix, class_name) + 1;
   char* chars = Thread::Current()->zone()->Alloc<char>(len);
-  OS::SNPrint(chars, len, format, library_name, class_name);
+  OS::SNPrint(chars, len, format, library_name, patch_prefix, class_name);
   return chars;
 }
 
@@ -4548,7 +4559,8 @@
                              const TypeArguments& other,
                              intptr_t from_index,
                              intptr_t len,
-                             Error* bound_error) const {
+                             Error* bound_error,
+                             Heap::Space space) const {
   ASSERT(Length() >= (from_index + len));
   ASSERT(!other.IsNull());
   ASSERT(other.Length() >= (from_index + len));
@@ -4559,7 +4571,7 @@
     ASSERT(!type.IsNull());
     other_type = other.TypeAt(from_index + i);
     ASSERT(!other_type.IsNull());
-    if (!type.TypeTest(test_kind, other_type, bound_error)) {
+    if (!type.TypeTest(test_kind, other_type, bound_error, space)) {
       return false;
     }
   }
@@ -6044,7 +6056,8 @@
   // Check that this function's signature type is a subtype of the other
   // function's signature type.
   if (!TypeTest(kIsSubtypeOf, Object::null_type_arguments(),
-                other, Object::null_type_arguments(), bound_error)) {
+                other, Object::null_type_arguments(), bound_error,
+                Heap::kOld)) {
     // For more informative error reporting, use the location of the other
     // function here, since the caller will use the location of this function.
     *bound_error = LanguageError::NewFormatted(
@@ -6086,12 +6099,15 @@
     const TypeArguments& type_arguments,
     const Function& other,
     const TypeArguments& other_type_arguments,
-    Error* bound_error) const {
+    Error* bound_error,
+    Heap::Space space) const {
   AbstractType& other_param_type =
       AbstractType::Handle(other.ParameterTypeAt(other_parameter_position));
   if (!other_param_type.IsInstantiated()) {
     other_param_type = other_param_type.InstantiateFrom(other_type_arguments,
-                                                        bound_error);
+                                                        bound_error,
+                                                        NULL,  // trail
+                                                        space);
     ASSERT((bound_error == NULL) || bound_error->IsNull());
   }
   if (other_param_type.IsDynamicType()) {
@@ -6100,20 +6116,21 @@
   AbstractType& param_type =
       AbstractType::Handle(ParameterTypeAt(parameter_position));
   if (!param_type.IsInstantiated()) {
-    param_type = param_type.InstantiateFrom(type_arguments, bound_error);
+    param_type = param_type.InstantiateFrom(
+        type_arguments, bound_error, NULL /*trail*/, space);
     ASSERT((bound_error == NULL) || bound_error->IsNull());
   }
   if (param_type.IsDynamicType()) {
     return test_kind == kIsSubtypeOf;
   }
   if (test_kind == kIsSubtypeOf) {
-    if (!param_type.IsSubtypeOf(other_param_type, bound_error) &&
-        !other_param_type.IsSubtypeOf(param_type, bound_error)) {
+    if (!param_type.IsSubtypeOf(other_param_type, bound_error, space) &&
+        !other_param_type.IsSubtypeOf(param_type, bound_error, space)) {
       return false;
     }
   } else {
     ASSERT(test_kind == kIsMoreSpecificThan);
-    if (!param_type.IsMoreSpecificThan(other_param_type, bound_error)) {
+    if (!param_type.IsMoreSpecificThan(other_param_type, bound_error, space)) {
       return false;
     }
   }
@@ -6125,7 +6142,8 @@
                         const TypeArguments& type_arguments,
                         const Function& other,
                         const TypeArguments& other_type_arguments,
-                        Error* bound_error) const {
+                        Error* bound_error,
+                        Heap::Space space) const {
   const intptr_t num_fixed_params = num_fixed_parameters();
   const intptr_t num_opt_pos_params = NumOptionalPositionalParameters();
   const intptr_t num_opt_named_params = NumOptionalNamedParameters();
@@ -6183,7 +6201,8 @@
     if (!TestParameterType(test_kind,
                            i + num_ignored_params, i + other_num_ignored_params,
                            type_arguments, other, other_type_arguments,
-                           bound_error)) {
+                           bound_error,
+                           space)) {
       return false;
     }
   }
@@ -6214,7 +6233,8 @@
         if (!TestParameterType(test_kind,
                                j, i,
                                type_arguments, other, other_type_arguments,
-                               bound_error)) {
+                               bound_error,
+                               space)) {
           return false;
         }
         break;
@@ -6760,9 +6780,9 @@
       tmp = cls.PrettyName();
     }
   }
-  tmp = String::Concat(tmp, Symbols::Dot());
+  tmp = String::Concat(tmp, Symbols::Dot(), Heap::kOld);
   const String& suffix = String::Handle(PrettyName());
-  return String::Concat(tmp, suffix);
+  return String::Concat(tmp, suffix, Heap::kOld);
 }
 
 
@@ -6874,7 +6894,7 @@
     set_ic_data_array(Object::empty_array());
   } else {
     const Array& a = Array::Handle(Array::New(count, Heap::kOld));
-    INC_STAT(Isolate::Current(), total_code_size, count * sizeof(uword));
+    INC_STAT(Thread::Current(), total_code_size, count * sizeof(uword));
     count = 0;
     for (intptr_t i = 0; i < deopt_id_to_ic_data.length(); i++) {
       if (deopt_id_to_ic_data[i] != NULL) {
@@ -7182,7 +7202,7 @@
 
 
 RawString* Field::GetterName(const String& field_name) {
-  return Field::GetterSymbol(field_name);
+  return String::Concat(Symbols::GetterPrefix(), field_name);
 }
 
 
@@ -7191,6 +7211,11 @@
 }
 
 
+RawString* Field::LookupGetterSymbol(const String& field_name) {
+  return Symbols::LookupFromConcat(Symbols::GetterPrefix(), field_name);
+}
+
+
 RawString* Field::SetterName(const String& field_name) {
   return String::Concat(Symbols::SetterPrefix(), field_name);
 }
@@ -7201,6 +7226,11 @@
 }
 
 
+RawString* Field::LookupSetterSymbol(const String& field_name) {
+  return Symbols::LookupFromConcat(Symbols::SetterPrefix(), field_name);
+}
+
+
 RawString* Field::NameFromGetter(const String& getter_name) {
   return Symbols::New(getter_name, kGetterPrefixLength,
       getter_name.Length() - kGetterPrefixLength);
@@ -7249,18 +7279,6 @@
 }
 
 
-RawInstance* Field::value() const {
-  ASSERT(is_static());  // Valid only for static dart fields.
-  return raw_ptr()->value_;
-}
-
-
-void Field::set_value(const Instance& value) const {
-  ASSERT(is_static());  // Valid only for static dart fields.
-  StorePointer(&raw_ptr()->value_, value.raw());
-}
-
-
 void Field::set_type(const AbstractType& value) const {
   ASSERT(!value.IsNull());
   StorePointer(&raw_ptr()->type_, value.raw());
@@ -7287,9 +7305,7 @@
   const Field& result = Field::Handle(Field::New());
   result.set_name(name);
   result.set_is_static(is_static);
-  if (is_static) {
-    result.set_value(Object::null_instance());
-  } else {
+  if (!is_static) {
     result.SetOffset(0);
   }
   result.set_is_final(is_final);
@@ -7431,7 +7447,7 @@
     return;
   }
   if (is_static()) {
-    const Instance& valueObj = Instance::Handle(value());
+    const Instance& valueObj = Instance::Handle(StaticValue());
     jsobj.AddProperty("staticValue", valueObj);
   }
 
@@ -7460,6 +7476,7 @@
   }
 }
 
+
 // Build a closure object that gets (or sets) the contents of a static
 // field f and cache the closure in a newly created static field
 // named #f (or #f= in case of a setter).
@@ -7477,7 +7494,8 @@
   closure_field = field_owner.LookupStaticField(closure_name);
   if (!closure_field.IsNull()) {
     ASSERT(closure_field.is_static());
-    const Instance& closure = Instance::Handle(closure_field.value());
+    const Instance& closure =
+        Instance::Handle(closure_field.StaticValue());
     ASSERT(!closure.IsNull());
     ASSERT(closure.IsClosure());
     return closure.raw();
@@ -7510,7 +7528,7 @@
                              false,  // is_reflectable
                              field_owner,
                              this->token_pos());
-  closure_field.set_value(Instance::Cast(result));
+  closure_field.SetStaticValue(Instance::Cast(result), true);
   closure_field.set_type(Type::Handle(Type::DynamicType()));
   field_owner.AddField(closure_field);
 
@@ -7586,33 +7604,43 @@
 
 
 bool Field::IsUninitialized() const {
-  const Instance& value = Instance::Handle(raw_ptr()->value_);
+  const Instance& value = Instance::Handle(raw_ptr()->value_.static_value_);
   ASSERT(value.raw() != Object::transition_sentinel().raw());
   return value.raw() == Object::sentinel().raw();
 }
 
 
-void Field::set_initializer(const Function& initializer) const {
-  StorePointer(&raw_ptr()->initializer_, initializer.raw());
+void Field::SetPrecompiledInitializer(const Function& initializer) const {
+  StorePointer(&raw_ptr()->initializer_.precompiled_, initializer.raw());
+}
+
+
+bool Field::HasPrecompiledInitializer() const {
+  return raw_ptr()->initializer_.precompiled_->IsFunction();
+}
+
+
+void Field::SetSavedInitialStaticValue(const Instance& value) const {
+  StorePointer(&raw_ptr()->initializer_.saved_value_, value.raw());
 }
 
 
 void Field::EvaluateInitializer() const {
   ASSERT(is_static());
-  if (value() == Object::sentinel().raw()) {
-    set_value(Object::transition_sentinel());
+  if (StaticValue() == Object::sentinel().raw()) {
+    SetStaticValue(Object::transition_sentinel());
     Object& value = Object::Handle(Compiler::EvaluateStaticInitializer(*this));
     if (value.IsError()) {
-      set_value(Object::null_instance());
+      SetStaticValue(Object::null_instance());
       Exceptions::PropagateError(Error::Cast(value));
       UNREACHABLE();
     }
     ASSERT(value.IsNull() || value.IsInstance());
-    set_value(value.IsNull() ? Instance::null_instance()
+    SetStaticValue(value.IsNull() ? Instance::null_instance()
                              : Instance::Cast(value));
     return;
-  } else if (value() == Object::transition_sentinel().raw()) {
-    set_value(Object::null_instance());
+  } else if (StaticValue() == Object::transition_sentinel().raw()) {
+    SetStaticValue(Object::null_instance());
     const Array& ctor_args = Array::Handle(Array::New(1));
     const String& field_name = String::Handle(name());
     ctor_args.SetAt(0, field_name);
@@ -8564,7 +8592,6 @@
 void Script::Tokenize(const String& private_key) const {
   Thread* thread = Thread::Current();
   Zone* zone = thread->zone();
-  Isolate* isolate = thread->isolate();
   const TokenStream& tkns = TokenStream::Handle(zone, tokens());
   if (!tkns.IsNull()) {
     // Already tokenized.
@@ -8578,7 +8605,7 @@
   set_tokens(TokenStream::Handle(zone,
                                  TokenStream::New(scanner.GetStream(),
                                                   private_key)));
-  INC_STAT(isolate, src_length, src.Length());
+  INC_STAT(thread, src_length, src.Length());
 }
 
 
@@ -9058,35 +9085,40 @@
 
 
 static RawString* MakeClassMetaName(const Class& cls) {
-  String& cname = String::Handle(cls.Name());
-  return String::Concat(Symbols::At(), cname);
+  return Symbols::FromConcat(Symbols::At(), String::Handle(cls.Name()));
 }
 
 
 static RawString* MakeFieldMetaName(const Field& field) {
   const String& cname =
       String::Handle(MakeClassMetaName(Class::Handle(field.origin())));
-  String& fname = String::Handle(field.name());
-  fname = String::Concat(Symbols::At(), fname);
-  return String::Concat(cname, fname);
+  GrowableHandlePtrArray<const String> pieces(Thread::Current()->zone(), 3);
+  pieces.Add(cname);
+  pieces.Add(Symbols::At());
+  pieces.Add(String::Handle(field.name()));
+  return Symbols::FromConcatAll(pieces);
 }
 
 
 static RawString* MakeFunctionMetaName(const Function& func) {
   const String& cname =
       String::Handle(MakeClassMetaName(Class::Handle(func.origin())));
-  String& fname = String::Handle(func.QualifiedPrettyName());
-  fname = String::Concat(Symbols::At(), fname);
-  return String::Concat(cname, fname);
+  GrowableHandlePtrArray<const String> pieces(Thread::Current()->zone(), 3);
+  pieces.Add(cname);
+  pieces.Add(Symbols::At());
+  pieces.Add(String::Handle(func.QualifiedPrettyName()));
+  return Symbols::FromConcatAll(pieces);
 }
 
 
 static RawString* MakeTypeParameterMetaName(const TypeParameter& param) {
   const String& cname = String::Handle(
       MakeClassMetaName(Class::Handle(param.parameterized_class())));
-  String& pname = String::Handle(param.name());
-  pname = String::Concat(Symbols::At(), pname);
-  return String::Concat(cname, pname);
+  GrowableHandlePtrArray<const String> pieces(Thread::Current()->zone(), 3);
+  pieces.Add(cname);
+  pieces.Add(Symbols::At());
+  pieces.Add(String::Handle(param.name()));
+  return Symbols::FromConcatAll(pieces);
 }
 
 
@@ -9102,7 +9134,7 @@
                                           cls,
                                           token_pos));
   field.set_type(Type::Handle(Type::DynamicType()));
-  field.set_value(Array::empty_array());
+  field.SetStaticValue(Array::empty_array(), true);
   GrowableObjectArray& metadata =
       GrowableObjectArray::Handle(this->metadata());
   metadata.Add(field, Heap::kOld);
@@ -9196,13 +9228,13 @@
     return Object::empty_array().raw();
   }
   Object& metadata = Object::Handle();
-  metadata = field.value();
-  if (field.value() == Object::empty_array().raw()) {
+  metadata = field.StaticValue();
+  if (field.StaticValue() == Object::empty_array().raw()) {
     metadata = Parser::ParseMetadata(Class::Handle(field.owner()),
                                      field.token_pos());
     if (metadata.IsArray()) {
       ASSERT(Array::Cast(metadata).raw() != Object::empty_array().raw());
-      field.set_value(Array::Cast(metadata));
+      field.SetStaticValue(Array::Cast(metadata), true);
     }
   }
   return metadata.raw();
@@ -10611,7 +10643,7 @@
                                           owner_class,
                                           token_pos));
   field.set_type(Type::Handle(Type::DynamicType()));
-  field.set_value(Array::empty_array());
+  field.SetStaticValue(Array::empty_array(), true);
   set_metadata_field(field);
   owner_class.AddField(field);
 }
@@ -10624,13 +10656,13 @@
     return Object::empty_array().raw();
   }
   Object& metadata = Object::Handle();
-  metadata = field.value();
-  if (field.value() == Object::empty_array().raw()) {
+  metadata = field.StaticValue();
+  if (field.StaticValue() == Object::empty_array().raw()) {
     metadata = Parser::ParseMetadata(Class::Handle(field.owner()),
                                      field.token_pos());
     if (metadata.IsArray()) {
       ASSERT(Array::Cast(metadata).raw() != Object::empty_array().raw());
-      field.set_value(Array::Cast(metadata));
+      field.SetStaticValue(Array::Cast(metadata), true);
     }
   }
   return metadata.raw();
@@ -10709,9 +10741,16 @@
   if (!Field::IsGetterName(name) &&
       !Field::IsSetterName(name) &&
       (obj.IsNull() || obj.IsLibraryPrefix())) {
-    obj = lib.LookupEntry(String::Handle(Field::GetterName(name)), &ignore);
+    const String& getter_name = String::Handle(Field::LookupGetterSymbol(name));
+    if (!getter_name.IsNull()) {
+      obj = lib.LookupEntry(getter_name, &ignore);
+    }
     if (obj.IsNull()) {
-      obj = lib.LookupEntry(String::Handle(Field::SetterName(name)), &ignore);
+      const String& setter_name =
+          String::Handle(Field::LookupSetterSymbol(name));
+      if (!setter_name.IsNull()) {
+        obj = lib.LookupEntry(setter_name, &ignore);
+      }
     }
   }
 
@@ -11040,10 +11079,6 @@
     return "link native";
   }
 
-  if (addr == reinterpret_cast<uword>(Symbols::PredefinedAddress())) {
-    return "predefined symbols";
-  }
-
   return "UNKNOWN";
 }
 
@@ -11092,15 +11127,14 @@
 RawPcDescriptors* PcDescriptors::New(GrowableArray<uint8_t>* data) {
   ASSERT(Object::pc_descriptors_class() != Class::null());
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
   PcDescriptors& result = PcDescriptors::Handle(thread->zone());
   {
     uword size = PcDescriptors::InstanceSize(data->length());
     RawObject* raw = Object::Allocate(PcDescriptors::kClassId,
                                       size,
                                       Heap::kOld);
-    INC_STAT(isolate, total_code_size, size);
-    INC_STAT(isolate, pc_desc_size, size);
+    INC_STAT(thread, total_code_size, size);
+    INC_STAT(thread, pc_desc_size, size);
     NoSafepointScope no_safepoint;
     result ^= raw;
     result.SetLength(data->length());
@@ -11113,15 +11147,14 @@
 RawPcDescriptors* PcDescriptors::New(intptr_t length) {
   ASSERT(Object::pc_descriptors_class() != Class::null());
   Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
   PcDescriptors& result = PcDescriptors::Handle(thread->zone());
   {
     uword size = PcDescriptors::InstanceSize(length);
     RawObject* raw = Object::Allocate(PcDescriptors::kClassId,
                                       size,
                                       Heap::kOld);
-    INC_STAT(isolate, total_code_size, size);
-    INC_STAT(isolate, pc_desc_size, size);
+    INC_STAT(thread, total_code_size, size);
+    INC_STAT(thread, pc_desc_size, size);
     NoSafepointScope no_safepoint;
     result ^= raw;
     result.SetLength(length);
@@ -11571,8 +11604,8 @@
     RawObject* raw = Object::Allocate(LocalVarDescriptors::kClassId,
                                       size,
                                       Heap::kOld);
-    INC_STAT(Isolate::Current(), total_code_size, size);
-    INC_STAT(Isolate::Current(), vardesc_size, size);
+    INC_STAT(Thread::Current(), total_code_size, size);
+    INC_STAT(Thread::Current(), vardesc_size, size);
     NoSafepointScope no_safepoint;
     result ^= raw;
     result.StoreNonPointer(&result.raw_ptr()->num_entries_, num_variables);
@@ -12713,7 +12746,7 @@
 void Code::set_stackmaps(const Array& maps) const {
   ASSERT(maps.IsOld());
   StorePointer(&raw_ptr()->stackmaps_, maps.raw());
-  INC_STAT(Isolate::Current(),
+  INC_STAT(Thread::Current(),
            total_code_size,
            maps.IsNull() ? 0 : maps.Length() * sizeof(uword));
 }
@@ -13017,8 +13050,8 @@
   Code& code = Code::ZoneHandle(Code::New(pointer_offset_count));
   Instructions& instrs =
       Instructions::ZoneHandle(Instructions::New(assembler->CodeSize()));
-  INC_STAT(isolate, total_instr_size, assembler->CodeSize());
-  INC_STAT(isolate, total_code_size, assembler->CodeSize());
+  INC_STAT(Thread::Current(), total_instr_size, assembler->CodeSize());
+  INC_STAT(Thread::Current(), total_code_size, assembler->CodeSize());
 
   // Copy the instructions into the instruction area and apply all fixups.
   // Embedded pointers are still in handles at this point.
@@ -13058,7 +13091,7 @@
     code.set_is_alive(true);
 
     // Set object pool in Instructions object.
-    INC_STAT(isolate,
+    INC_STAT(Thread::Current(),
              total_code_size, object_pool.Length() * sizeof(uintptr_t));
     instrs.set_object_pool(object_pool.raw());
 
@@ -13079,7 +13112,7 @@
     // pushed onto the stack.
     code.SetPrologueOffset(assembler->CodeSize());
   }
-  INC_STAT(isolate,
+  INC_STAT(Thread::Current(),
            total_code_size, code.comments().comments_.Length());
   return code.raw();
 }
@@ -14531,7 +14564,7 @@
     other_type_arguments = other.arguments();
   }
   return cls.IsSubtypeOf(type_arguments, other_class, other_type_arguments,
-                         bound_error);
+                         bound_error, Heap::kOld);
 }
 
 
@@ -15192,7 +15225,8 @@
 
 bool AbstractType::TypeTest(TypeTestKind test_kind,
                             const AbstractType& other,
-                            Error* bound_error) const {
+                            Error* bound_error,
+                            Heap::Space space) const {
   ASSERT(IsResolved());
   ASSERT(other.IsResolved());
   if (IsMalformed() || other.IsMalformed()) {
@@ -15261,7 +15295,8 @@
                       TypeArguments::Handle(arguments()),
                       Class::Handle(other.type_class()),
                       TypeArguments::Handle(other.arguments()),
-                      bound_error);
+                      bound_error,
+                      space);
 }
 
 
@@ -16973,7 +17008,8 @@
 }
 
 
-RawInteger* Integer::BitOp(Token::Kind kind, const Integer& other) const {
+RawInteger* Integer::BitOp(
+    Token::Kind kind, const Integer& other, Heap::Space space) const {
   if (IsSmi() && other.IsSmi()) {
     intptr_t op1_value = Smi::Value(Smi::RawCast(raw()));
     intptr_t op2_value = Smi::Value(Smi::RawCast(other.raw()));
@@ -16998,11 +17034,11 @@
     int64_t b = other.AsInt64Value();
     switch (kind) {
       case Token::kBIT_AND:
-        return Integer::New(a & b);
+        return Integer::New(a & b, space);
       case Token::kBIT_OR:
-        return Integer::New(a | b);
+        return Integer::New(a | b, space);
       case Token::kBIT_XOR:
-        return Integer::New(a ^ b);
+        return Integer::New(a ^ b, space);
       default:
         UNIMPLEMENTED();
     }
@@ -17014,6 +17050,7 @@
 // TODO(srdjan): Clarify handling of negative right operand in a shift op.
 RawInteger* Smi::ShiftOp(Token::Kind kind,
                          const Smi& other,
+                         Heap::Space space,
                          const bool silent) const {
   intptr_t result = 0;
   const intptr_t left_value = Value();
@@ -17028,10 +17065,10 @@
         int cnt = Utils::BitLength(left_value);
         if ((cnt + right_value) > Smi::kBits) {
           if ((cnt + right_value) > Mint::kBits) {
-            return Bigint::NewFromShiftedInt64(left_value, right_value);
+            return Bigint::NewFromShiftedInt64(left_value, right_value, space);
           } else {
             int64_t left_64 = left_value;
-            return Integer::New(left_64 << right_value, Heap::kNew, silent);
+            return Integer::New(left_64 << right_value, space, silent);
           }
         }
       }
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 369f87a..9d1326e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -1133,24 +1133,28 @@
   bool IsSubtypeOf(const TypeArguments& type_arguments,
                    const Class& other,
                    const TypeArguments& other_type_arguments,
-                   Error* bound_error) const {
+                   Error* bound_error,
+                   Heap::Space space = Heap::kNew) const {
     return TypeTest(kIsSubtypeOf,
                     type_arguments,
                     other,
                     other_type_arguments,
-                    bound_error);
+                    bound_error,
+                    space);
   }
 
   // Check the 'more specific' relationship.
   bool IsMoreSpecificThan(const TypeArguments& type_arguments,
                           const Class& other,
                           const TypeArguments& other_type_arguments,
-                          Error* bound_error) const {
+                          Error* bound_error,
+                          Heap::Space space = Heap::kNew) const {
     return TypeTest(kIsMoreSpecificThan,
                     type_arguments,
                     other,
                     other_type_arguments,
-                    bound_error);
+                    bound_error,
+                    space);
   }
 
   // Check if this is the top level class.
@@ -1490,7 +1494,8 @@
                 const TypeArguments& type_arguments,
                 const Class& other,
                 const TypeArguments& other_type_arguments,
-                Error* bound_error) const;
+                Error* bound_error,
+                Heap::Space space) const;
 
   static bool TypeTestNonRecursive(
       const Class& cls,
@@ -1498,7 +1503,8 @@
       const TypeArguments& type_arguments,
       const Class& other,
       const TypeArguments& other_type_arguments,
-      Error* bound_error);
+      Error* bound_error,
+      Heap::Space space);
 
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Class, Object);
   friend class AbstractType;
@@ -1591,8 +1597,9 @@
   bool IsSubtypeOf(const TypeArguments& other,
                    intptr_t from_index,
                    intptr_t len,
-                   Error* bound_error) const {
-    return TypeTest(kIsSubtypeOf, other, from_index, len, bound_error);
+                   Error* bound_error,
+                   Heap::Space space = Heap::kNew) const {
+    return TypeTest(kIsSubtypeOf, other, from_index, len, bound_error, space);
   }
 
   // Check the 'more specific' relationship, considering only a subvector of
@@ -1600,8 +1607,10 @@
   bool IsMoreSpecificThan(const TypeArguments& other,
                           intptr_t from_index,
                           intptr_t len,
-                          Error* bound_error) const {
-    return TypeTest(kIsMoreSpecificThan, other, from_index, len, bound_error);
+                          Error* bound_error,
+                          Heap::Space space = Heap::kNew) const {
+    return TypeTest(kIsMoreSpecificThan,
+        other, from_index, len, bound_error, space);
   }
 
   // Check if the vectors are equal (they may be null).
@@ -1713,7 +1722,8 @@
                 const TypeArguments& other,
                 intptr_t from_index,
                 intptr_t len,
-                Error* bound_error) const;
+                Error* bound_error,
+                Heap::Space space) const;
 
   // Return the internal or public name of a subvector of this type argument
   // vector, e.g. "<T, dynamic, List<T>, int>".
@@ -2424,12 +2434,14 @@
   bool IsSubtypeOf(const TypeArguments& type_arguments,
                    const Function& other,
                    const TypeArguments& other_type_arguments,
-                   Error* bound_error) const {
+                   Error* bound_error,
+                   Heap::Space space = Heap::kNew) const {
     return TypeTest(kIsSubtypeOf,
                     type_arguments,
                     other,
                     other_type_arguments,
-                    bound_error);
+                    bound_error,
+                    space);
   }
 
   // Returns true if the type of this function is more specific than the type of
@@ -2437,12 +2449,14 @@
   bool IsMoreSpecificThan(const TypeArguments& type_arguments,
                           const Function& other,
                           const TypeArguments& other_type_arguments,
-                          Error* bound_error) const {
+                          Error* bound_error,
+                   Heap::Space space = Heap::kNew) const {
     return TypeTest(kIsMoreSpecificThan,
                     type_arguments,
                     other,
                     other_type_arguments,
-                    bound_error);
+                    bound_error,
+                    space);
   }
 
   // Returns true if this function represents an explicit getter function.
@@ -2727,7 +2741,8 @@
                 const TypeArguments& type_arguments,
                 const Function& other,
                 const TypeArguments& other_type_arguments,
-                Error* bound_error) const;
+                Error* bound_error,
+                Heap::Space space) const;
 
   // Checks the type of the formal parameter at the given position for
   // subtyping or 'more specific' relationship between the type of this function
@@ -2738,7 +2753,8 @@
                          const TypeArguments& type_arguments,
                          const Function& other,
                          const TypeArguments& other_type_arguments,
-                         Error* bound_error) const;
+                         Error* bound_error,
+                         Heap::Space space) const;
 
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Function, Object);
   friend class Class;
@@ -2831,10 +2847,11 @@
   }
 
   inline intptr_t Offset() const;
-  inline void SetOffset(intptr_t value_in_bytes) const;
+  inline void SetOffset(intptr_t offset_in_bytes) const;
 
-  RawInstance* value() const;
-  void set_value(const Instance& value) const;
+  inline RawInstance* StaticValue() const;
+  inline void SetStaticValue(const Instance& value,
+                             bool save_initial_value = false) const;
 
   RawClass* owner() const;
   RawClass* origin() const;  // Either mixin class, or same as owner().
@@ -2858,7 +2875,12 @@
   // owner of the clone is new_owner.
   RawField* Clone(const Class& new_owner) const;
 
-  static intptr_t value_offset() { return OFFSET_OF(RawField, value_); }
+  static intptr_t instance_field_offset() {
+    return OFFSET_OF(RawField, value_.offset_);
+  }
+  static intptr_t static_value_offset() {
+    return OFFSET_OF(RawField, value_.static_value_);
+  }
 
   static intptr_t kind_bits_offset() { return OFFSET_OF(RawField, kind_bits_); }
 
@@ -2969,10 +2991,16 @@
 
   void EvaluateInitializer() const;
 
-  RawFunction* initializer() const {
-    return raw_ptr()->initializer_;
+  RawFunction* PrecompiledInitializer() const {
+    return raw_ptr()->initializer_.precompiled_;
   }
-  void set_initializer(const Function& initializer) const;
+  void SetPrecompiledInitializer(const Function& initializer) const;
+  bool HasPrecompiledInitializer() const;
+
+  RawInstance* SavedInitialStaticValue() const {
+    return raw_ptr()->initializer_.saved_value_;
+  }
+  void SetSavedInitialStaticValue(const Instance& value) const;
 
   // For static fields only. Constructs a closure that gets/sets the
   // field value.
@@ -2983,8 +3011,12 @@
   // Constructs getter and setter names for fields and vice versa.
   static RawString* GetterName(const String& field_name);
   static RawString* GetterSymbol(const String& field_name);
+  // Returns String::null() if getter symbol does not exist.
+  static RawString* LookupGetterSymbol(const String& field_name);
   static RawString* SetterName(const String& field_name);
   static RawString* SetterSymbol(const String& field_name);
+  // Returns String::null() if setter symbol does not exist.
+  static RawString* LookupSetterSymbol(const String& field_name);
   static RawString* NameFromGetter(const String& getter_name);
   static RawString* NameFromSetter(const String& setter_name);
   static bool IsGetterName(const String& function_name);
@@ -3044,6 +3076,7 @@
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Field, Object);
   friend class Class;
   friend class HeapProfiler;
+  friend class RawField;
 };
 
 
@@ -5115,21 +5148,25 @@
   bool IsFunctionType() const;
 
   // Check the subtype relationship.
-  bool IsSubtypeOf(const AbstractType& other, Error* bound_error) const {
-    return TypeTest(kIsSubtypeOf, other, bound_error);
+  bool IsSubtypeOf(const AbstractType& other,
+                   Error* bound_error,
+                   Heap::Space space = Heap::kNew) const {
+    return TypeTest(kIsSubtypeOf, other, bound_error, space);
   }
 
   // Check the 'more specific' relationship.
   bool IsMoreSpecificThan(const AbstractType& other,
-                          Error* bound_error) const {
-    return TypeTest(kIsMoreSpecificThan, other, bound_error);
+                          Error* bound_error,
+                          Heap::Space space = Heap::kNew) const {
+    return TypeTest(kIsMoreSpecificThan, other, bound_error, space);
   }
 
  private:
   // Check the subtype or 'more specific' relationship.
   bool TypeTest(TypeTestKind test_kind,
                 const AbstractType& other,
-                Error* bound_error) const;
+                Error* bound_error,
+                Heap::Space space) const;
 
   // Return the internal or public name of this type, including the names of its
   // type arguments, if any.
@@ -5616,7 +5653,9 @@
   RawInteger* ArithmeticOp(Token::Kind operation,
                            const Integer& other,
                            Heap::Space space = Heap::kNew) const;
-  RawInteger* BitOp(Token::Kind operation, const Integer& other) const;
+  RawInteger* BitOp(Token::Kind operation,
+                    const Integer& other,
+                    Heap::Space space = Heap::kNew) const;
 
   // Returns true if the Integer does not fit in a Javascript integer.
   bool CheckJavascriptIntegerOverflow() const;
@@ -5677,6 +5716,7 @@
 
   RawInteger* ShiftOp(Token::Kind kind,
                       const Smi& other,
+                      Heap::Space space = Heap::kNew,
                       const bool silent = false) const;
 
   void operator=(RawSmi* value) {
@@ -7968,17 +8008,33 @@
 
 
 intptr_t Field::Offset() const {
-  ASSERT(!is_static());  // Offset is valid only for instance fields.
-  intptr_t value = Smi::Value(reinterpret_cast<RawSmi*>(raw_ptr()->value_));
+  ASSERT(!is_static());  // Valid only for dart instance fields.
+  intptr_t value = Smi::Value(raw_ptr()->value_.offset_);
   return (value * kWordSize);
 }
 
 
-void Field::SetOffset(intptr_t value_in_bytes) const {
-  ASSERT(!is_static());  // SetOffset is valid only for instance fields.
+void Field::SetOffset(intptr_t offset_in_bytes) const {
+  ASSERT(!is_static());  // Valid only for dart instance fields.
   ASSERT(kWordSize != 0);
-  StorePointer(&raw_ptr()->value_,
-               static_cast<RawInstance*>(Smi::New(value_in_bytes / kWordSize)));
+  StorePointer(&raw_ptr()->value_.offset_,
+               Smi::New(offset_in_bytes / kWordSize));
+}
+
+
+RawInstance* Field::StaticValue() const {
+  ASSERT(is_static());  // Valid only for static dart fields.
+  return raw_ptr()->value_.static_value_;
+}
+
+
+void Field::SetStaticValue(const Instance& value,
+                           bool save_initial_value) const {
+  ASSERT(is_static());  // Valid only for static dart fields.
+  StorePointer(&raw_ptr()->value_.static_value_, value.raw());
+  if (save_initial_value) {
+    StorePointer(&raw_ptr()->initializer_.saved_value_, value.raw());
+  }
 }
 
 
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 5e006a0..692a03b 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -4779,6 +4779,14 @@
     const String* data[3] = { &str1, &Symbols::Dot(), &str2 };
     CheckConcatAll(data, 3);
   }
+
+  {
+    const String& empty = String::Handle(String::New(""));
+    const String* data[3] = { &Symbols::FallThroughError(),
+                              &empty,
+                              &Symbols::isPaused() };
+    CheckConcatAll(data, 3);
+  }
 }
 
 }  // namespace dart
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 604e453..d837830 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -466,8 +466,6 @@
 
 
 Token::Kind Parser::LookaheadToken(int num_tokens) {
-  INC_STAT(I, num_tokens_lookahead, 1);
-  INC_STAT(I, num_token_checks, 1);
   return tokens_iterator_.LookaheadTokenKind(num_tokens);
 }
 
@@ -804,25 +802,26 @@
 
 
 void Parser::ParseClass(const Class& cls) {
+  Thread* thread = Thread::Current();
+  Zone* zone = thread->zone();
+  const int64_t num_tokes_before = STAT_VALUE(thread, num_tokens_consumed);
   if (!cls.is_synthesized_class()) {
-    Thread* thread = Thread::Current();
-    Zone* zone = thread->zone();
-    CSTAT_TIMER_SCOPE(thread, parser_timer);
     ASSERT(thread->long_jump_base()->IsSafeToJump());
+    CSTAT_TIMER_SCOPE(thread, parser_timer);
     const Script& script = Script::Handle(zone, cls.script());
     const Library& lib = Library::Handle(zone, cls.library());
     Parser parser(script, lib, cls.token_pos());
     parser.ParseClassDefinition(cls);
   } else if (cls.is_enum_class()) {
-    Thread* thread = Thread::Current();
-    Zone* zone = thread->zone();
-    CSTAT_TIMER_SCOPE(thread, parser_timer);
     ASSERT(thread->long_jump_base()->IsSafeToJump());
+    CSTAT_TIMER_SCOPE(thread, parser_timer);
     const Script& script = Script::Handle(zone, cls.script());
     const Library& lib = Library::Handle(zone, cls.library());
     Parser parser(script, lib, cls.token_pos());
     parser.ParseEnumDefinition(cls);
   }
+  const int64_t num_tokes_after = STAT_VALUE(thread, num_tokens_consumed);
+  INC_STAT(thread, num_class_tokens, num_tokes_after - num_tokes_before);
 }
 
 
@@ -909,10 +908,9 @@
 void Parser::ParseFunction(ParsedFunction* parsed_function) {
   Thread* thread = parsed_function->thread();
   ASSERT(thread == Thread::Current());
-  Isolate* isolate = thread->isolate();
   Zone* zone = thread->zone();
   CSTAT_TIMER_SCOPE(thread, parser_timer);
-  INC_STAT(isolate, num_functions_compiled, 1);
+  INC_STAT(thread, num_functions_parsed, 1);
   VMTagScope tagScope(thread, VMTag::kCompileParseFunctionTagId,
                       FLAG_profile_vm);
 
@@ -956,10 +954,11 @@
       break;
     case RawFunction::kImplicitStaticFinalGetter:
       node_sequence = parser.ParseStaticFinalGetter(func);
-      INC_STAT(isolate, num_implicit_final_getters, 1);
+      INC_STAT(thread, num_implicit_final_getters, 1);
       break;
     case RawFunction::kMethodExtractor:
       node_sequence = parser.ParseMethodExtractor(func);
+      INC_STAT(thread, num_method_extractors, 1);
       break;
     case RawFunction::kNoSuchMethodDispatcher:
       node_sequence =
@@ -1051,7 +1050,7 @@
 RawArray* Parser::EvaluateMetadata() {
   CheckToken(Token::kAT, "Metadata character '@' expected");
   GrowableObjectArray& meta_values =
-      GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+      GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
   while (CurrentToken() == Token::kAT) {
     ConsumeToken();
     intptr_t expr_pos = TokenPos();
@@ -1120,7 +1119,7 @@
       ReportError(expr_pos, "expression must be a compile-time constant");
     }
     const Instance& val = EvaluateConstExpr(expr_pos, expr);
-    meta_values.Add(val);
+    meta_values.Add(val, Heap::kOld);
   }
   return Array::MakeArray(meta_values);
 }
@@ -1152,15 +1151,21 @@
   String& init_name = String::Handle(zone,
       Symbols::FromConcat(Symbols::InitPrefix(), field_name));
 
+  Object& initializer_owner = Object::Handle(field.owner());
+  if (field.owner() != field.origin()) {
+    initializer_owner =
+        PatchClass::New(Class::Handle(field.owner()), script_cls);
+  }
+
   const Function& initializer = Function::ZoneHandle(zone,
       Function::New(init_name,
-                    RawFunction::kRegularFunction,
+                    RawFunction::kImplicitStaticFinalGetter,
                     true,   // static
                     false,  // !const
                     false,  // !abstract
                     false,  // !external
                     false,  // !native
-                    Class::Handle(field.owner()),
+                    initializer_owner,
                     field.token_pos()));
   initializer.set_result_type(AbstractType::Handle(zone, field.type()));
   // Static initializer functions are hidden from the user.
@@ -1192,47 +1197,6 @@
 }
 
 
-RawObject* Parser::ParseFunctionFromSource(const Class& owning_class,
-                                           const String& source) {
-  Thread* thread = Thread::Current();
-  Isolate* isolate = thread->isolate();
-  StackZone stack_zone(thread);
-  LongJumpScope jump;
-  if (setjmp(*jump.Set()) == 0) {
-    const String& uri = String::Handle(Symbols::New("dynamically-added"));
-    const Script& script = Script::Handle(
-        Script::New(uri, source, RawScript::kSourceTag));
-    const Library& owning_library = Library::Handle(owning_class.library());
-    const String& private_key = String::Handle(owning_library.private_key());
-    script.Tokenize(private_key);
-    const intptr_t token_pos = 0;
-    Parser parser(script, owning_library, token_pos);
-    parser.is_top_level_ = true;
-    parser.set_current_class(owning_class);
-    const String& class_name = String::Handle(owning_class.Name());
-    ClassDesc members(stack_zone.GetZone(),
-                      owning_class,
-                      class_name,
-                      false,  /* is_interface */
-                      token_pos);
-    const intptr_t metadata_pos = parser.SkipMetadata();
-    parser.ParseClassMemberDefinition(&members, metadata_pos);
-    ASSERT(members.functions().length() == 1);
-    const Function& func = *members.functions().At(0);
-    func.set_eval_script(script);
-    ParsedFunction* parsed_function = new ParsedFunction(thread, func);
-    Parser::ParseFunction(parsed_function);
-    return func.raw();
-  } else {
-    const Error& error = Error::Handle(isolate->object_store()->sticky_error());
-    isolate->object_store()->clear_sticky_error();
-    return error.raw();
-  }
-  UNREACHABLE();
-  return Object::null();
-}
-
-
 SequenceNode* Parser::ParseStaticFinalGetter(const Function& func) {
   TRACE_PARSER("ParseStaticFinalGetter");
   ParamList params;
@@ -1249,6 +1213,7 @@
   const Class& field_class = Class::Handle(Z, func.Owner());
   const Field& field =
       Field::ZoneHandle(Z, field_class.LookupStaticField(field_name));
+  ASSERT(!field.IsNull());
 
   // Static final fields must have an initializer.
   ExpectToken(Token::kASSIGN);
@@ -1576,7 +1541,7 @@
 
   if (desc.NamedCount() > 0) {
     const Array& arg_names =
-        Array::ZoneHandle(Z, Array::New(desc.NamedCount()));
+        Array::ZoneHandle(Z, Array::New(desc.NamedCount(), Heap::kOld));
     for (intptr_t i = 0; i < arg_names.Length(); ++i) {
       arg_names.SetAt(i, String::Handle(Z, desc.NameAt(i)));
     }
@@ -1640,7 +1605,7 @@
     function_object = receiver;
   } else {
     const String& getter_name = String::ZoneHandle(Z,
-        Symbols::New(String::Handle(Z, Field::GetterName(name))));
+        Symbols::New(String::Handle(Z, Field::GetterSymbol(name))));
     function_object = new(Z) InstanceCallNode(
         token_pos, receiver, getter_name, no_args);
   }
@@ -2330,14 +2295,18 @@
   AstNode* implicit_argument = LoadReceiver(field_pos);
 
   const String& getter_name =
-      String::ZoneHandle(Z, Field::GetterName(field_name));
-  const Function& super_getter = Function::ZoneHandle(Z,
-      Resolver::ResolveDynamicAnyArgs(super_class, getter_name));
+      String::ZoneHandle(Z, Field::LookupGetterSymbol(field_name));
+  Function& super_getter = Function::ZoneHandle(Z);
+  if (!getter_name.IsNull()) {
+    super_getter = Resolver::ResolveDynamicAnyArgs(super_class, getter_name);
+  }
   if (super_getter.IsNull()) {
     const String& setter_name =
-        String::ZoneHandle(Z, Field::SetterName(field_name));
-    const Function& super_setter = Function::ZoneHandle(Z,
-        Resolver::ResolveDynamicAnyArgs(super_class, setter_name));
+        String::ZoneHandle(Z, Field::LookupSetterSymbol(field_name));
+    Function& super_setter = Function::ZoneHandle(Z);
+    if (!setter_name.IsNull()) {
+      super_setter = Resolver::ResolveDynamicAnyArgs(super_class, setter_name);
+    }
     if (super_setter.IsNull()) {
       // Check if this is an access to an implicit closure using 'super'.
       // If a function exists of the specified field_name then try
@@ -2584,9 +2553,12 @@
   } else {
     init_expr = ParseExpr(kAllowConst, kConsumeCascades);
     if (init_expr->EvalConstExpr() != NULL) {
-      init_expr =
-          new LiteralNode(field.token_pos(),
-                          EvaluateConstExpr(expr_pos, init_expr));
+      Instance& expr_value = Instance::ZoneHandle(Z);
+      if (!GetCachedConstant(expr_pos, &expr_value)) {
+        expr_value = EvaluateConstExpr(expr_pos, init_expr).raw();
+        CacheConstantValue(expr_pos, expr_value);
+      }
+      init_expr = new(Z) LiteralNode(field.token_pos(), expr_value);
     }
   }
   set_current_class(saved_class);
@@ -2630,8 +2602,12 @@
           intptr_t expr_pos = TokenPos();
           init_expr = ParseExpr(kAllowConst, kConsumeCascades);
           if (init_expr->EvalConstExpr() != NULL) {
-            init_expr = new LiteralNode(field.token_pos(),
-                                        EvaluateConstExpr(expr_pos, init_expr));
+            Instance& expr_value = Instance::ZoneHandle(Z);
+            if (!GetCachedConstant(expr_pos, &expr_value)) {
+              expr_value = EvaluateConstExpr(expr_pos, init_expr).raw();
+              CacheConstantValue(expr_pos, expr_value);
+            }
+            init_expr = new(Z) LiteralNode(field.token_pos(), expr_value);
           }
         }
       }
@@ -3769,13 +3745,16 @@
       }
       ConsumeToken();  // Colon.
       ExpectToken(Token::kTHIS);
-      String& redir_name = String::ZoneHandle(Z,
-          String::Concat(members->class_name(), Symbols::Dot()));
+      GrowableHandlePtrArray<const String> pieces(Z, 3);
+      pieces.Add(members->class_name());
+      pieces.Add(Symbols::Dot());
       if (CurrentToken() == Token::kPERIOD) {
         ConsumeToken();
-        redir_name = String::Concat(redir_name,
-            *ExpectIdentifier("constructor name expected"));
+        pieces.Add(*ExpectIdentifier("constructor name expected"));
       }
+      String& redir_name =
+          String::ZoneHandle(Z, Symbols::FromConcatAll(pieces));
+
       method->redirect_name = &redir_name;
       CheckToken(Token::kLPAREN);
       SkipToMatchingParenthesis();
@@ -4050,7 +4029,7 @@
     // For static final fields (this includes static const fields), set value to
     // "uninitialized" and create a kImplicitStaticFinalGetter getter method.
     if (field->has_static && has_initializer) {
-      class_field.set_value(init_value);
+      class_field.SetStaticValue(init_value, true);
       if (!has_simple_literal) {
         String& getter_name =
             String::Handle(Z, Field::GetterSymbol(*field->name));
@@ -4632,7 +4611,7 @@
 
 void Parser::ParseClassDefinition(const Class& cls) {
   TRACE_PARSER("ParseClassDefinition");
-  INC_STAT(I, num_classes_compiled, 1);
+  INC_STAT(thread(), num_classes_parsed, 1);
   set_current_class(cls);
   is_top_level_ = true;
   String& class_name = String::Handle(Z, cls.Name());
@@ -4692,7 +4671,7 @@
 
 void Parser::ParseEnumDefinition(const Class& cls) {
   TRACE_PARSER("ParseEnumDefinition");
-  INC_STAT(I, num_classes_compiled, 1);
+  INC_STAT(thread(), num_classes_parsed, 1);
 
   SkipMetadata();
   ExpectToken(Token::kENUM);
@@ -4789,7 +4768,7 @@
     // Initialize the field with the ordinal value. It will be patched
     // later with the enum constant instance.
     const Smi& ordinal_value = Smi::Handle(Z, Smi::New(i));
-    enum_value.set_value(ordinal_value);
+    enum_value.SetStaticValue(ordinal_value, true);
     enum_value.RecordStore(ordinal_value);
     i++;
 
@@ -4827,7 +4806,7 @@
   // Allocate the immutable array containing the enumeration values.
   // The actual enum instance values will be patched in later.
   const Array& values_array = Array::Handle(Z, Array::New(i, Heap::kOld));
-  values_field.set_value(values_array);
+  values_field.SetStaticValue(values_array, true);
   values_field.RecordStore(values_array);
 
   // Create a static field that contains the list of enumeration names.
@@ -4838,7 +4817,7 @@
   names_field = names_field.Clone(cls);
   enum_members.AddField(names_field);
   const Array& names_array = Array::Handle(Array::MakeArray(enum_names));
-  names_field.set_value(names_array);
+  names_field.SetStaticValue(names_array, true);
   names_field.RecordStore(names_array);
 
   // Clone the toString() function from the helper class.
@@ -5431,7 +5410,7 @@
     field = Field::New(var_name, is_static, is_final, is_const, is_reflectable,
                        current_class(), name_pos);
     field.set_type(type);
-    field.set_value(Object::null_instance());
+    field.SetStaticValue(Object::null_instance(), true);
     top_level->AddField(field);
     library_.AddObject(field, var_name);
     if (metadata_pos >= 0) {
@@ -5445,7 +5424,7 @@
         has_simple_literal = IsSimpleLiteral(type, &field_value);
       }
       SkipExpr();
-      field.set_value(field_value);
+      field.SetStaticValue(field_value, true);
       field.set_has_initializer(true);
 
       if (!has_simple_literal) {
@@ -5818,13 +5797,14 @@
   ConsumeToken();
   String& lib_name = *ExpectIdentifier("library name expected");
   if (CurrentToken() == Token::kPERIOD) {
+    GrowableHandlePtrArray<const String> pieces(Z, 3);
+    pieces.Add(lib_name);
     while (CurrentToken() == Token::kPERIOD) {
       ConsumeToken();
-      lib_name = String::Concat(lib_name, Symbols::Dot());
-      lib_name = String::Concat(lib_name,
-          *ExpectIdentifier("malformed library name"));
+      pieces.Add(Symbols::Dot());
+      pieces.Add(*ExpectIdentifier("malformed library name"));
     }
-    lib_name = Symbols::New(lib_name);
+    lib_name = Symbols::FromConcatAll(pieces);
   }
   library_.SetName(lib_name);
   ExpectSemicolon();
@@ -6347,7 +6327,7 @@
   }
 
   const GrowableObjectArray& handler_types =
-      GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+      GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
   handler_types.Add(dynamic_type);  // Catch block handles all exceptions.
 
   CatchClauseNode* catch_clause = new(Z) CatchClauseNode(
@@ -6464,7 +6444,7 @@
   SequenceNode* catch_handler_list = CloseBlock();
 
   const GrowableObjectArray& handler_types =
-      GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+      GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
   handler_types.SetLength(0);
   handler_types.Add(*exception_param.type);
 
@@ -6709,9 +6689,9 @@
     const String& async_func_name =
         String::Handle(Z, innermost_function().name());
     String& closure_name = String::Handle(Z,
-        String::NewFormatted("<%s_async_body>", async_func_name.ToCString()));
+        Symbols::NewFormatted("<%s_async_body>", async_func_name.ToCString()));
     closure = Function::NewClosureFunction(
-        String::Handle(Z, Symbols::New(closure_name)),
+        closure_name,
         innermost_function(),
         async_func_pos);
     closure.set_is_generated_body(true);
@@ -7670,6 +7650,7 @@
 
   // Parse the local function.
   SequenceNode* statements = Parser::ParseFunc(function);
+  INC_STAT(thread(), num_functions_parsed, 1);
 
   // Now that the local function has formal parameters, lookup the signature
   // class in the current library (but not in its imports) and only create a new
@@ -8497,11 +8478,10 @@
 static LocalVariable* LookupAsyncSavedTryContextVar(LocalScope* scope,
                                                     uint16_t try_index) {
   const String& async_saved_try_ctx_name =
-      String::ZoneHandle(Symbols::New(String::Handle(
-          String::NewFormatted(
-              "%s%d",
-              Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
-              try_index))));
+      String::ZoneHandle(Symbols::NewFormatted(
+          "%s%d",
+          Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
+          try_index));
   LocalVariable* var = scope->LocalLookupVariable(async_saved_try_ctx_name);
   ASSERT(var != NULL);
   return var;
@@ -8573,9 +8553,8 @@
       Z, lib.LookupFunctionAllowPrivate(Symbols::print()));
   ASSERT(!print_fn.IsNull());
   ArgumentListNode* one_arg = new(Z) ArgumentListNode(Scanner::kNoSourcePos);
-  String& msg = String::Handle(String::NewFormatted("%s", str));
-  one_arg->Add(new(Z) LiteralNode(Scanner::kNoSourcePos,
-               String::ZoneHandle(Symbols::New(msg))));
+  String& msg = String::ZoneHandle(Symbols::NewFormatted("%s", str));
+  one_arg->Add(new(Z) LiteralNode(Scanner::kNoSourcePos, msg));
   AstNode* print_call =
       new(Z) StaticCallNode(Scanner::kNoSourcePos, print_fn, one_arg);
   return print_call;
@@ -9525,10 +9504,9 @@
 
 void Parser::SetupSavedTryContext(LocalVariable* saved_try_context) {
   const String& async_saved_try_ctx_name = String::ZoneHandle(Z,
-      Symbols::New(String::Handle(Z,
-          String::NewFormatted("%s%d",
-                               Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
-                               last_used_try_index_ - 1))));
+      Symbols::NewFormatted("%s%d",
+                           Symbols::AsyncSavedTryCtxVarPrefix().ToCString(),
+                           last_used_try_index_ - 1));
   LocalVariable* async_saved_try_ctx = new (Z) LocalVariable(
       Scanner::kNoSourcePos,
       async_saved_try_ctx_name,
@@ -9670,7 +9648,7 @@
   try_stack_->enter_catch();
   const intptr_t handler_pos = TokenPos();
   const GrowableObjectArray& handler_types =
-      GrowableObjectArray::Handle(Z, GrowableObjectArray::New());
+      GrowableObjectArray::Handle(Z, GrowableObjectArray::New(Heap::kOld));
   bool needs_stack_trace = false;
   SequenceNode* catch_handler_list =
       ParseCatchClauses(handler_pos,
@@ -10789,10 +10767,11 @@
     const Field& field = expr->AsLoadStaticFieldNode()->field();
     if (field.is_const() &&
         !expr->AsLoadStaticFieldNode()->is_deferred_reference()) {
-      ASSERT(field.value() != Object::sentinel().raw());
-      ASSERT(field.value() != Object::transition_sentinel().raw());
-      return new(zone) LiteralNode(expr->token_pos(),
-                                   Instance::ZoneHandle(zone, field.value()));
+      ASSERT(field.StaticValue() != Object::sentinel().raw());
+      ASSERT(field.StaticValue() != Object::transition_sentinel().raw());
+      return new(zone) LiteralNode(
+          expr->token_pos(),
+          Instance::ZoneHandle(zone, field.StaticValue()));
     }
   }
   return expr;
@@ -11161,7 +11140,8 @@
   ASSERT(field.is_static());
   const Class& field_owner = Class::ZoneHandle(Z, field.owner());
   const String& field_name = String::ZoneHandle(Z, field.name());
-  const String& getter_name = String::Handle(Z, Field::GetterName(field_name));
+  const String& getter_name =
+      String::Handle(Z, Field::GetterSymbol(field_name));
   const Function& getter = Function::Handle(Z,
       field_owner.LookupStaticFunction(getter_name));
   // Never load field directly if there is a getter (deterministic AST).
@@ -11556,7 +11536,7 @@
       is_setter_name = true;
     }
   } else if (Token::CanBeOverloaded(CurrentToken())) {
-    extractor_name = String::New(Token::Str(CurrentToken()));
+    extractor_name = Symbols::New(Token::Str(CurrentToken()));
     ConsumeToken();
   } else {
     ReportError("identifier or operator expected");
@@ -11670,10 +11650,13 @@
   }
 
   // Closurization of instance getter, setter, method or operator.
+  GrowableHandlePtrArray<const String> pieces(Z, 3);
+  pieces.Add(Symbols::HashMark());
   if (is_setter_name) {
-    extractor_name = String::Concat(Symbols::SetterPrefix(), extractor_name);
+    pieces.Add(Symbols::SetterPrefix());
   }
-  extractor_name = Symbols::FromConcat(Symbols::HashMark(), extractor_name);
+  pieces.Add(extractor_name);
+  extractor_name = Symbols::FromConcatAll(pieces);
   return new(Z) InstanceGetterNode(property_pos, primary, extractor_name);
 }
 
@@ -11928,12 +11911,17 @@
   // We don't want to allocate anything in the heap here since this code
   // is called from the optimizing compiler in the background thread. Allocate
   // the key value in the zone instead.
-  const char* key = Z->PrintToString("%s_%" Pd "",
-      String::Handle(Z, script_.url()).ToCString(),
-      token_pos);
+  // const char* key = Z->PrintToString("%s_%" Pd "",
+  //     String::Handle(Z, script_.url()).ToCString(),
+  //    token_pos);
+
+  const String& key = String::Handle(Z,
+      Symbols::NewFormatted("%s_%" Pd "",
+          String::Handle(Z, script_.url()).ToCString(),
+          token_pos));
   ConstantsMap constants(isolate()->object_store()->compile_time_constants());
   bool is_present = false;
-  *value ^= constants.GetOrNull<const char *>(key, &is_present);
+  *value ^= constants.GetOrNull(key, &is_present);
   ASSERT(constants.Release().raw() ==
       isolate()->object_store()->compile_time_constants());
   if (FLAG_compiler_stats && is_present) {
@@ -11966,10 +11954,11 @@
   ASSERT(field.is_static());
   const Class& field_owner = Class::ZoneHandle(Z, field.owner());
   const String& field_name = String::ZoneHandle(Z, field.name());
-  const String& getter_name = String::Handle(Z, Field::GetterName(field_name));
+  const String& getter_name =
+      String::Handle(Z, Field::GetterSymbol(field_name));
   const Function& getter = Function::Handle(Z,
       field_owner.LookupStaticFunction(getter_name));
-  const Instance& value = Instance::Handle(Z, field.value());
+  const Instance& value = Instance::Handle(Z, field.StaticValue());
   if (value.raw() == Object::transition_sentinel().raw()) {
     if (field.is_const()) {
       ReportError("circular dependency while initializing static field '%s'",
@@ -11984,7 +11973,7 @@
     // not been evaluated. If the field is const, call the static getter method
     // to evaluate the expression and canonicalize the value.
     if (field.is_const()) {
-      field.set_value(Object::transition_sentinel());
+      field.SetStaticValue(Object::transition_sentinel());
       const int kNumArguments = 0;  // no arguments.
       const Function& func = Function::Handle(Z,
           Resolver::ResolveStatic(field_owner,
@@ -12005,7 +11994,7 @@
           // generated AST is not deterministic. Therefore mark the function as
           // not optimizable.
           current_function().SetIsOptimizable(false);
-          field.set_value(Object::null_instance());
+          field.SetStaticValue(Object::null_instance());
           // It is a compile-time error if evaluation of a compile-time constant
           // would raise an exception.
           const String& field_name = String::Handle(Z, field.name());
@@ -12022,7 +12011,7 @@
       Instance& instance = Instance::Handle(Z);
       instance ^= const_value.raw();
       instance = TryCanonicalize(instance, field_ref_pos);
-      field.set_value(instance);
+      field.SetStaticValue(instance);
       return NULL;   // Constant
     } else {
       return new(Z) StaticGetterNode(
@@ -12048,7 +12037,8 @@
   // Constructors have 2 extra arguments: rcvr and construction phase.
   const int kNumExtraArgs = constructor.IsFactory() ? 1 : 2;
   const int num_arguments = arguments->length() + kNumExtraArgs;
-  const Array& arg_values = Array::Handle(Z, Array::New(num_arguments));
+  const Array& arg_values =
+      Array::Handle(Z, Array::New(num_arguments, Heap::kOld));
   Instance& instance = Instance::Handle(Z);
   if (!constructor.IsFactory()) {
     instance = Instance::New(type_class, Heap::kOld);
@@ -13016,29 +13006,36 @@
   ASSERT(CurrentToken() == Token::kHASH);
   ConsumeToken();
   intptr_t symbol_pos = TokenPos();
-  String& symbol = String::Handle(Z);
+  String& symbol = String::ZoneHandle(Z);
   if (IsIdentifier()) {
     symbol = CurrentLiteral()->raw();
     ConsumeToken();
+    GrowableHandlePtrArray<const String> pieces(Z, 3);
+    pieces.Add(symbol);
     while (CurrentToken() == Token::kPERIOD) {
-      symbol = String::Concat(symbol, Symbols::Dot());
+      pieces.Add(Symbols::Dot());
       ConsumeToken();
-      symbol = String::Concat(symbol,
-                              *ExpectIdentifier("identifier expected"));
+      pieces.Add(*ExpectIdentifier("identifier expected"));
     }
+    symbol = Symbols::FromConcatAll(pieces);
   } else if (Token::CanBeOverloaded(CurrentToken())) {
-    symbol = String::New(Token::Str(CurrentToken()));
+    symbol = Symbols::New(Token::Str(CurrentToken()));
     ConsumeToken();
   } else {
     ReportError("illegal symbol literal");
   }
+  ASSERT(symbol.IsSymbol());
+
+  Instance& symbol_instance = Instance::ZoneHandle(Z);
+  if (GetCachedConstant(symbol_pos, &symbol_instance)) {
+    return new(Z) LiteralNode(symbol_pos, symbol_instance);
+  }
 
   // Call Symbol class constructor to create a symbol instance.
   const Class& symbol_class = Class::Handle(I->object_store()->symbol_class());
   ASSERT(!symbol_class.IsNull());
   ArgumentListNode* constr_args = new(Z) ArgumentListNode(symbol_pos);
-  constr_args->Add(new(Z) LiteralNode(
-      symbol_pos, String::ZoneHandle(Z, Symbols::New(symbol))));
+  constr_args->Add(new(Z) LiteralNode(symbol_pos, symbol));
   const Function& constr = Function::ZoneHandle(Z,
       symbol_class.LookupConstructor(Symbols::SymbolCtor()));
   ASSERT(!constr.IsNull());
@@ -13052,9 +13049,9 @@
                  script_, symbol_pos,
                  "error executing const Symbol constructor");
   }
-  const Instance& instance = Instance::Cast(result);
-  return new(Z) LiteralNode(symbol_pos,
-                            Instance::ZoneHandle(Z, instance.raw()));
+  symbol_instance ^= result.raw();
+  CacheConstantValue(symbol_pos, symbol_instance);
+  return new(Z) LiteralNode(symbol_pos, symbol_instance);
 }
 
 
@@ -13333,7 +13330,11 @@
         // The type arguments of the redirection type are instantiated from the
         // type arguments of the parsed type of the 'new' or 'const' expression.
         Error& error = Error::Handle(Z);
-        redirect_type ^= redirect_type.InstantiateFrom(type_arguments, &error);
+        redirect_type ^= redirect_type.InstantiateFrom(
+            type_arguments,
+            &error,
+            NULL,  // trail
+            Heap::kOld);
         if (!error.IsNull()) {
           redirect_type = ClassFinalizer::NewFinalizedMalformedType(
               error,
@@ -13545,14 +13546,15 @@
   ASSERT(!func.IsNull());
 
   // Build the array of literal values to interpolate.
-  const Array& value_arr = Array::Handle(Z, Array::New(values.length()));
+  const Array& value_arr = Array::Handle(Z,
+      Array::New(values.length(), Heap::kOld));
   for (int i = 0; i < values.length(); i++) {
     ASSERT(values[i]->IsLiteralNode());
     value_arr.SetAt(i, values[i]->AsLiteralNode()->literal());
   }
 
   // Build argument array to pass to the interpolation function.
-  const Array& interpolate_arg = Array::Handle(Z, Array::New(1));
+  const Array& interpolate_arg = Array::Handle(Z, Array::New(1, Heap::kOld));
   interpolate_arg.SetAt(0, value_arr);
 
   // Call interpolation function.
@@ -13592,6 +13594,16 @@
     return primary;
   }
   // String interpolation needed.
+
+  // First, check whether we've cached a compile-time constant for this
+  // string interpolation.
+  Instance& cached_string = Instance::Handle(Z);
+  if (GetCachedConstant(literal_start, &cached_string)) {
+    SkipStringLiteral();
+    return new(Z) LiteralNode(literal_start,
+                              Instance::ZoneHandle(Z, cached_string.raw()));
+  }
+
   bool is_compiletime_const = true;
   bool has_interpolation = false;
   GrowableArray<AstNode*> values_list;
@@ -13644,7 +13656,9 @@
   }
   if (is_compiletime_const) {
     if (has_interpolation) {
-      primary = new(Z) LiteralNode(literal_start, Interpolate(values_list));
+      const String& interpolated_string = Interpolate(values_list);
+      primary = new(Z) LiteralNode(literal_start, interpolated_string);
+      CacheConstantValue(literal_start, interpolated_string);
     } else {
       GrowableHandlePtrArray<const String> pieces(Z, values_list.length());
       for (int i = 0; i < values_list.length(); i++) {
@@ -13654,6 +13668,8 @@
       }
       const String& lit = String::ZoneHandle(Z, Symbols::FromConcatAll(pieces));
       primary = new(Z) LiteralNode(literal_start, lit);
+      // Caching of constant not necessary because the symbol lookup will
+      // find the value next time.
     }
   } else {
     ArrayNode* values = new(Z) ArrayNode(
@@ -13735,9 +13751,12 @@
           // is used. We cheat a little here by looking at the next token
           // to determine whether we have an unresolved method call or
           // field access.
-          String& qualified_name = String::ZoneHandle(Z, prefix.name());
-          qualified_name = String::Concat(qualified_name, Symbols::Dot());
-          qualified_name = Symbols::FromConcat(qualified_name, ident);
+          GrowableHandlePtrArray<const String> pieces(Z, 3);
+          pieces.Add(String::Handle(Z, prefix.name()));
+          pieces.Add(Symbols::Dot());
+          pieces.Add(ident);
+          const String& qualified_name = String::ZoneHandle(Z,
+              Symbols::FromConcatAll(pieces));
           InvocationMirror::Type call_type =
               CurrentToken() == Token::kLPAREN ?
                   InvocationMirror::kMethod : InvocationMirror::kGetter;
@@ -13884,9 +13903,9 @@
     // We already checked that this field is const and has been
     // initialized.
     ASSERT(field.is_const());
-    ASSERT(field.value() != Object::sentinel().raw());
-    ASSERT(field.value() != Object::transition_sentinel().raw());
-    return Instance::ZoneHandle(Z, field.value());
+    ASSERT(field.StaticValue() != Object::sentinel().raw());
+    ASSERT(field.StaticValue() != Object::transition_sentinel().raw());
+    return Instance::ZoneHandle(Z, field.StaticValue());
   } else {
     ASSERT(expr->EvalConstExpr() != NULL);
     ReturnNode* ret = new(Z) ReturnNode(expr->token_pos(), expr);
diff --git a/runtime/vm/parser.h b/runtime/vm/parser.h
index f081a7a..24ff1ac 100644
--- a/runtime/vm/parser.h
+++ b/runtime/vm/parser.h
@@ -211,10 +211,6 @@
   // given static field.
   static ParsedFunction* ParseStaticFieldInitializer(const Field& field);
 
-  // Returns a RawFunction or RawError.
-  static RawObject* ParseFunctionFromSource(const Class& owning_class,
-                                            const String& source);
-
   // Parse a function to retrieve parameter information that is not retained in
   // the dart::Function object. Returns either an error if the parse fails
   // (which could be the case for local functions), or a flat array of entries
@@ -303,7 +299,6 @@
     if (token_kind_ == Token::kILLEGAL) {
       ComputeCurrentToken();
     }
-    INC_STAT(isolate_, num_token_checks, 1);
     return token_kind_;
   }
 
@@ -323,7 +318,7 @@
     // Reset cache and advance the token.
     token_kind_ = Token::kILLEGAL;
     tokens_iterator_.Advance();
-    INC_STAT(isolate_, num_tokens_consumed, 1);
+    INC_STAT(thread(), num_tokens_consumed, 1);
   }
   void ConsumeRightAngleBracket();
   void CheckToken(Token::Kind token_expected, const char* msg = NULL);
diff --git a/runtime/vm/precompiler.cc b/runtime/vm/precompiler.cc
index bd9b73f..c49e68c 100644
--- a/runtime/vm/precompiler.cc
+++ b/runtime/vm/precompiler.cc
@@ -434,19 +434,20 @@
   if (field.is_static()) {
     // Potential const object. Uninitialized field will harmlessly do a
     // redundant add of the Null class.
-    const Object& value = Object::Handle(Z, field.value());
+    const Object& value = Object::Handle(Z, field.StaticValue());
     const Class& cls = Class::Handle(Z, value.clazz());
     AddClass(cls);
 
     if (field.has_initializer()) {
-      if (field.initializer() != Function::null()) return;
+      if (field.PrecompiledInitializer() != Function::null()) return;
 
       if (FLAG_trace_precompiler) {
         ISL_Print("Precompiling initializer for %s\n", field.ToCString());
       }
       Compiler::CompileStaticInitializer(field);
 
-      const Function& function = Function::Handle(Z, field.initializer());
+      const Function& function =
+          Function::Handle(Z, field.PrecompiledInitializer());
       AddCalleesOf(function);
     }
   }
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 0c4fcf6..bacbfe8 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -830,9 +830,20 @@
   RawObject* owner_;  // Class or patch class or mixin class
                       // where this field is defined.
   RawAbstractType* type_;
-  RawInstance* value_;  // Offset in words for instance and value for static.
+  union {
+    RawInstance* static_value_;  // Value for static fields.
+    RawSmi* offset_;  // Offset in words for instance fields.
+  } value_;
   RawArray* dependent_code_;
-  RawFunction* initializer_;
+  union {
+    // When precompiling we need to save the static initializer function here
+    // so that code for it can be generated.
+    RawFunction* precompiled_;  // Static initializer function - precompiling.
+    // When generating script snapshots after running the application it is
+    // necessary to save the initial value of static fields so that we can
+    // restore the value back to the original initial value.
+    RawInstance* saved_value_;  // Saved initial value - static fields.
+  } initializer_;
   RawSmi* guarded_list_length_;
   RawObject** to() {
     return reinterpret_cast<RawObject**>(&ptr()->guarded_list_length_);
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index ff8c438..13791de 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -747,9 +747,25 @@
   writer->Write<int32_t>(ptr()->is_nullable_);
   writer->Write<uint8_t>(ptr()->kind_bits_);
 
-  // Write out all the object pointer fields.
-  SnapshotWriterVisitor visitor(writer);
-  visitor.VisitPointers(from(), to());
+  // Write out the name.
+  writer->WriteObjectImpl(ptr()->name_, kAsReference);
+  // Write out the owner.
+  writer->WriteObjectImpl(ptr()->owner_, kAsReference);
+  // Write out the type.
+  writer->WriteObjectImpl(ptr()->type_, kAsReference);
+  // Write out the initial static value or field offset.
+  if (Field::StaticBit::decode(ptr()->kind_bits_)) {
+    // For static field we write out the initial static value.
+    writer->WriteObjectImpl(ptr()->initializer_.saved_value_, kAsReference);
+  } else {
+    writer->WriteObjectImpl(ptr()->value_.offset_, kAsReference);
+  }
+  // Write out the dependent code.
+  writer->WriteObjectImpl(ptr()->dependent_code_, kAsReference);
+  // Write out the initializer value.
+  writer->WriteObjectImpl(ptr()->initializer_.saved_value_, kAsReference);
+  // Write out the guarded list length.
+  writer->WriteObjectImpl(ptr()->guarded_list_length_, kAsReference);
 }
 
 
@@ -1180,6 +1196,8 @@
   // Write out all the object pointer fields.
   SnapshotWriterVisitor visitor(writer);
   visitor.VisitPointers(from(), to());
+
+  writer->SetInstructionsCode(ptr()->instructions_, this);
 }
 
 
diff --git a/runtime/vm/regexp.cc b/runtime/vm/regexp.cc
index f20e015..f58137c 100644
--- a/runtime/vm/regexp.cc
+++ b/runtime/vm/regexp.cc
@@ -5275,10 +5275,18 @@
                                                            Heap::kOld)));
   fn.set_parameter_names(Array::Handle(zone, Array::New(kParamCount,
                                                            Heap::kOld)));
-  fn.SetParameterTypeAt(0, Type::Handle(zone, Type::DynamicType()));
-  fn.SetParameterNameAt(0, Symbols::string_param());
-  fn.SetParameterTypeAt(1, Type::Handle(zone, Type::DynamicType()));
-  fn.SetParameterNameAt(1, Symbols::start_index_param());
+  fn.SetParameterTypeAt(RegExpMacroAssembler::kParamRegExpIndex,
+                        Type::Handle(zone, Type::DynamicType()));
+  fn.SetParameterNameAt(RegExpMacroAssembler::kParamRegExpIndex,
+                        Symbols::This());
+  fn.SetParameterTypeAt(RegExpMacroAssembler::kParamStringIndex,
+                        Type::Handle(zone, Type::DynamicType()));
+  fn.SetParameterNameAt(RegExpMacroAssembler::kParamStringIndex,
+                        Symbols::string_param());
+  fn.SetParameterTypeAt(RegExpMacroAssembler::kParamStartOffsetIndex,
+                        Type::Handle(zone, Type::DynamicType()));
+  fn.SetParameterNameAt(RegExpMacroAssembler::kParamStartOffsetIndex,
+                        Symbols::start_index_param());
   fn.set_result_type(Type::Handle(zone, Type::ArrayType()));
 
   // Cache the result.
diff --git a/runtime/vm/regexp_assembler.h b/runtime/vm/regexp_assembler.h
index 821f054..5d0ee47 100644
--- a/runtime/vm/regexp_assembler.h
+++ b/runtime/vm/regexp_assembler.h
@@ -99,7 +99,8 @@
   static const intptr_t kTableMask = kTableSize - 1;
 
   enum {
-    kParamStringIndex = 0,
+    kParamRegExpIndex = 0,
+    kParamStringIndex,
     kParamStartOffsetIndex,
     kParamCount
   };
diff --git a/runtime/vm/regexp_assembler_ir.cc b/runtime/vm/regexp_assembler_ir.cc
index 5704b12..63ce272 100644
--- a/runtime/vm/regexp_assembler_ir.cc
+++ b/runtime/vm/regexp_assembler_ir.cc
@@ -162,8 +162,10 @@
   index_temp_ = Local(Symbols::index_temp());
   result_ = Local(Symbols::result());
 
-  string_param_ = Parameter(Symbols::string_param(), 0);
-  start_index_param_ = Parameter(Symbols::start_index_param(), 1);
+  string_param_ = Parameter(Symbols::string_param(),
+                            RegExpMacroAssembler::kParamStringIndex);
+  start_index_param_ = Parameter(Symbols::start_index_param(),
+                                 RegExpMacroAssembler::kParamStartOffsetIndex);
 }
 
 
@@ -307,19 +309,24 @@
 
 
 RawArray* IRRegExpMacroAssembler::Execute(
-    const Function& function,
+    const JSRegExp& regexp,
     const String& input,
     const Smi& start_offset,
     Zone* zone) {
+  const intptr_t cid = input.GetClassId();
+  const Function& fun = Function::Handle(regexp.function(cid));
+  ASSERT(!fun.IsNull());
   // Create the argument list.
-  const Array& args = Array::Handle(Array::New(2));
-  args.SetAt(0, input);
-  args.SetAt(1, start_offset);
+  const Array& args =
+      Array::Handle(Array::New(RegExpMacroAssembler::kParamCount));
+  args.SetAt(RegExpMacroAssembler::kParamRegExpIndex, regexp);
+  args.SetAt(RegExpMacroAssembler::kParamStringIndex, input);
+  args.SetAt(RegExpMacroAssembler::kParamStartOffsetIndex, start_offset);
 
   // And finally call the generated code.
 
   const Object& retval =
-      Object::Handle(zone, DartEntry::InvokeFunction(function, args));
+      Object::Handle(zone, DartEntry::InvokeFunction(fun, args));
   if (retval.IsError()) {
     const Error& error = Error::Cast(retval);
     OS::Print("%s\n", error.ToErrorCString());
@@ -433,7 +440,7 @@
   ASSERT(!word_character_field.IsUninitialized());
 
   return new(Z) ConstantInstr(
-        Instance::ZoneHandle(Z, word_character_field.value()));
+        Instance::ZoneHandle(Z, word_character_field.StaticValue()));
 }
 
 
diff --git a/runtime/vm/regexp_assembler_ir.h b/runtime/vm/regexp_assembler_ir.h
index 1bd3930..eeb9f26 100644
--- a/runtime/vm/regexp_assembler_ir.h
+++ b/runtime/vm/regexp_assembler_ir.h
@@ -37,7 +37,7 @@
 
   virtual bool CanReadUnaligned();
 
-  static RawArray* Execute(const Function& function,
+  static RawArray* Execute(const JSRegExp& regexp,
                            const String& input,
                            const Smi& start_offset,
                            Zone* zone);
diff --git a/runtime/vm/regexp_test.cc b/runtime/vm/regexp_test.cc
index 7b33982..d75dac4 100644
--- a/runtime/vm/regexp_test.cc
+++ b/runtime/vm/regexp_test.cc
@@ -16,11 +16,8 @@
   Zone* zone = Thread::Current()->zone();
   const JSRegExp& regexp = JSRegExp::Handle(
       RegExpEngine::CreateJSRegExp(zone, pat, false, false));
-  const intptr_t cid = str.GetClassId();
-  const Function& fn = Function::Handle(regexp.function(cid));
-  EXPECT(!fn.IsNull());
   const Smi& idx = Smi::Handle(Smi::New(0));
-  return IRRegExpMacroAssembler::Execute(fn, str, idx, zone);
+  return IRRegExpMacroAssembler::Execute(regexp, str, idx, zone);
 }
 
 TEST_CASE(RegExp_OneByteString) {
diff --git a/runtime/vm/scanner.cc b/runtime/vm/scanner.cc
index d5533eb..50073cd 100644
--- a/runtime/vm/scanner.cc
+++ b/runtime/vm/scanner.cc
@@ -385,9 +385,8 @@
   }
   if (current_token_.kind != Token::kILLEGAL) {
     intptr_t len = lookahead_pos_ - token_start_;
-    String& str = String::ZoneHandle(Z,
-        String::SubString(source_, token_start_, len, Heap::kOld));
-    str = Symbols::New(str);
+    const String& str =
+        String::ZoneHandle(Z, Symbols::New(source_, token_start_, len));
     current_token_.literal = &str;
   }
 }
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 5ab4697..b336512 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -264,7 +264,7 @@
 
 RawObject* SnapshotReader::ReadStaticImplicitClosure(intptr_t object_id,
                                                      intptr_t class_header) {
-  ASSERT(kind_ == Snapshot::kMessage);
+  ASSERT(kind_ != Snapshot::kFull);
 
   // First create a function object and associate it with the specified
   // 'object_id'.
@@ -1138,18 +1138,106 @@
 
 
 int32_t InstructionsWriter::GetOffsetFor(RawInstructions* instructions) {
-  // Instructions are allocated with the code alignment and we don't write
-  // anything else in the text section.
-  ASSERT(Utils::IsAligned(stream_.bytes_written(),
-                          OS::PreferredCodeAlignment()));
-
-  intptr_t offset = stream_.bytes_written();
-  stream_.WriteBytes(reinterpret_cast<uint8_t*>(instructions) - kHeapObjectTag,
-                     instructions->Size());
+  intptr_t offset = next_offset_;
+  next_offset_ += instructions->Size();
+  instructions_.Add(InstructionsData(instructions));
   return offset;
 }
 
 
+static void EnsureIdentifier(char* label) {
+  for (char c = *label; c != '\0'; c = *++label) {
+    if (((c >= 'a') && (c <= 'z')) ||
+        ((c >= 'A') && (c <= 'Z')) ||
+        ((c >= '0') && (c <= '9'))) {
+      continue;
+    }
+    *label = '_';
+  }
+}
+
+
+void InstructionsWriter::WriteAssembly() {
+  Zone* Z = Thread::Current()->zone();
+
+  // Handlify collected raw pointers as building the names below
+  // will allocate on the Dart heap.
+  for (intptr_t i = 0; i < instructions_.length(); i++) {
+    InstructionsData& data = instructions_[i];
+    data.insns_ = &Instructions::Handle(Z, data.raw_insns_);
+    ASSERT(data.raw_code_ != NULL);
+    data.code_ = &Code::Handle(Z, data.raw_code_);
+  }
+
+  stream_.Print(".text\n");
+  stream_.Print(".globl _kInstructionsSnapshot\n");
+  stream_.Print(".balign %" Pd ", 0\n", OS::PreferredCodeAlignment());
+  stream_.Print("_kInstructionsSnapshot:\n");
+
+  Object& owner = Object::Handle(Z);
+  String& str = String::Handle(Z);
+
+  for (intptr_t i = 0; i < instructions_.length(); i++) {
+    const Instructions& insns = *instructions_[i].insns_;
+    const Code& code = *instructions_[i].code_;
+
+    ASSERT(insns.raw()->Size() % sizeof(uint64_t) == 0);
+
+    {
+      // 1. Write from the header to the entry point.
+      NoSafepointScope no_safepoint;
+      uword beginning = reinterpret_cast<uword>(insns.raw()) - kHeapObjectTag;
+      uword entry = beginning + Instructions::HeaderSize();
+
+      ASSERT(Utils::IsAligned(beginning, sizeof(uint64_t)));
+      ASSERT(Utils::IsAligned(entry, sizeof(uint64_t)));
+
+      for (uint64_t* cursor = reinterpret_cast<uint64_t*>(beginning);
+           cursor < reinterpret_cast<uint64_t*>(entry);
+           cursor++) {
+        stream_.Print(".quad 0x%0.16" Px64 "\n", *cursor);
+      }
+    }
+
+    // 2. Write a label at the entry point.
+    owner = code.owner();
+    if (owner.IsNull()) {
+      const char* name = StubCode::NameOfStub(insns.EntryPoint());
+      stream_.Print("Precompiled_Stub_%s:\n", name);
+    } else if (owner.IsClass()) {
+      str = Class::Cast(owner).Name();
+      const char* name = str.ToCString();
+      EnsureIdentifier(const_cast<char*>(name));
+      stream_.Print("Precompiled_AllocationStub_%s_%" Pd ":\n", name, i);
+    } else if (owner.IsFunction()) {
+      const char* name = Function::Cast(owner).ToQualifiedCString();
+      EnsureIdentifier(const_cast<char*>(name));
+      stream_.Print("Precompiled_%s_%" Pd ":\n", name, i);
+    } else {
+      UNREACHABLE();
+    }
+
+    {
+      // 3. Write from the entry point to the end.
+      NoSafepointScope no_safepoint;
+      uword beginning = reinterpret_cast<uword>(insns.raw()) - kHeapObjectTag;
+      uword entry = beginning + Instructions::HeaderSize();
+      uword end = beginning + insns.raw()->Size();
+
+      ASSERT(Utils::IsAligned(beginning, sizeof(uint64_t)));
+      ASSERT(Utils::IsAligned(entry, sizeof(uint64_t)));
+      ASSERT(Utils::IsAligned(end, sizeof(uint64_t)));
+
+      for (uint64_t* cursor = reinterpret_cast<uint64_t*>(entry);
+           cursor < reinterpret_cast<uint64_t*>(end);
+           cursor++) {
+        stream_.Print(".quad 0x%0.16" Px64 "\n", *cursor);
+      }
+    }
+  }
+}
+
+
 RawInstructions* InstructionsReader::GetInstructionsAt(int32_t offset,
                                                        uword expected_tags) {
   ASSERT(Utils::IsAligned(offset, OS::PreferredCodeAlignment()));
@@ -1944,6 +2032,7 @@
     }
     WriteIsolateFullSnapshot();
 
+    instructions_writer_->WriteAssembly();
     instructions_snapshot_size_ = instructions_writer_->BytesWritten();
   } else {
     if (vm_isolate_snapshot_buffer() != NULL) {
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index d84d3fe..f612073 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -22,9 +22,11 @@
 class Array;
 class Class;
 class ClassTable;
+class Code;
 class ExternalTypedData;
 class GrowableObjectArray;
 class Heap;
+class Instructions;
 class LanguageError;
 class Library;
 class Object;
@@ -777,7 +779,9 @@
   InstructionsWriter(uint8_t** buffer,
                      ReAlloc alloc,
                      intptr_t initial_size)
-    : stream_(buffer, alloc, initial_size) {
+    : stream_(buffer, alloc, initial_size),
+      next_offset_(0),
+      instructions_() {
     ASSERT(buffer != NULL);
     ASSERT(alloc != NULL);
   }
@@ -787,8 +791,37 @@
 
   int32_t GetOffsetFor(RawInstructions* instructions);
 
+  void SetInstructionsCode(RawInstructions* insns, RawCode* code) {
+    for (intptr_t i = 0; i < instructions_.length(); i++) {
+      if (instructions_[i].raw_insns_ == insns) {
+        instructions_[i].raw_code_ = code;
+        return;
+      }
+    }
+    UNREACHABLE();
+  }
+
+  void WriteAssembly();
+
  private:
+  struct InstructionsData {
+    explicit InstructionsData(RawInstructions* insns)
+        : raw_insns_(insns), raw_code_(NULL) { }
+
+    union {
+      RawInstructions* raw_insns_;
+      const Instructions* insns_;
+    };
+    union {
+      RawCode* raw_code_;
+      const Code* code_;
+    };
+  };
+
+
   WriteStream stream_;
+  intptr_t next_offset_;
+  GrowableArray<InstructionsData> instructions_;
 
   DISALLOW_COPY_AND_ASSIGN(InstructionsWriter);
 };
@@ -839,6 +872,10 @@
     return instructions_writer_->GetOffsetFor(instructions);
   }
 
+  void SetInstructionsCode(RawInstructions* instructions, RawCode* code) {
+    return instructions_writer_->SetInstructionsCode(instructions, code);
+  }
+
  protected:
   void UnmarkAll() {
     if (!unmarked_objects_ && forward_list_ != NULL) {
@@ -909,6 +946,7 @@
   friend class RawClosureData;
   friend class RawContextScope;
   friend class RawExceptionHandlers;
+  friend class RawField;
   friend class RawGrowableObjectArray;
   friend class RawImmutableArray;
   friend class RawInstructions;
diff --git a/runtime/vm/symbols.cc b/runtime/vm/symbols.cc
index c827704..c9cbcf3 100644
--- a/runtime/vm/symbols.cc
+++ b/runtime/vm/symbols.cc
@@ -492,6 +492,41 @@
 }
 
 
+template<typename StringType>
+RawString* Symbols::Lookup(const StringType& str) {
+  Thread* thread = Thread::Current();
+  Isolate* isolate = thread->isolate();
+  Zone* zone = thread->zone();
+  String& symbol = String::Handle(zone);
+  {
+    Isolate* vm_isolate = Dart::vm_isolate();
+    SymbolTable table(zone, vm_isolate->object_store()->symbol_table());
+    symbol ^= table.GetOrNull(str);
+    table.Release();
+  }
+  if (symbol.IsNull()) {
+    SymbolTable table(zone, isolate->object_store()->symbol_table());
+    symbol ^= table.GetOrNull(str);
+    table.Release();
+  }
+
+  ASSERT(symbol.IsNull() || symbol.IsSymbol());
+  ASSERT(symbol.IsNull() || symbol.HasHash());
+  return symbol.raw();
+}
+
+
+RawString* Symbols::LookupFromConcat(const String& str1, const String& str2) {
+  if (str1.Length() == 0) {
+    return Lookup(str2);
+  } else if (str2.Length() == 0) {
+    return Lookup(str1);
+  } else {
+    return Lookup(ConcatString(str1, str2));
+  }
+}
+
+
 RawString* Symbols::New(const String& str) {
   if (str.IsSymbol()) {
     return str.raw();
@@ -505,6 +540,31 @@
 }
 
 
+
+RawString* Symbols::NewFormatted(const char* format, ...) {
+  va_list args;
+  va_start(args, format);
+  RawString* result = NewFormattedV(format, args);
+  NoSafepointScope no_safepoint;
+  va_end(args);
+  return result;
+}
+
+
+RawString* Symbols::NewFormattedV(const char* format, va_list args) {
+  va_list args_copy;
+  va_copy(args_copy, args);
+  intptr_t len = OS::VSNPrint(NULL, 0, format, args_copy);
+  va_end(args_copy);
+
+  Zone* zone = Thread::Current()->zone();
+  char* buffer = zone->Alloc<char>(len + 1);
+  OS::VSNPrint(buffer, (len + 1), format, args);
+
+  return Symbols::New(buffer);
+}
+
+
 RawString* Symbols::FromCharCode(int32_t char_code) {
   if (char_code > kMaxOneCharCodeSymbol) {
     return FromUTF32(&char_code, 1);
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index f5f7bbf..8a91de7 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -595,6 +595,10 @@
                         intptr_t begin_index,
                         intptr_t length);
 
+  static RawString* NewFormatted(const char* format, ...)
+      PRINTF_ATTRIBUTE(1, 2);
+  static RawString* NewFormattedV(const char* format, va_list args);
+
   static RawString* FromConcat(const String& str1, const String& str2);
 
   static RawString* FromConcatAll(
@@ -611,6 +615,13 @@
 
   static void DumpStats();
 
+  // Returns Symbol::Null if no symbol is found.
+  template<typename StringType>
+  static RawString* Lookup(const StringType& str);
+
+  // Returns Symbol::Null if no symbol is found.
+  static RawString* LookupFromConcat(const String& str1, const String& str2);
+
  private:
   enum {
     kInitialVMIsolateSymtabSize = 1024,
diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc
index d0c4651..7d817d3 100644
--- a/runtime/vm/thread.cc
+++ b/runtime/vm/thread.cc
@@ -12,6 +12,7 @@
 #include "vm/profiler.h"
 #include "vm/runtime_entry.h"
 #include "vm/stub_code.h"
+#include "vm/symbols.h"
 #include "vm/thread_interrupter.h"
 #include "vm/thread_registry.h"
 
diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h
index 9643c33..16eade4 100644
--- a/runtime/vm/thread.h
+++ b/runtime/vm/thread.h
@@ -20,6 +20,7 @@
 class Object;
 class RawBool;
 class RawObject;
+class RawString;
 class RuntimeEntry;
 class StackResource;
 class TimelineEventBlock;
@@ -34,6 +35,8 @@
 #define CACHED_ADDRESSES_LIST(V)                                               \
   V(uword, update_store_buffer_entry_point_,                                   \
     StubCode::UpdateStoreBuffer_entry()->EntryPoint(), 0)                      \
+  V(RawString**, predefined_symbols_address_,                                  \
+    Symbols::PredefinedAddress(), NULL)                                        \
 
 #define CACHED_CONSTANTS_LIST(V)                                               \
   CACHED_VM_OBJECTS_LIST(V)                                                    \
diff --git a/sdk/lib/_internal/js_runtime/lib/js_helper.dart b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
index 2a91a2e..91049cd 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_helper.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_helper.dart
@@ -2396,8 +2396,12 @@
                      jsArguments,
                      String propertyName) {
     JS_EFFECT(() {
-      BoundClosure.receiverOf(JS('BoundClosure', 'void 0'));
-      BoundClosure.selfOf(JS('BoundClosure', 'void 0'));
+      // The functions are called here to model the calls from JS forms below.
+      // The types in the JS forms in the arguments are propagated in type
+      // inference.
+      BoundClosure.receiverOf(JS('BoundClosure', '0'));
+      BoundClosure.selfOf(JS('BoundClosure', '0'));
+      getType(JS('int', '0'));
     });
     // TODO(ahe): All the place below using \$ should be rewritten to go
     // through the namer.
diff --git a/sdk/lib/io/file_impl.dart b/sdk/lib/io/file_impl.dart
index 8ed6c58..2164bc9 100644
--- a/sdk/lib/io/file_impl.dart
+++ b/sdk/lib/io/file_impl.dart
@@ -566,56 +566,43 @@
 
 
 class _RandomAccessFile
-    extends Object with _ServiceObject
     implements RandomAccessFile {
-  // Use default Map so we keep order.
-  static Map<int, _RandomAccessFile> _files = new Map<int, _RandomAccessFile>();
+  static bool _connectedResourceHandler = false;
 
   final String path;
   int _id;
   bool _asyncDispatched = false;
   SendPort _fileService;
 
-  int _totalRead = 0;
-  int _totalWritten = 0;
-  int _readCount = 0;
-  int _writeCount = 0;
-
+  _FileResourceInfo _resourceInfo;
 
   _RandomAccessFile(this._id, this.path) {
-    _files[_serviceId] = this;
-  }
-
-  String get _serviceTypePath => 'io/file/randomaccessfiles';
-  String get _serviceTypeName => 'RandomAccessFile';
-
-  Map _toJSON(bool ref) {
-    var r = {
-      'id': _servicePath,
-      'type': _serviceType(ref),
-      'name': '$path',
-      'user_name': '$path',
-    };
-    if (ref) {
-      return r;
-    }
-    r['asyncDispatched'] = _asyncDispatched;
-    r['fd'] = _getFD(_id);
-    r['totalRead'] = _totalRead;
-    r['totalWritten'] = _totalWritten;
-    r['readCount'] = _totalWritten;
-    r['writeCount'] = _writeCount;
-    return r;
+    _resourceInfo = new _FileResourceInfo(this);
+    _maybeConnectHandler();
   }
 
   void _maybePerformCleanup() {
     if (closed) {
-      _files.remove(_serviceId);
+      _FileResourceInfo.FileClosed(_resourceInfo);
     }
   }
 
   external static int _getFD(int id);
 
+  _maybeConnectHandler() {
+    if (!_connectedResourceHandler) {
+      // TODO(ricow): we probably need set these in some initialization code.
+      // We need to make sure that these are always awailable from the
+      // observatory even if no files (or sockets for the socket ones) are
+      // open.
+      registerExtension('__getOpenFiles',
+                        _FileResourceInfo.getOpenFiles);
+      registerExtension('__getFileByID',
+                        _FileResourceInfo.getFileInfoMapByID);
+      _connectedResourceHandler = true;
+    }
+  }
+
   Future<RandomAccessFile> close() {
     return _dispatch(_FILE_CLOSE, [_id], markClosed: true).then((result) {
       if (result != -1) {
@@ -645,8 +632,8 @@
       if (_isErrorResponse(response)) {
         throw _exceptionFromResponse(response, "readByte failed", path);
       }
-      _readCount++;
-      _totalRead++;
+      _resourceInfo.readCount++;
+      _resourceInfo.totalRead++;
       return response;
     });
   }
@@ -659,8 +646,8 @@
     if (result is OSError) {
       throw new FileSystemException("readByte failed", path, result);
     }
-    _readCount++;
-    _totalRead++;
+    _resourceInfo.readCount++;
+    _resourceInfo.totalRead++;
     return result;
   }
 
@@ -672,8 +659,8 @@
       if (_isErrorResponse(response)) {
         throw _exceptionFromResponse(response, "read failed", path);
       }
-      _readCount++;
-      _totalRead += response[1].length;
+      _resourceInfo.readCount++;
+      _resourceInfo.totalRead += response[1].length;
       return response[1];
     });
   }
@@ -689,8 +676,8 @@
     if (result is OSError) {
       throw new FileSystemException("readSync failed", path, result);
     }
-    _readCount++;
-    _totalRead += result.length;
+    _resourceInfo.readCount++;
+    _resourceInfo.totalRead += result.length;
     return result;
   }
 
@@ -710,8 +697,8 @@
       var read = response[1];
       var data = response[2];
       buffer.setRange(start, start + read, data);
-      _readCount++;
-      _totalRead += read;
+      _resourceInfo.readCount++;
+      _resourceInfo.totalRead += read;
       return read;
     });
   }
@@ -731,8 +718,8 @@
     if (result is OSError) {
       throw new FileSystemException("readInto failed", path, result);
     }
-    _readCount++;
-    _totalRead += result;
+    _resourceInfo.readCount++;
+    _resourceInfo.totalRead += result;
     return result;
   }
 
@@ -744,8 +731,8 @@
       if (_isErrorResponse(response)) {
         throw _exceptionFromResponse(response, "writeByte failed", path);
       }
-      _writeCount++;
-      _totalWritten++;
+      _resourceInfo.writeCount++;
+      _resourceInfo.totalWritten++;
       return this;
     });
   }
@@ -761,8 +748,8 @@
     if (result is OSError) {
       throw new FileSystemException("writeByte failed", path, result);
     }
-    _writeCount++;
-    _totalWritten++;
+    _resourceInfo.writeCount++;
+    _resourceInfo.totalWritten++;
     return result;
   }
 
@@ -791,8 +778,8 @@
       if (_isErrorResponse(response)) {
         throw _exceptionFromResponse(response, "writeFrom failed", path);
       }
-      _writeCount++;
-      _totalWritten += end - (start - result.start);
+      _resourceInfo.writeCount++;
+      _resourceInfo.totalWritten += end - (start - result.start);
       return this;
     });
   }
@@ -817,8 +804,8 @@
     if (result is OSError) {
       throw new FileSystemException("writeFrom failed", path, result);
     }
-    _writeCount++;
-    _totalWritten += end - (start - bufferAndStart.start);
+    _resourceInfo.writeCount++;
+    _resourceInfo.totalWritten += end - (start - bufferAndStart.start);
   }
 
   Future<RandomAccessFile> writeString(String string,
diff --git a/sdk/lib/io/io.dart b/sdk/lib/io/io.dart
index b3ca214..aea1b5b 100644
--- a/sdk/lib/io/io.dart
+++ b/sdk/lib/io/io.dart
@@ -206,6 +206,7 @@
                               LinkedListEntry,
                               UnmodifiableMapView;
 import 'dart:convert';
+import 'dart:developer';
 import 'dart:isolate';
 import 'dart:math';
 import 'dart:typed_data';
@@ -226,6 +227,7 @@
 part 'http_impl.dart';
 part 'http_parser.dart';
 part 'http_session.dart';
+part 'io_resource_info.dart';
 part 'io_sink.dart';
 part 'io_service.dart';
 part 'link.dart';
diff --git a/sdk/lib/io/io_resource_info.dart b/sdk/lib/io/io_resource_info.dart
new file mode 100644
index 0000000..8c30c64
--- /dev/null
+++ b/sdk/lib/io/io_resource_info.dart
@@ -0,0 +1,205 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.io;
+
+abstract class _IOResourceInfo {
+  final String type;
+  final int id;
+  String get name;
+  static int _count = 0;
+
+  _IOResourceInfo(this.type) : id = _IOResourceInfo.getNextID();
+
+  String toJSON();
+
+  /// Get the full set of values for a specific implementation. This is normally
+  /// looked up based on an id from a referenceValueMap.
+  Map<String, String> get fullValueMap;
+
+  /// The reference map, used to return a list of values, e.g., getting
+  /// all open sockets. The structure of this is shared among all subclasses.
+  Map<String, String> get referenceValueMap =>
+      {
+        // The type for a reference object is prefixed with @ in observatory.
+        'type': '@$type',
+        'id': id,
+        'name': name,
+      };
+
+  static int getNextID() => _count++;
+}
+
+// TODO(ricow): Move stopwatch into this class and use it for both files
+// and sockets (by using setters on totalRead/totalWritten). Also, consider
+// setting readCount and writeCount in those setters.
+abstract class _ReadWriteResourceInfo extends _IOResourceInfo {
+  int totalRead;
+  int totalWritten;
+  int readCount;
+  int writeCount;
+  double lastRead;
+  double lastWrite;
+
+  _ReadWriteResourceInfo(String type) :
+    totalRead = 0,
+    totalWritten = 0,
+    readCount = 0,
+    writeCount = 0,
+    lastRead = 0.0,
+    lastWrite = 0.0,
+    super(type);
+
+  Map<String, String> get fullValueMap =>
+    {
+      'type': type,
+      'id': id,
+      'name': name,
+      'total_read': totalRead,
+      'total_written': totalWritten,
+      'read_count': readCount,
+      'write_count': writeCount,
+      'last_read': lastRead,
+      'last_write': lastWrite
+    };
+
+  String toJSON() {
+    return JSON.encode(fullValueMap);
+  }
+}
+
+class _FileResourceInfo extends _ReadWriteResourceInfo {
+  static const String TYPE = '_file';
+
+  final file;
+
+  static Map<int, _FileResourceInfo> openFiles =
+      new Map<int, _FileResourceInfo>();
+
+  _FileResourceInfo(this.file) : super(TYPE) {
+    FileOpened(this);
+  }
+
+  static FileOpened(_FileResourceInfo info) {
+    assert(!openFiles.containsKey(info.id));
+    openFiles[info.id] = info;
+  }
+
+  static FileClosed(_FileResourceInfo info) {
+    assert(openFiles.containsKey(info.id));
+    openFiles.remove(info.id);
+  }
+
+  static Iterable<Map<String, String>> getOpenFilesList() {
+    return new List.from(openFiles.values.map((e) => e.referenceValueMap));
+  }
+
+  static Future<ServiceExtensionResponse> getOpenFiles(function, params) {
+    assert(function == '__getOpenFiles');
+    var data = {'type': '_openfiles', 'data': getOpenFilesList()};
+    var json = JSON.encode(data);
+    return new Future.value(new ServiceExtensionResponse.result(json));
+  }
+
+  Map<String, String> getFileInfoMap() {
+    var result = fullValueMap;
+    return result;
+  }
+
+  static Future<ServiceExtensionResponse> getFileInfoMapByID(function, params) {
+    assert(params.containsKey('id'));
+    var id = int.parse(params['id']);
+    var result =
+      openFiles.containsKey(id) ? openFiles[id].getFileInfoMap() : {};
+    var json = JSON.encode(result);
+    return new Future.value(new ServiceExtensionResponse.result(json));
+  }
+
+  String get name {
+    return '${file.path}';
+  }
+}
+
+class _SocketResourceInfo extends _ReadWriteResourceInfo {
+  static const String TCP_STRING = 'TCP';
+  static const String UDP_STRING = 'UDP';
+  static const String TYPE = '_socket';
+
+  final socket;
+
+  static Map<int, _SocketResourceInfo> openSockets =
+      new Map<int, _SocketResourceInfo>();
+
+  _SocketResourceInfo(this.socket) : super(TYPE) {
+    SocketOpened(this);
+  }
+
+  String get name {
+    if (socket.isListening) {
+      return 'listening:${socket.address.host}:${socket.port}';
+    }
+    var remote = '';
+    try {
+      var remoteHost = socket.remoteAddress.host;
+      var remotePort = socket.remotePort;
+      remote = ' -> $remoteHost:$remotePort';
+    } catch (e) { } // ignored if we can't get the information
+    return '${socket.address.host}:${socket.port}$remote';
+  }
+
+  static Iterable<Map<String, String>> getOpenSocketsList() {
+    return new List.from(openSockets.values.map((e) => e.referenceValueMap));
+  }
+
+  Map<String, String> getSocketInfoMap() {
+    var result = fullValueMap;
+    result['socket_type'] = socket.isTcp ? TCP_STRING : UDP_STRING;
+    result['listening'] = socket.isListening;
+    result['host'] = socket.address.host;
+    result['port'] = socket.port;
+    if (!socket.isListening) {
+      try {
+        result['remote_host'] = socket.remoteAddress.host;
+        result['remote_port'] = socket.remotePort;
+      } catch (e) {
+        // UDP.
+        result['remote_port'] = 'NA';
+        result['remote_host'] = 'NA';
+      }
+    } else {
+      result['remote_port'] = 'NA';
+      result['remote_host'] = 'NA';
+    }
+    result['address_type'] = socket.address.type.name;
+    return result;
+  }
+
+  static Future<ServiceExtensionResponse> getSocketInfoMapByID(
+      String function, Map<String, String> params) {
+    assert(params.containsKey('id'));
+    var id = int.parse(params['id']);
+    var result =
+      openSockets.containsKey(id) ? openSockets[id].getSocketInfoMap() : {};
+    var json = JSON.encode(result);
+    return new Future.value(new ServiceExtensionResponse.result(json));
+  }
+
+  static Future<ServiceExtensionResponse> getOpenSockets(function, params) {
+    assert(function == '__getOpenSockets');
+    var data = {'type': '_opensockets', 'data': getOpenSocketsList()};
+    var json = JSON.encode(data);
+    return new Future.value(new ServiceExtensionResponse.result(json));
+  }
+
+  static SocketOpened(_SocketResourceInfo info) {
+    assert(!openSockets.containsKey(info.id));
+    openSockets[info.id] = info;
+  }
+
+  static SocketClosed(_SocketResourceInfo info) {
+    assert(openSockets.containsKey(info.id));
+    openSockets.remove(info.id);
+  }
+
+}
diff --git a/sdk/lib/io/iolib_sources.gypi b/sdk/lib/io/iolib_sources.gypi
index bb8848c..ad2110f 100644
--- a/sdk/lib/io/iolib_sources.gypi
+++ b/sdk/lib/io/iolib_sources.gypi
@@ -20,6 +20,7 @@
     'http_impl.dart',
     'http_parser.dart',
     'http_session.dart',
+    'io_resource_info.dart',
     'io_sink.dart',
     'io_service.dart',
     'link.dart',
diff --git a/tests/co19/co19-dart2js.status b/tests/co19/co19-dart2js.status
index 019e273..224f620 100644
--- a/tests/co19/co19-dart2js.status
+++ b/tests/co19/co19-dart2js.status
@@ -1536,6 +1536,20 @@
 LayoutTests/fast/text/regional-indicator-symobls_t01: RuntimeError # Please triage this failure
 LayoutTests/fast/text/text-combine-shrink-to-fit_t01: RuntimeError # Please triage this failure
 
+[ $compiler == dart2js && $runtime == chrome && $system != linux ]
+# These are failures in Chrome 45, which is not yet on Linux.
+# This will be merged with the earlier blocks when Linux is updated.
+LayoutTests/fast/css/background-position-serialize_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/font-shorthand-from-longhands_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/getComputedStyle/computed-style-font_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/getPropertyValue-columns_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/css/parse-color-int-or-percent-crash_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/forms/ValidityState-customError_t01: RuntimeError # Please triage this failure
+LayoutTests/fast/multicol/balance-short-trailing-empty-block_t01: Pass
+LayoutTests/fast/multicol/balance-trailing-border_t01: Pass
+LayoutTests/fast/multicol/columns-shorthand-parsing_t02: RuntimeError # Please triage this failure
+LayoutTests/fast/multicol/widows_t02: Pass
+
 [ $compiler == dart2js && $runtime == ff ]
 Language/12_Expressions/28_Postfix_Expressions_A07_t02: Skip # Times out. Please triage this failure
 LayoutTests/fast/alignment/parse-align-items_t01: RuntimeError # Please triage this failure
diff --git a/tests/compiler/dart2js/dart2js.status b/tests/compiler/dart2js/dart2js.status
index d2c606b..3518280 100644
--- a/tests/compiler/dart2js/dart2js.status
+++ b/tests/compiler/dart2js/dart2js.status
@@ -20,6 +20,12 @@
 simple_inferrer_const_closure_test: Fail # Issue 16507
 simple_inferrer_const_closure2_test: Fail # Issue 16507
 simple_inferrer_global_field_closure_test: Fail # Issue 16507
+simple_inferrer_const_closure_default_test/05: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/06: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/09: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/10: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/11: Fail # Issue 24297
+simple_inferrer_const_closure_default_test/12: Fail # Issue 24297
 
 logical_expression_test: Fail # Issue 17027
 
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 e8e5ebc..51d2762 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_basic_test.dart
@@ -25,14 +25,15 @@
   print('($m)');
 }""",r"""
 function() {
+  var 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("(1)");
-  P.print("(" + H.S([1, 2, 3]) + ")");
-  P.print("(" + H.S(P.LinkedHashMap_LinkedHashMap$_literal(["s", 1])) + ")");
+  P.print("(" + H.S(l) + ")");
+  P.print("(" + H.S(m) + ")");
 }"""),
   const TestEntry("""
 foo(a, [b = "b"]) { print(b); return b; }
diff --git a/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart b/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart
index bb5e902..cafe812 100644
--- a/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart
+++ b/tests/compiler/dart2js/js_backend_cps_ir_interceptors_test.dart
@@ -38,8 +38,11 @@
     if (i < 0 || i >= l.length)
       H.ioore(l, i);
     x_ = J.getInterceptor$as(x = l[i]);
-    for (j = 0; j < x_.get$length(x); j = j + 1)
-      P.print(x_.$index(x, j));
+    for (j = 0; j < x_.get$length(x); j = j + 1) {
+      if (j < 0 || j >= x.length)
+        H.ioore(x, j);
+      P.print(x[j]);
+    }
   }
 }"""),
 ];
diff --git a/tests/compiler/dart2js/lookup_map_test.dart b/tests/compiler/dart2js/lookup_map_test.dart
index d73bc75..d3893b0 100644
--- a/tests/compiler/dart2js/lookup_map_test.dart
+++ b/tests/compiler/dart2js/lookup_map_test.dart
@@ -8,10 +8,50 @@
 import 'compiler_helper.dart';
 
 main() {
+  Map<String, String> testDeclarations = {
+    'types': r'''
+      import 'package:lookup_map/lookup_map.dart';
+      class A {}
+      class B {}
+      class C {}
+      class D {}
+      class E {}''',
+
+    'const keys': r'''
+      import 'package:lookup_map/lookup_map.dart';
+      class Key { final name; const Key(this.name); }
+      const A = const Key("A");
+      const B = const Key("B");
+      const C = const Key("C");
+      const D = const Key("D");
+      const E = const Key("E");''',
+
+    'mixed keys': r'''
+      import 'package:lookup_map/lookup_map.dart';
+      class Key { final name; const Key(this.name); }
+      const A = const Key("A");
+      class B {}
+      const C = const Key("C");
+      class D {}
+      const E = const Key("E");''',
+  };
+
+  testDeclarations.forEach((name, declarations) {
+    group(name, () => _commonTests(declarations));
+  });
+  group('generic', _genericTests);
+  group('metadata', _metadataTests);
+  group('unsupported', _unsupportedKeysTests);
+  group('mirrors', _mirrorsTests);
+}
+
+/// Common tests for both declarations that use Types or other const expressions
+/// as keys. The argument [declaration] should contain a declaration for
+/// constant keys named `A`, `B`, `C`, `D`, and `E`.
+_commonTests(String declarations) {
   test('live entries are kept', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
+    String generated = await compileAll("""
+        $declarations
         const map = const LookupMap(const [
             A, "the-text-for-A",
         ]);
@@ -21,9 +61,8 @@
   });
 
   test('live entries are kept - single-pair', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
+    String generated = await compileAll("""
+        $declarations
         const map = const LookupMap.pair(A, "the-text-for-A");
         main() => print(map[A]);
     """);
@@ -31,10 +70,8 @@
   });
 
   test('unused entries are removed', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
+    String generated = await compileAll("""
+        $declarations
         const map = const LookupMap(const [
             A, "the-text-for-A",
             B, "the-text-for-B",
@@ -45,10 +82,8 @@
   });
 
   test('unused entries are removed - nested maps', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
+    String generated = await compileAll("""
+        $declarations
         const map = const LookupMap(const [], const [
           const LookupMap(const [
               A, "the-text-for-A",
@@ -61,10 +96,8 @@
   });
 
   test('unused entries are removed - single-pair', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
+    String generated = await compileAll("""
+        $declarations
         const map = const LookupMap.pair(A, "the-text-for-A");
         main() => print(map[A]);
     """);
@@ -72,10 +105,9 @@
   });
 
   test('unused entries are removed - nested single-pair', () async {
-    String generated = await compileAll(r"""
+    String generated = await compileAll("""
         import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
+        $declarations
         const map = const LookupMap(const [], const [
           const LookupMap.pair(A, "the-text-for-A"),
           const LookupMap.pair(B, "the-text-for-B"),
@@ -86,10 +118,8 @@
   });
 
   test('works if entries are declared separate from map', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
+    String generated = await compileAll("""
+        $declarations
         const entries = const [
             A, "the-text-for-A",
             B, "the-text-for-B",
@@ -101,10 +131,8 @@
   });
 
   test('escaping entries disable tree-shaking', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
+    String generated = await compileAll("""
+        $declarations
         const entries = const [
             A, "the-text-for-A",
             B, "the-text-for-B",
@@ -119,13 +147,8 @@
   });
 
   test('uses include recursively reachable data', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
-        class C{}
-        class D{}
-        class E{}
+    String generated = await compileAll("""
+        $declarations
         const map = const LookupMap(const [
             A, const ["the-text-for-A", B],
             B, const ["the-text-for-B", C],
@@ -143,27 +166,62 @@
   });
 
   test('uses are found through newly discovered code', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{ A(B x);}
-        class B{}
-        class C{}
-        class D{}
-        class E{}
-        createA() => new A(map[B][1]());
-        createB() => new B();
+    String generated = await compileAll("""
+        $declarations
+        f1() => map[B][1]();
+        f2() => E;
         const map = const LookupMap(const [
-            A, const ["the-text-for-A", createA],
-            B, const ["the-text-for-B", createB],
+            A, const ["the-text-for-A", f1],
+            B, const ["the-text-for-B", f2],
             C, const ["the-text-for-C"],
+            D, const ["the-text-for-D"],
+            E, const ["the-text-for-E"],
         ]);
         main() => print(map[A][1]());
     """);
     expect(generated, contains("the-text-for-A"));
     expect(generated, contains("the-text-for-B"));
     expect(generated, isNot(contains("the-text-for-C")));
+    expect(generated, isNot(contains("the-text-for-C")));
+    expect(generated, contains("the-text-for-E"));
   });
 
+  test('support subclassing LookupMap', () async {
+    String generated = await compileAll("""
+        $declarations
+        class S extends LookupMap {
+          const S(list) : super(list);
+        }
+        const map = const S(const [
+            A, "the-text-for-A",
+            B, "the-text-for-B",
+        ]);
+
+        main() => print(map[A]);
+    """);
+    expect(generated, contains("the-text-for-A"));
+    expect(generated, isNot(contains("the-text-for-B")));
+  });
+
+  test('constants keys are processed recursively', () async {
+    String generated = await compileAll("""
+        $declarations
+
+        const nested = const [ B ];
+        const map = const LookupMap(const [
+            A, "the-text-for-A",
+            B, "the-text-for-B",
+        ]);
+        main() => print(map[nested]);
+    """);
+    expect(generated, isNot(contains("the-text-for-A")));
+    expect(generated, contains("the-text-for-B"));
+  });
+}
+
+/// Tests specific to type keys, we ensure that generic type arguments are
+/// considered.
+_genericTests() {
   test('generic type allocations are considered used', () async {
     String generated = await compileAll(r"""
         import 'package:lookup_map/lookup_map.dart';
@@ -193,6 +251,23 @@
     expect(generated, isNot(contains("the-text-for-B")));
   });
 
+  // regression test for a failure when looking up `dynamic` in a generic.
+  test('do not choke with dynamic type arguments', () async {
+    await compileAll(r"""
+        import 'package:lookup_map/lookup_map.dart';
+        class A{}
+        class M<T>{ get type => T; }
+        const map = const LookupMap(const [
+            A, "the-text-for-A",
+        ]);
+        main() => print(map[new M<dynamic>().type]);
+    """);
+  });
+}
+
+/// Sanity checks about metadata: it is ignored for codegen even though it is
+/// visited during resolution.
+_metadataTests() {
   test('metadata is ignored', () async {
     String generated = await compileAll(r"""
         import 'package:lookup_map/lookup_map.dart';
@@ -225,37 +300,95 @@
     """);
     expect(generated, isNot(contains("the-text-for-A")));
   });
+}
 
-  // regression test for a failure when looking up `dynamic` in a generic.
-  test('do not choke on dynamic types', () async {
-    await compileAll(r"""
+_unsupportedKeysTests() {
+  test('primitive and string keys are always kept', () async {
+    String generated = await compileAll("""
         import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class M<T>{ get type => T; }
+        const A = "A";
+        const B = "B";
         const map = const LookupMap(const [
             A, "the-text-for-A",
-        ]);
-        main() => print(map[new M<dynamic>().type]);
-    """);
-  });
-
-
-  test('support subclassing LookupMap', () async {
-    String generated = await compileAll(r"""
-        import 'package:lookup_map/lookup_map.dart';
-        class A{}
-        class B{}
-        class S extends LookupMap {
-          const S(list) : super(list);
-        }
-        const map = const S(const [
-            A, "the-text-for-A",
             B, "the-text-for-B",
+            3, "the-text-for-3",
+            1.1, "the-text-for-1.1",
+            false, "the-text-for-false",
         ]);
-
         main() => print(map[A]);
     """);
     expect(generated, contains("the-text-for-A"));
-    expect(generated, isNot(contains("the-text-for-B")));
+    expect(generated, contains("the-text-for-B"));
+    expect(generated, contains("the-text-for-3"));
+    expect(generated, contains("the-text-for-1.1"));
+    expect(generated, contains("the-text-for-false"));
+  });
+
+  test('non-type const keys implementing equals are not removed', () async {
+    String generated = await compileAll("""
+        import 'package:lookup_map/lookup_map.dart';
+        class Key {
+          final name;
+          const Key(this.name);
+          int get hashCode => name.hashCode * 13;
+          operator ==(other) => other is Key && name == other.name;
+        }
+        const A = const Key("A");
+        const B = const Key("B");
+        const map = const LookupMap(const [
+            A, "the-text-for-A",
+            B, "the-text-for-B",
+        ]);
+        main() => print(map[A]);
+    """);
+    expect(generated, contains("the-text-for-B"));
+  });
+}
+
+_mirrorsTests() {
+  test('retain entries if mirrors keep the type', () async {
+    String generated = await compileAll("""
+        import 'dart:mirrors';
+        import 'package:lookup_map/lookup_map.dart';
+        class A {}
+        class B {}
+        class C {}
+        const map = const LookupMap(const [
+          A, "the-text-for-A",
+          B, "the-text-for-B",
+          C, "the-text-for-C",
+        ]);
+        main() {
+          reflectType(A);
+          print(map[A]);
+        }
+    """);
+    expect(generated, contains("the-text-for-A"));
+    expect(generated, contains("the-text-for-B"));
+    expect(generated, contains("the-text-for-C"));
+  });
+
+  test('exclude entries if MirrorsUsed also exclude the type', () async {
+    String generated = await compileAll("""
+        library foo;
+        @MirrorsUsed(targets: const [B])
+        import 'dart:mirrors';
+        import 'package:lookup_map/lookup_map.dart';
+        class A {}
+        class B {}
+        class C {}
+        const map = const LookupMap(const [
+          A, "the-text-for-A",
+          B, "the-text-for-B",
+          C, "the-text-for-C",
+        ]);
+        main() {
+          reflectType(A);
+          print(map[A]);
+        }
+    """);
+    expect(generated, contains("the-text-for-A"));
+    expect(generated, contains("the-text-for-B"));
+    expect(generated, isNot(contains("the-text-for-C")));
   });
 }
diff --git a/tests/compiler/dart2js/mock_compiler.dart b/tests/compiler/dart2js/mock_compiler.dart
index e185712..cda5aa6 100644
--- a/tests/compiler/dart2js/mock_compiler.dart
+++ b/tests/compiler/dart2js/mock_compiler.dart
@@ -124,8 +124,9 @@
                    buildLibrarySource(DEFAULT_INTERCEPTORS_LIBRARY));
     registerSource(JavaScriptBackend.DART_ISOLATE_HELPER,
                    buildLibrarySource(DEFAULT_ISOLATE_HELPER_LIBRARY));
-    registerSource(Uris.dart_mirrors,
-                   buildLibrarySource(DEFAULT_MIRRORS_LIBRARY));
+    registerSource(Uris.dart_mirrors, DEFAULT_MIRRORS_SOURCE);
+    registerSource(JavaScriptBackend.DART_JS_MIRRORS,
+        DEFAULT_JS_MIRRORS_SOURCE);
 
     Map<String, String> asyncLibrarySource = <String, String>{};
     asyncLibrarySource.addAll(DEFAULT_ASYNC_LIBRARY);
diff --git a/tests/compiler/dart2js/mock_libraries.dart b/tests/compiler/dart2js/mock_libraries.dart
index 2031cf0..b10aedb 100644
--- a/tests/compiler/dart2js/mock_libraries.dart
+++ b/tests/compiler/dart2js/mock_libraries.dart
@@ -39,7 +39,10 @@
         static var NAN = 0;
         static parse(s) {}
       }''',
-  'Function': 'class Function {}',
+  'Function': r'''
+      class Function {
+        static apply(Function fn, List positional, [Map named]) => null;
+      }''',
   'identical': 'bool identical(Object a, Object b) { return true; }',
   'int': 'abstract class int extends num { }',
   'Iterable': 'abstract class Iterable {}',
@@ -228,7 +231,12 @@
   'throwRuntimeError': 'throwRuntimeError(message) {}',
   'throwTypeError': 'throwTypeError(message) {}',
   'TypeImpl': 'class TypeImpl {}',
-  'TypeVariable': 'class TypeVariable {}',
+  'TypeVariable': '''class TypeVariable {
+    final Type owner;
+    final String name;
+    final int bound;
+    TypeVariable(this.owner, this.name, this.bound);
+  }''',
   'unwrapException': 'unwrapException(e) {}',
   'voidTypeCheck': 'voidTypeCheck(value) {}',
   'wrapException': 'wrapException(x) { return x; }',
@@ -393,15 +401,27 @@
   '_asyncHelper': '_asyncHelper(o, f, c) {}',
 };
 
-const Map<String, String> DEFAULT_MIRRORS_LIBRARY = const <String, String>{
-  'Comment': 'class Comment {}',
-  'MirrorSystem': 'class MirrorSystem {}',
-  'MirrorsUsed': 'class MirrorsUsed {}',
-};
+const String DEFAULT_MIRRORS_SOURCE = r'''
+import 'dart:_js_mirrors' as js;
+class Comment {}
+class MirrorSystem {}
+class MirrorsUsed {
+  final targets;
+  const MirrorsUsed({this.targets});
+}
+void reflectType(Type t) => js.disableTreeShaking();
+''';
+
+const String DEFAULT_JS_MIRRORS_SOURCE = r'''
+disableTreeShaking(){}
+preserveMetadata(){}
+preserveUris(){}
+preserveLibraryNames(){}
+''';
 
 const Map<String, String> DEFAULT_LOOKUP_MAP_LIBRARY = const <String, String>{
   'LookupMap': r'''
-  class LookupMap<T> {
+  class LookupMap<K, V> {
     final _key;
     final _value;
     final _entries;
@@ -412,5 +432,7 @@
 
     const LookupMap.pair(this._key, this._value)
         : _entries = const [], _nestedMaps = const [];
+    V operator[](K k) => null;
   }''',
+  '_version': 'const _version = "0.0.1";',
 };
diff --git a/tests/compiler/dart2js/simple_inferrer_const_closure_default_test.dart b/tests/compiler/dart2js/simple_inferrer_const_closure_default_test.dart
new file mode 100644
index 0000000..e6d2e92
--- /dev/null
+++ b/tests/compiler/dart2js/simple_inferrer_const_closure_default_test.dart
@@ -0,0 +1,84 @@
+// Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+import "package:async_helper/async_helper.dart";
+import 'compiler_helper.dart';
+import 'type_mask_test_helper.dart';
+
+
+
+const String TEST = """
+
+// [defaultFn_i] is called only via [foo_i]'s default value with a small integer.
+
+defaultFn1(a) => a;
+defaultFn2(a) => a;
+defaultFn3(a) => a;
+defaultFn4(a) => a;
+defaultFn5(a) => a;
+defaultFn6(a) => a;
+
+foo1([fn = defaultFn1]) => fn(54);
+foo2({fn: defaultFn2}) => fn(54);
+foo3([fn = defaultFn3]) => fn(54);
+foo4({fn: defaultFn4}) => fn(54);
+foo5([fn = defaultFn5]) => fn(54);
+foo6({fn: defaultFn6}) => fn(54);
+
+main() {
+  // Direct calls.
+  foo1();
+  foo2();
+  // Indirect calls.
+  (foo3)();
+  (foo4)();
+  // Calls via Function.apply.
+  Function.apply(foo5, []);
+  Function.apply(foo6, []);
+}
+""";
+
+
+void main() {
+  Uri uri = new Uri(scheme: 'source');
+  var compiler = compilerFor(TEST, uri);
+  asyncTest(() => compiler.runCompiler(uri).then((_) {
+    var typesInferrer = compiler.typesTask.typesInferrer;
+
+    checkArgument(String functionName, type) {
+      var functionElement = findElement(compiler, functionName);
+      var signature = functionElement.functionSignature;
+      var element = signature.requiredParameterCount > 0
+          ? signature.requiredParameters.first
+          : signature.optionalParameters.first;
+      Expect.equals(type,
+          simplify(typesInferrer.getTypeOfElement(element), compiler),
+          functionName);
+    }
+
+    checkOptionalArgument(String functionName, type) {
+      var functionElement = findElement(compiler, functionName);
+      var signature = functionElement.functionSignature;
+      var element = signature.optionalParameters.first;
+      Expect.equals(type,
+          simplify(typesInferrer.getTypeOfElement(element), compiler),
+          functionName);
+    }
+
+    checkArgument('foo1', compiler.typesTask.functionType);   /// 01: ok
+    checkArgument('foo2', compiler.typesTask.functionType);   /// 02: ok
+    checkArgument('foo3', compiler.typesTask.functionType);   /// 03: ok
+    checkArgument('foo4', compiler.typesTask.functionType);   /// 04: ok
+    checkArgument('foo5', compiler.typesTask.functionType);   /// 05: ok
+    checkArgument('foo6', compiler.typesTask.functionType);   /// 06: ok
+
+    checkArgument('defaultFn1', compiler.typesTask.uint31Type);   /// 07: ok
+    checkArgument('defaultFn2', compiler.typesTask.uint31Type);   /// 08: ok
+    checkArgument('defaultFn3', compiler.typesTask.uint31Type);   /// 09: ok
+    checkArgument('defaultFn4', compiler.typesTask.uint31Type);   /// 10: ok
+    checkArgument('defaultFn5', compiler.typesTask.uint31Type);   /// 11: ok
+    checkArgument('defaultFn6', compiler.typesTask.uint31Type);   /// 12: ok
+  }));
+}
diff --git a/tests/compiler/dart2js_extra/dart2js_extra.status b/tests/compiler/dart2js_extra/dart2js_extra.status
index b2d49f9..e0321e9 100644
--- a/tests/compiler/dart2js_extra/dart2js_extra.status
+++ b/tests/compiler/dart2js_extra/dart2js_extra.status
@@ -65,11 +65,11 @@
 
 [ $compiler == none && $runtime == vm ]
 invalid_annotation_test/01: MissingCompileTimeError, OK # vm is lazy
+lookup_map/dead_entry_through_mirrors_test: SkipByDesign # Test for tree-shaking, vm never tree-shakes
 
 [ $compiler == dart2js && $cps_ir ]
 16407_test: Pass # Please triage this failure.
 22868_test: RuntimeError # Issue 23997
-22895_test: RuntimeError # Issue 23997
 23432_test: RuntimeError # Issue 23432
 async_stacktrace_test/asyncStar: Crash # (foo()async*{try {tr...  cannot handle sync*/async* functions
 async_stacktrace_test/none: RuntimeError # $async$temp1.Tracer$ is not a function
@@ -84,6 +84,7 @@
 deferred_fail_and_retry_test: Crash # (lib.foo()): deferred access is not implemented
 deferred_fail_and_retry_worker_test: Crash # (lib.foo()): deferred access is not implemented
 deferred_split_test: Crash # (b.createA()): deferred access is not implemented
+lookup_map/live_entry_through_mirrors_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
 mirror_printer_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
 mirror_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
 reflect_native_types_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
diff --git a/tests/compiler/dart2js_extra/lookup_map/dead_entry_through_mirrors_test.dart b/tests/compiler/dart2js_extra/lookup_map/dead_entry_through_mirrors_test.dart
new file mode 100644
index 0000000..6de44b4
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/dead_entry_through_mirrors_test.dart
@@ -0,0 +1,30 @@
+// 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 dead_entry_through_mirrors_test;
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+
+@MirrorsUsed(targets: const [A])
+import 'dart:mirrors';
+
+class A{}
+class B{}
+const map = const LookupMap(const [
+    A, "the-text-for-A",
+    B, "the-text-for-B",
+]);
+
+main() {
+  LibraryMirror lib = currentMirrorSystem().findLibrary(
+      #dead_entry_through_mirrors_test);
+
+  // `A` is included by @MirrorsUsed, so its entry is retained too.
+  ClassMirror aClass = lib.declarations[#A];
+  Expect.equals(map[aClass.reflectedType], "the-text-for-A");
+
+  // `B` is not included altogether.
+  Expect.equals(lib.declarations[#B], null);
+}
diff --git a/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_test.dart b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_test.dart
new file mode 100644
index 0000000..9ad99cf
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_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.
+
+library live_entry_through_mirrors_test;
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+import 'dart:mirrors';
+
+class A{}
+class B{}
+const map = const LookupMap(const [
+    A, "the-text-for-A",
+    B, "the-text-for-B",
+]);
+
+main() {
+  // `A` is referenced explicitly, so its entry should be retained regardless.
+  ClassMirror aClass = reflectClass(A);
+  Expect.equals(map[aClass.reflectedType], "the-text-for-A");
+
+  // `B` is used via mirrors. Because no @MirrorsUsed was found that's enough to
+  // retain the entry.
+  LibraryMirror lib = currentMirrorSystem().findLibrary(
+      #live_entry_through_mirrors_test);
+  ClassMirror bClass = lib.declarations[#B];
+  Expect.equals(map[bClass.reflectedType], "the-text-for-B");
+}
diff --git a/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_used_test.dart b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_used_test.dart
new file mode 100644
index 0000000..c5b752c
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/live_entry_through_mirrors_used_test.dart
@@ -0,0 +1,28 @@
+// 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.
+
+// Subset of dead_entry_through_mirrors_test that is not affected by
+// tree-shaking. This subset can be run in the VM.
+library live_entry_through_mirrors_used_test;
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+
+@MirrorsUsed(targets: const [A])
+import 'dart:mirrors';
+
+class A{}
+class B{}
+const map = const LookupMap(const [
+    A, "the-text-for-A",
+    B, "the-text-for-B",
+]);
+
+main() {
+  // `A` is included by @MirrorsUsed, so its entry is retained too.
+  LibraryMirror lib = currentMirrorSystem().findLibrary(
+      #live_entry_through_mirrors_used_test);
+  ClassMirror aClass = lib.declarations[#A];
+  Expect.equals(map[aClass.reflectedType], "the-text-for-A");
+}
diff --git a/tests/compiler/dart2js_extra/lookup_map/reachable_data2_test.dart b/tests/compiler/dart2js_extra/lookup_map/reachable_data2_test.dart
new file mode 100644
index 0000000..9c82430
--- /dev/null
+++ b/tests/compiler/dart2js_extra/lookup_map/reachable_data2_test.dart
@@ -0,0 +1,23 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:lookup_map/lookup_map.dart';
+import 'package:expect/expect.dart';
+
+class Key { final x; const Key(this.x); }
+const A = const Key(1);
+const B = const Key(2);
+const C = const Key(3);
+const D = const Key(4);
+const E = const Key(5);
+const map = const LookupMap(const [
+    A, const ["the-text-for-A", B],
+    B, const ["the-text-for-B", C],
+    C, const ["the-text-for-C"],
+    D, const ["the-text-for-D", E],
+    E, const ["the-text-for-E"],
+]);
+main() {
+  Expect.equals(map[map[A][1]][0], 'the-text-for-B');
+}
diff --git a/tests/compiler/dart2js_native/dart2js_native.status b/tests/compiler/dart2js_native/dart2js_native.status
index 1ace12b..9d325b2 100644
--- a/tests/compiler/dart2js_native/dart2js_native.status
+++ b/tests/compiler/dart2js_native/dart2js_native.status
@@ -22,7 +22,7 @@
 
 [ $compiler == dart2js && $cps_ir ]
 mirror_intercepted_field_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-native_exception_test: RuntimeError # value_.toString$0 is not a function
+native_exception_test: RuntimeError # J.getInterceptor(...).toString$0 is not a function
 native_method_inlining_test: RuntimeError # Please triage this failure.
 native_mirror_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
 native_no_such_method_exception3_frog_test: RuntimeError # Please triage this failure.
diff --git a/tests/corelib/corelib.status b/tests/corelib/corelib.status
index 092e399..56f3f17 100644
--- a/tests/corelib/corelib.status
+++ b/tests/corelib/corelib.status
@@ -218,7 +218,7 @@
 big_integer_parsed_mul_div_vm_test: Pass, Slow
 
 [ $compiler == dart2js && $cps_ir ]
-data_resource_test: Crash # bailout: (await for(var byteSlice in resource.openRead()){streamBytes.addAll(byteSlice);}): await for
+data_resource_test: Crash # (await for(var byteSlice in resource.openRead()){streamBytes.addAll(byteSlice);}): await for
 error_stack_trace1_test: Pass # H.unwrapException(...).get$stackTrace is not a function
 growable_list_test: RuntimeError # Typed lists
 iterable_empty_test: RuntimeError # Please triage this failure.
@@ -231,7 +231,6 @@
 map_values3_test: RuntimeError # Please triage this failure.
 map_values4_test: RuntimeError # Please triage this failure.
 package_resource_test: Crash # (await for(var byteSlice in resource.openRead()){streamBytes.addAll(byteSlice);}): await for
-regexp/pcre_test: Crash # Stack Overflow
 symbol_operator_test/03: RuntimeError # Please triage this failure.
 symbol_reserved_word_test/03: Pass # Please triage this failure.
 symbol_reserved_word_test/06: RuntimeError # Please triage this failure.
diff --git a/tests/html/html.status b/tests/html/html.status
index 4d21887..aba5566 100644
--- a/tests/html/html.status
+++ b/tests/html/html.status
@@ -116,6 +116,11 @@
 transition_event_test/functional: Skip # Times out. Issue 22167
 request_animation_frame_test: Skip # Times out. Issue 22167
 
+[ $runtime == chrome && $system != linux ]
+# These are failures in Chrome 45, which is not yet on Linux.
+# This will be merged with the earlier blocks when Linux is updated.
+element_animate_test/simple_timing: RuntimeError # Please triage this failure
+
 [$runtime == drt || $runtime == dartium || $runtime == chrome || $runtime == chromeOnAndroid || $runtime == ContentShellOnAndroid ]
 webgl_1_test: Pass, Fail # Issue 8219
 
@@ -201,9 +206,6 @@
 css_test/supportsPointConversions: Fail # Issues 21710
 css_test/functional: Fail # Issues 21710
 
-[ $compiler == dart2js && $cps_ir ]
-resource_http_test: Crash # Cannot handle async functions.
-
 [ $runtime == ie11 ]
 custom/document_register_type_extensions_test/single-parameter: Fail # Issue 13193.
 canvasrenderingcontext2d_test/arc: Pass, Fail # Pixel unexpected value. Please triage this failure.
@@ -421,3 +423,6 @@
 typing_test: StaticWarning
 webgl_1_test: StaticWarning
 window_nosuchmethod_test: StaticWarning
+
+[ $compiler == dart2js && $cps_ir ]
+resource_http_test: Crash # (await for(var b in r.openRead()){bytes.addAll(b);}): await for
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index 05eec1c..1ced213 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -287,7 +287,7 @@
 await_for_test: Crash # (await for(var x in infiniteStream()){i++ ;if(i>10)break;t4.record(x);}): await for
 await_for_use_local_test: Crash # (await for(var v in s){accum+= v;}): await for
 await_future_test: RuntimeError # Please triage this failure.
-await_regression_test: RuntimeError # Please triage this failure.
+await_regression_test: RuntimeError # "Obelix".then$1 is not a function
 cha_deopt1_test: Crash # (d.make_u()): deferred access is not implemented
 cha_deopt2_test: Crash # (d.make_u()): deferred access is not implemented
 cha_deopt3_test: Crash # (d.make_u()): deferred access is not implemented
diff --git a/tests/standalone/full_coverage_test.dart b/tests/standalone/full_coverage_test.dart
index 2ee1979..9eab81d 100644
--- a/tests/standalone/full_coverage_test.dart
+++ b/tests/standalone/full_coverage_test.dart
@@ -49,8 +49,8 @@
   }
 }
 ''',
-    'expectedHits': [-1, 0, 0, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, 1, -1,
-                     0, -1, 1, -1, -1]
+    'expectedHits': [-1, 0, 0, -1, -1, -1, -1, -1, 1, 1, -1, -1, -1, -1, -1,
+                     -1, 0, -1, 1, -1, -1]
   },{
     'name': 'closures',
     'program': '''
@@ -64,7 +64,7 @@
   });
 }
 ''',
-    'expectedHits': [1, -1, 1, -1, -1, 1, 1, -1, -1]
+    'expectedHits': [-1, -1, 1, -1, -1, 1, 1, -1, -1]
   }
 ];
 
diff --git a/tests/standalone/io/http_cookie_date_test.dart b/tests/standalone/io/http_cookie_date_test.dart
index 85385b4..bb7a1a2 100644
--- a/tests/standalone/io/http_cookie_date_test.dart
+++ b/tests/standalone/io/http_cookie_date_test.dart
@@ -8,6 +8,7 @@
 import "dart:async";
 import "dart:collection";
 import "dart:convert";
+import "dart:developer";
 import "dart:math";
 import "dart:typed_data";
 import "dart:isolate";
@@ -28,6 +29,7 @@
 part "../../../sdk/lib/io/http_parser.dart";
 part "../../../sdk/lib/io/http_headers.dart";
 part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
 part "../../../sdk/lib/io/io_service.dart";
 part "../../../sdk/lib/io/io_sink.dart";
 part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/io/http_headers_test.dart b/tests/standalone/io/http_headers_test.dart
index 6e2a4ef..6dbdcd4 100644
--- a/tests/standalone/io/http_headers_test.dart
+++ b/tests/standalone/io/http_headers_test.dart
@@ -8,6 +8,7 @@
 import "dart:async";
 import "dart:collection";
 import "dart:convert";
+import "dart:developer";
 import "dart:math";
 import "dart:typed_data";
 import "dart:isolate";
@@ -28,6 +29,7 @@
 part "../../../sdk/lib/io/http_parser.dart";
 part "../../../sdk/lib/io/http_headers.dart";
 part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
 part "../../../sdk/lib/io/io_service.dart";
 part "../../../sdk/lib/io/io_sink.dart";
 part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/io/http_parser_test.dart b/tests/standalone/io/http_parser_test.dart
index 9d8e014..95edf3b 100644
--- a/tests/standalone/io/http_parser_test.dart
+++ b/tests/standalone/io/http_parser_test.dart
@@ -8,6 +8,7 @@
 import "dart:async";
 import "dart:collection";
 import "dart:convert";
+import "dart:developer";
 import "dart:math";
 import "dart:typed_data";
 import "dart:isolate";
@@ -28,6 +29,7 @@
 part "../../../sdk/lib/io/http_parser.dart";
 part "../../../sdk/lib/io/http_headers.dart";
 part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
 part "../../../sdk/lib/io/io_service.dart";
 part "../../../sdk/lib/io/io_sink.dart";
 part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/io/web_socket_protocol_processor_test.dart b/tests/standalone/io/web_socket_protocol_processor_test.dart
index ba75622..0f7c1fa 100644
--- a/tests/standalone/io/web_socket_protocol_processor_test.dart
+++ b/tests/standalone/io/web_socket_protocol_processor_test.dart
@@ -9,6 +9,7 @@
 import "dart:async";
 import "dart:collection";
 import "dart:convert";
+import "dart:developer";
 import "dart:math";
 import "dart:typed_data";
 import "dart:isolate";
@@ -29,6 +30,7 @@
 part "../../../sdk/lib/io/http_parser.dart";
 part "../../../sdk/lib/io/http_headers.dart";
 part "../../../sdk/lib/io/http_session.dart";
+part "../../../sdk/lib/io/io_resource_info.dart";
 part "../../../sdk/lib/io/io_service.dart";
 part "../../../sdk/lib/io/io_sink.dart";
 part "../../../sdk/lib/io/platform.dart";
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 9bb7ec3..b6e570b 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -186,16 +186,8 @@
 io/test_runner_test: Skip  # Timeout.
 io/http_client_stays_alive_test: Skip  # Timeout.
 
-[ $compiler == dart2js && $cps_ir ]
-coverage_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-io/addlatexhash_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-io/file_error_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-io/file_read_encoded_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-io/file_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-io/http_client_connect_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-io/observatory_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-io/skipping_dart2js_compilations_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
-priority_queue_stress_test: RuntimeError # Cannot read property 'length' of undefined
+[ $compiler == dart2js ]
+io/observatory_test: Crash # Issue 24291
 
 [ $runtime == vm ]
 # Failures in secure networking while NSS is replaced with BoringSSL
@@ -204,3 +196,13 @@
 io/secure_server_client_no_certificate_test: RuntimeError # Issue 24069
 io/secure_socket_renegotiate_test: RuntimeError
 io/secure_socket_bad_data_test: RuntimeError  # An error in a secure connection just puts a READ_CLOSED on the stream, rather than signaling an error on the stream.
+
+[ $compiler == dart2js && $cps_ir ]
+coverage_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
+io/addlatexhash_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
+io/file_error_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
+io/file_read_encoded_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
+io/file_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
+io/http_client_connect_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
+io/skipping_dart2js_compilations_test: Crash # (static Iterable<Str...  cannot handle sync*/async* functions
+priority_queue_stress_test: RuntimeError # Cannot read property 'length' of undefined
diff --git a/tools/VERSION b/tools/VERSION
index 5ef9ccf..03c8deb 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 1
 MINOR 13
 PATCH 0
-PRERELEASE 1
+PRERELEASE 2
 PRERELEASE_PATCH 0
diff --git a/tools/precompilation/create_instructions_snapshot_assembly.dart b/tools/precompilation/create_instructions_snapshot_assembly.dart
deleted file mode 100644
index 98cccb2..0000000
--- a/tools/precompilation/create_instructions_snapshot_assembly.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'dart:io';
-
-void main(List<String> args) {
-  print(args[0]);
-  print(args[1]);
-
-  var bytes = new File(args[0]).readAsBytesSync();
-  print(bytes.length);
-
-  var out = new StringBuffer();
-  out.writeln(".text");
-  out.writeln("  .globl _kInstructionsSnapshot");
-  out.writeln("_kInstructionsSnapshot:");
-  out.writeln("  .balign 32, 0");
-  for (var i = 0; i < bytes.length; i++) {
-    var byte = bytes[i];
-    out.writeln("  .byte $byte");
-  }
-
-  new File(args[1]).writeAsString(out.toString());
-}
diff --git a/tools/precompilation/test_linux.sh b/tools/precompilation/test_linux.sh
index 14f7fa4..e8839e1 100755
--- a/tools/precompilation/test_linux.sh
+++ b/tools/precompilation/test_linux.sh
@@ -8,10 +8,6 @@
 
 ./out/DebugX64/dart_no_snapshot --gen-precompiled-snapshot ~/hello.dart
 
-./out/DebugX64/dart ./tools/precompilation/create_instructions_snapshot_assembly.dart precompiled.instructions precompiled.S
-
-gcc -m64 -c -o precompiled.o precompiled.S
-
-gcc -m64 -shared -Wl,-soname,libprecompiled.so -o libprecompiled.so precompiled.o
+gcc -m64 -shared -Wl,-soname,libprecompiled.so -o libprecompiled.so precompiled.S
 
 LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD" gdb -ex run --args ./out/DebugX64/dart --run-precompiled-snapshot not_used.dart
diff --git a/tools/precompilation/test_linux_simarm.sh b/tools/precompilation/test_linux_simarm.sh
new file mode 100755
index 0000000..b55a5e9
--- /dev/null
+++ b/tools/precompilation/test_linux_simarm.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -ex
+
+# Usage:
+#   cd sdk
+#   ./tools/precompilation/test_linux.sh
+
+./tools/build.py -mdebug -asimarm runtime
+
+./out/DebugSIMARM/dart_no_snapshot --gen-precompiled-snapshot ~/hello.dart
+
+gcc -m32 -shared -Wl,-soname,libprecompiled.so -o libprecompiled.so precompiled.S
+
+LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD" gdb -ex run --args ./out/DebugSIMARM/dart --run-precompiled-snapshot not_used.dart
diff --git a/tools/precompilation/test_macos.sh b/tools/precompilation/test_macos.sh
index a233004..48e318d 100755
--- a/tools/precompilation/test_macos.sh
+++ b/tools/precompilation/test_macos.sh
@@ -8,8 +8,6 @@
 
 ./xcodebuild/DebugX64/dart_no_snapshot --gen-precompiled-snapshot ~/hello.dart
 
-./xcodebuild/DebugX64/dart ./tools/precompilation/create_instructions_snapshot_assembly.dart precompiled.instructions precompiled.S
-
 clang -m64 -dynamiclib -o libprecompiled.dylib precompiled.S
 
 LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$PWD" lldb -- ./xcodebuild/DebugX64/dart --run-precompiled-snapshot not_used.dart