Version 1.12.0-dev.5.2

Cherry-pick 206083c8b1520c77e66c2eb138aa88fc45a49321 to dev
Cherry-pick 3cbedb02e418ccbad3b865ba5bd2ee558de1c7ef to dev
Cherry-pick 7e9fcbf5d6942ef14a287984ee72207469f34943 to dev
Cherry-pick 49daca25d0fe85ee47c88a959f0007b18e60d4f7 to dev
Cherry-pick 6377f1858726949354437381f596d82316c361dd to dev
Cherry-pick d71110f31fbfe1c17fbd516a111968e06dfffd7d to dev
Cherry-pick 1426ac4f77eb1669775baee49cee7ea5a79b7987 to dev
Cherry-pick f73b8aa019adcc1a443ceb0f61953853130fa528 to dev
Cherry-pick e974b952a73dffa72ed1ea87f5f8cd85f8790782 to dev
Cherry-pick 223e5a6bb5877dd4c3c46cf1f097172c879e8424 to dev
Cherry-pick ee9c0cbb2c86935bab2a31c241ebbbe01466e9e0 to dev
Cherry-pick b6c979f20b01239e92e7acc3ad34b879d4d95b83 to dev
Cherry-pick 227010d9dc84c2e5bf042ca1c0367f36eacbfa6b to dev
Cherry-pick 521dee763c95a374031e06572d3f56ceb8e8d91f to dev
Cherry-pick 38b9945cfd7cb2cc587df013d4d31ae028d99f6b to dev
Cherry-pick b34267d0d79d43bc343a26901f67a00a176f4bb8 to dev
Cherry-pick 49a61ba3f53a135cd7ce6085fb0afc642c50d123 to dev
Cherry-pick 8cee9ac89d92f702950c27e2982177ea1da5560b to dev
Cherry-pick 97a4ae8f9496b6740cff67b1664258b51c0a7f68 to dev
Cherry-pick 756763f3ca17737a464d8482303cebf068c7599d to dev
Cherry-pick 2b2d4cf1bf355648e218542e9c7d4641999fb5bc to dev
Cherry-pick 447e497ac985d7f13dfb1ad2d98a5b1c2e8fc709 to dev
Cherry-pick fc981a21c5b141aaf2526073988d7545ec851c16 to dev
Cherry-pick 5b6136fdfb0addf98abc52fff1c605a84bfd465c to dev
Cherry-pick a5c0dbfffe58ab3a568b4928da3e6a3b79315262 to dev
Cherry-pick 55b7315d8387268e43cac325106dd62b241b2c7f to dev
Cherry-pick e61f1dc8ce48789166121364bcbc961f45dc38be to dev
Cherry-pick 256b044857a44977ba1b4ca58d3d96ab39765bdf to dev
diff --git a/pkg/analysis_server/lib/src/context_manager.dart b/pkg/analysis_server/lib/src/context_manager.dart
index 41fb266..b879a36 100644
--- a/pkg/analysis_server/lib/src/context_manager.dart
+++ b/pkg/analysis_server/lib/src/context_manager.dart
@@ -729,7 +729,7 @@
    * dependencies (currently we only use it to track "pub list" dependencies).
    */
   FolderDisposition _computeFolderDisposition(
-      Folder folder, void addDependency(String path)) {
+      Folder folder, void addDependency(String path), File packagespecFile) {
     String packageRoot = normalizedPackageRoots[folder.path];
     if (packageRoot != null) {
       // TODO(paulberry): We shouldn't be using JavaFile here because it
@@ -762,18 +762,28 @@
       // resolve packages.
       return new NoPackageFolderDisposition(packageRoot: packageRoot);
     } else {
-      callbacks.beginComputePackageMap();
-      if (packageResolverProvider != null) {
-        UriResolver resolver = packageResolverProvider(folder);
-        if (resolver != null) {
-          return new CustomPackageResolverDisposition(resolver);
-        }
-      }
       PackageMapInfo packageMapInfo;
-      ServerPerformanceStatistics.pub.makeCurrentWhile(() {
-        packageMapInfo = _packageMapProvider.computePackageMap(folder);
-      });
-      callbacks.endComputePackageMap();
+      callbacks.beginComputePackageMap();
+      try {
+        if (ENABLE_PACKAGESPEC_SUPPORT) {
+          // Try .packages first.
+          if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
+            Packages packages = _readPackagespec(packagespecFile);
+            return new PackagesFileDisposition(packages);
+          }
+        }
+        if (packageResolverProvider != null) {
+          UriResolver resolver = packageResolverProvider(folder);
+          if (resolver != null) {
+            return new CustomPackageResolverDisposition(resolver);
+          }
+        }
+        ServerPerformanceStatistics.pub.makeCurrentWhile(() {
+          packageMapInfo = _packageMapProvider.computePackageMap(folder);
+        });
+      } finally {
+        callbacks.endComputePackageMap();
+      }
       for (String dependencyPath in packageMapInfo.dependencies) {
         addDependency(dependencyPath);
       }
@@ -797,17 +807,10 @@
     FolderDisposition disposition;
     List<String> dependencies = <String>[];
 
-    if (ENABLE_PACKAGESPEC_SUPPORT) {
-      // Try .packages first.
-      if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) {
-        Packages packages = _readPackagespec(packagespecFile);
-        disposition = new PackagesFileDisposition(packages);
-      }
-    }
-
     // Next resort to a package uri resolver.
     if (disposition == null) {
-      disposition = _computeFolderDisposition(folder, dependencies.add);
+      disposition =
+          _computeFolderDisposition(folder, dependencies.add, packagespecFile);
     }
 
     info.setDependencies(dependencies);
@@ -832,18 +835,7 @@
       ContextInfo parent, Folder folder, bool withPackageSpecOnly) {
     // Decide whether a context needs to be created for [folder] here, and if
     // so, create it.
-    File packageSpec;
-
-    if (ENABLE_PACKAGESPEC_SUPPORT) {
-      // Start by looking for .packages.
-      packageSpec = folder.getChild(PACKAGE_SPEC_NAME);
-    }
-
-    // Fall back to looking for a pubspec.
-    if (packageSpec == null || !packageSpec.exists) {
-      packageSpec = folder.getChild(PUBSPEC_NAME);
-    }
-
+    File packageSpec = _findPackageSpecFile(folder);
     bool createContext = packageSpec.exists || !withPackageSpecOnly;
     if (withPackageSpecOnly &&
         packageSpec.exists &&
@@ -928,6 +920,28 @@
   }
 
   /**
+   * Find the file that should be used to determine whether a context needs to
+   * be created here--this is either the ".packages" file or the "pubspec.yaml"
+   * file.
+   */
+  File _findPackageSpecFile(Folder folder) {
+    // Decide whether a context needs to be created for [folder] here, and if
+    // so, create it.
+    File packageSpec;
+
+    if (ENABLE_PACKAGESPEC_SUPPORT) {
+      // Start by looking for .packages.
+      packageSpec = folder.getChild(PACKAGE_SPEC_NAME);
+    }
+
+    // Fall back to looking for a pubspec.
+    if (packageSpec == null || !packageSpec.exists) {
+      packageSpec = folder.getChild(PUBSPEC_NAME);
+    }
+    return packageSpec;
+  }
+
+  /**
    * Return the [ContextInfo] for the "innermost" context whose associated
    * folder is or contains the given path.  ("innermost" refers to the nesting
    * of contexts, so if there is a context for path /foo and a context for
@@ -1178,8 +1192,8 @@
     // while we're rerunning "pub list", since any analysis we complete while
     // "pub list" is in progress is just going to get thrown away anyhow.
     List<String> dependencies = <String>[];
-    FolderDisposition disposition =
-        _computeFolderDisposition(info.folder, dependencies.add);
+    FolderDisposition disposition = _computeFolderDisposition(
+        info.folder, dependencies.add, _findPackageSpecFile(info.folder));
     info.setDependencies(dependencies);
     callbacks.updateContextPackageUriResolver(info.folder, disposition);
   }
diff --git a/pkg/analysis_server/test/context_manager_test.dart b/pkg/analysis_server/test/context_manager_test.dart
index d81f338..14b24a9 100644
--- a/pkg/analysis_server/test/context_manager_test.dart
+++ b/pkg/analysis_server/test/context_manager_test.dart
@@ -555,6 +555,20 @@
         equals('/home/somebody/.pub/cache/unittest-0.9.9/lib/unittest.dart'));
   }
 
+  void test_setRoots_addFolderWithPackagespecAndPackageRoot() {
+    // The package root should take priority.
+    String packagespecPath = posix.join(projPath, '.packages');
+    resourceProvider.newFile(packagespecPath,
+        'unittest:file:///home/somebody/.pub/cache/unittest-0.9.9/lib/');
+    String packageRootPath = '/package/root/';
+    manager.setRoots(<String>[projPath], <String>[],
+        <String, String>{projPath: packageRootPath});
+    expect(callbacks.currentContextPaths, hasLength(1));
+    expect(callbacks.currentContextPaths, contains(projPath));
+    expect(callbacks.currentContextDispositions[projPath].packageRoot,
+        packageRootPath);
+  }
+
   void test_setRoots_addFolderWithPubspec() {
     String pubspecPath = posix.join(projPath, 'pubspec.yaml');
     resourceProvider.newFile(pubspecPath, 'pubspec');
diff --git a/pkg/compiler/lib/src/apiimpl.dart b/pkg/compiler/lib/src/apiimpl.dart
index 808f88f1..123938d 100644
--- a/pkg/compiler/lib/src/apiimpl.dart
+++ b/pkg/compiler/lib/src/apiimpl.dart
@@ -93,6 +93,7 @@
             showPackageWarnings:
                 hasOption(options, '--show-package-warnings'),
             useContentSecurityPolicy: hasOption(options, '--csp'),
+            useStartupEmitter: hasOption(options, '--fast-startup'),
             hasIncrementalSupport:
                 forceIncrementalSupport ||
                 hasOption(options, '--incremental-support'),
diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart
index 1ed9781..f635379 100644
--- a/pkg/compiler/lib/src/compiler.dart
+++ b/pkg/compiler/lib/src/compiler.dart
@@ -1056,6 +1056,7 @@
             this.deferredMapUri: null,
             this.dumpInfo: false,
             this.showPackageWarnings: false,
+            bool useStartupEmitter: false,
             this.useContentSecurityPolicy: false,
             this.suppressWarnings: false,
             this.fatalWarnings: false,
@@ -1100,7 +1101,8 @@
     if (emitJavaScript) {
       js_backend.JavaScriptBackend jsBackend =
           new js_backend.JavaScriptBackend(
-              this, generateSourceMap: generateSourceMap);
+              this, generateSourceMap: generateSourceMap,
+              useStartupEmitter: useStartupEmitter);
       backend = jsBackend;
     } else {
       backend = new dart_backend.DartBackend(this, strips,
diff --git a/pkg/compiler/lib/src/dart2js.dart b/pkg/compiler/lib/src/dart2js.dart
index 24a058f..72c40ab 100644
--- a/pkg/compiler/lib/src/dart2js.dart
+++ b/pkg/compiler/lib/src/dart2js.dart
@@ -317,6 +317,7 @@
     new OptionHandler('--library-root=.+', setLibraryRoot),
     new OptionHandler('--out=.+|-o.*', setOutput, multipleArguments: true),
     new OptionHandler('--allow-mock-compilation', passThrough),
+    new OptionHandler('--fast-startup', passThrough),
     new OptionHandler('--minify|-m', implyCompilation),
     new OptionHandler('--preserve-uris', passThrough),
     new OptionHandler('--force-strip=.*', setStrip),
diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart
index 91c4765..d17fffd 100644
--- a/pkg/compiler/lib/src/js_backend/backend.dart
+++ b/pkg/compiler/lib/src/js_backend/backend.dart
@@ -622,7 +622,8 @@
   final SourceInformationStrategy sourceInformationStrategy;
 
   JavaScriptBackend(Compiler compiler,
-                    {bool generateSourceMap: true})
+                    {bool generateSourceMap: true,
+                     bool useStartupEmitter: false})
       : namer = determineNamer(compiler),
         oneShotInterceptors = new Map<jsAst.Name, Selector>(),
         interceptedElements = new Map<String, Set<Element>>(),
@@ -636,7 +637,8 @@
                      : const StartEndSourceInformationStrategy())
                 : const JavaScriptSourceInformationStrategy(),
         super(compiler) {
-    emitter = new CodeEmitterTask(compiler, namer, generateSourceMap);
+    emitter = new CodeEmitterTask(
+        compiler, namer, generateSourceMap, useStartupEmitter);
     typeVariableHandler = new TypeVariableHandler(compiler);
     customElementsAnalysis = new CustomElementsAnalysis(this);
     noSuchMethodRegistry = new NoSuchMethodRegistry(this);
diff --git a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
index b069aaf..bea4320 100644
--- a/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
+++ b/pkg/compiler/lib/src/js_emitter/code_emitter_task.dart
@@ -5,8 +5,6 @@
 part of dart2js.js_emitter;
 
 const USE_LAZY_EMITTER = const bool.fromEnvironment("dart2js.use.lazy.emitter");
-const USE_STARTUP_EMITTER =
-    const bool.fromEnvironment("dart2js.use.startup.emitter");
 
 /**
  * Generates the code for all used classes in the program. Static fields (even
@@ -35,14 +33,15 @@
   /// Contains a list of all classes that are emitted.
   Set<ClassElement> neededClasses;
 
-  CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap)
+  CodeEmitterTask(Compiler compiler, Namer namer, bool generateSourceMap,
+                  bool useStartupEmitter)
       : super(compiler),
         this.namer = namer,
         this.typeTestRegistry = new TypeTestRegistry(compiler) {
     nativeEmitter = new NativeEmitter(this);
     if (USE_LAZY_EMITTER) {
       emitter = new lazy_js_emitter.Emitter(compiler, namer, nativeEmitter);
-    } else if (USE_STARTUP_EMITTER) {
+    } else if (useStartupEmitter) {
       emitter = new startup_js_emitter.Emitter(
           compiler, namer, nativeEmitter, generateSourceMap);
     } else {
diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
index 01fda29..0bc38d3 100644
--- a/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/declarations.dart
@@ -13,28 +13,6 @@
 typedef jsAst.Property AddPropertyFunction(jsAst.Name name,
                                            jsAst.Expression value);
 
-const String GENERATED_BY = """
-// Generated by dart2js, the Dart to JavaScript compiler.
-""";
-
-const String HOOKS_API_USAGE = """
-// The code supports the following hooks:
-// dartPrint(message):
-//    if this function is defined it is called instead of the Dart [print]
-//    method.
-//
-// dartMainRunner(main, args):
-//    if this function is defined, the Dart [main] method will not be invoked
-//    directly. Instead, a closure that will invoke [main], and its arguments
-//    [args] is passed to [dartMainRunner].
-//
-// dartDeferredLibraryLoader(uri, successCallback, errorCallback):
-//    if this function is defined, it will be called when a deferered library
-//    is loaded. It should load and eval the javascript of `uri`, and call
-//    successCallback. If it fails to do so, it should call errorCallback with
-//    an error.
-""";
-
 // Compact field specifications.  The format of the field specification is
 // <accessorName>:<fieldName><suffix> where the suffix and accessor name
 // prefix are optional.  The suffix directs the generation of getter and
diff --git a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
index 60382c1..319bc72 100644
--- a/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/full_emitter/emitter.dart
@@ -12,6 +12,8 @@
     JsBuiltin,
     JsGetName;
 
+import '../headers.dart';
+
 import '../js_emitter.dart' hide Emitter;
 import '../js_emitter.dart' as js_emitter show Emitter;
 
@@ -2072,11 +2074,10 @@
   }
 
   jsAst.Comment buildGeneratedBy() {
-    String suffix = '';
-    if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
-    String msg = '// Generated by dart2js, the Dart to JavaScript '
-                 'compiler$suffix.';
-    return new jsAst.Comment(msg);
+    List<String> options = [];
+    if (compiler.mirrorsLibrary != null) options.add('mirrors');
+    if (compiler.useContentSecurityPolicy) options.add("CSP");
+    return new jsAst.Comment(generatedBy(compiler, flavor: options.join(", ")));
   }
 
   void outputSourceMap(CodeOutput output,
diff --git a/pkg/compiler/lib/src/js_emitter/headers.dart b/pkg/compiler/lib/src/js_emitter/headers.dart
new file mode 100644
index 0000000..7906dd9
--- /dev/null
+++ b/pkg/compiler/lib/src/js_emitter/headers.dart
@@ -0,0 +1,32 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library dart2js.js_emitter.headers;
+import '../dart2jslib.dart' show Compiler;
+
+String generatedBy(Compiler compiler, {String flavor: ""}) {
+  String suffix = '';
+  if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
+  if (flavor != "") flavor = " ($flavor)";
+  return '// Generated by dart2js$flavor, '
+      'the Dart to JavaScript compiler$suffix.';
+}
+
+const String HOOKS_API_USAGE = """
+// The code supports the following hooks:
+// dartPrint(message):
+//    if this function is defined it is called instead of the Dart [print]
+//    method.
+//
+// dartMainRunner(main, args):
+//    if this function is defined, the Dart [main] method will not be invoked
+//    directly. Instead, a closure that will invoke [main], and its arguments
+//    [args] is passed to [dartMainRunner].
+//
+// dartDeferredLibraryLoader(uri, successCallback, errorCallback):
+//    if this function is defined, it will be called when a deferered library
+//    is loaded. It should load and eval the javascript of `uri`, and call
+//    successCallback. If it fails to do so, it should call errorCallback with
+//    an error.
+""";
diff --git a/pkg/compiler/lib/src/js_emitter/model.dart b/pkg/compiler/lib/src/js_emitter/model.dart
index fe83370..7e4703e 100644
--- a/pkg/compiler/lib/src/js_emitter/model.dart
+++ b/pkg/compiler/lib/src/js_emitter/model.dart
@@ -378,6 +378,9 @@
   /// The element should only be used during the transition to the new model.
   /// Uses indicate missing information in the model.
   final Element element;
+  /// The name of the method. If the method is a [ParameterStubMethod] for a
+  /// static function, then the name can be `null`. In that case, only the
+  /// [ParameterStubMethod.callName] should be used.
   final js.Name name;
   final js.Expression code;
 
diff --git a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
index 92cf30e..44350b5 100644
--- a/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
+++ b/pkg/compiler/lib/src/js_emitter/parameter_stub_generator.dart
@@ -148,7 +148,7 @@
 
     jsAst.Fun function = js('function(#) { #; }', [parametersBuffer, body]);
 
-    jsAst.Name name = namer.invocationName(selector);
+    jsAst.Name name = member.isStatic ? null : namer.invocationName(selector);
     jsAst.Name callName =
         (callSelector != null) ? namer.invocationName(callSelector) : null;
     return new ParameterStubMethod(name, callName, function);
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
index f0b5a78..c4e0012 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart
@@ -518,9 +518,10 @@
   js.Statement emitDeferredInitializerGlobal(Map loadMap) {
     if (loadMap.isEmpty) return new js.Block.empty();
 
-    return js.js.statement("""
-    if (typeof(${ModelEmitter.deferredInitializersGlobal}) === 'undefined')
-      var ${ModelEmitter.deferredInitializersGlobal} = Object.create(null);""");
+    String global = ModelEmitter.deferredInitializersGlobal;
+    return js.js.statement(
+        "if (typeof($global) === 'undefined') var # = Object.create(null);",
+        new js.VariableDeclaration(global, allowRename: false));
   }
 
   /// Emits all holders, except for the static-state holder.
diff --git a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
index e30c58b..ed24300 100644
--- a/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
+++ b/pkg/compiler/lib/src/js_emitter/startup_emitter/model_emitter.dart
@@ -34,6 +34,7 @@
 import '../../util/uri_extras.dart' show
     relativize;
 
+import '../headers.dart';
 import '../js_emitter.dart' show AstContainer, NativeEmitter;
 
 import 'package:js_runtime/shared/embedded_names.dart' show
@@ -218,11 +219,11 @@
   }
 
   /// Generates a simple header that provides the compiler's build id.
-  String buildGeneratedBy(compiler) {
-    var suffix = '';
-    if (compiler.hasBuildId) suffix = ' version: ${compiler.buildId}';
-    return '// Generated by dart2js (fast startup), '
-        'the Dart to JavaScript compiler$suffix.\n';
+  js.Comment buildGeneratedBy() {
+    String flavor = compiler.useContentSecurityPolicy
+        ? 'fast startup, CSP'
+        : 'fast startup';
+    return new js.Comment(generatedBy(compiler, flavor: flavor));
   }
 
   /// Writes all deferred fragment's code into files.
@@ -258,7 +259,12 @@
             codeOutputListeners);
     outputBuffers[fragment] = mainOutput;
 
-    mainOutput.addBuffer(js.prettyPrint(code, compiler,
+    js.Program program = new js.Program([
+        buildGeneratedBy(),
+        new js.Comment(HOOKS_API_USAGE),
+        code]);
+
+    mainOutput.addBuffer(js.prettyPrint(program, compiler,
         monitor: compiler.dumpInfoTask));
 
     if (shouldGenerateSourceMap) {
@@ -307,9 +313,11 @@
     //   deferredInitializer.current = <pretty-printed code>;
     //   deferredInitializer[<hash>] = deferredInitializer.current;
 
-    output.add('\n${deferredInitializersGlobal}.current = ');
+    js.Program program = new js.Program([
+        buildGeneratedBy(),
+        js.js.statement('$deferredInitializersGlobal.current = #', code)]);
 
-    output.addBuffer(js.prettyPrint(code, compiler,
+    output.addBuffer(js.prettyPrint(program, compiler,
         monitor: compiler.dumpInfoTask));
 
     // Make a unique hash of the code (before the sourcemaps are added)
diff --git a/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart b/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart
index b29b582..9a2cbe2 100644
--- a/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart
+++ b/pkg/compiler/lib/src/mirrors/dart2js_instance_mirrors.dart
@@ -18,6 +18,10 @@
                         [Map<Symbol, dynamic> namedArguments]) {
     throw new UnsupportedError('ObjectMirror.invoke unsupported.');
   }
+
+  delegate(Invocation invocation) {
+    throw new UnsupportedError('ObjectMirror.delegate unsupported');
+  }
 }
 
 abstract class InstanceMirrorMixin implements InstanceMirror {
@@ -26,10 +30,6 @@
   get reflectee {
     throw new UnsupportedError('InstanceMirror.reflectee unsupported.');
   }
-
-  delegate(Invocation invocation) {
-    throw new UnsupportedError('InstanceMirror.delegate unsupported');
-  }
 }
 
 InstanceMirror _convertConstantToInstanceMirror(
diff --git a/runtime/bin/builtin.dart b/runtime/bin/builtin.dart
index d678665..feb26a9 100644
--- a/runtime/bin/builtin.dart
+++ b/runtime/bin/builtin.dart
@@ -309,13 +309,15 @@
 
 
 void _finishLoadRequest(_LoadRequest req) {
-  // Now that we are done with loading remove the request from the map.
-  var tmp = _reqMap.remove(req._id);
-  assert(tmp == req);
-  if (_traceLoading) {
-    _log("Loading of ${req._uri} finished: "
-         "${_reqMap.length} requests remaining, "
-         "${_pendingPackageLoads.length} packages pending.");
+  if (req != null) {
+    // Now that we are done with loading remove the request from the map.
+    var tmp = _reqMap.remove(req._id);
+    assert(tmp == req);
+    if (_traceLoading) {
+      _log("Loading of ${req._uri} finished: "
+           "${_reqMap.length} requests remaining, "
+           "${_pendingPackageLoads.length} packages pending.");
+    }
   }
 
   if (!_pendingLoads()) {
@@ -447,6 +449,8 @@
   // Reset the pending package loads to empty. So that we eventually can
   // finish loading.
   _pendingPackageLoads = [];
+  // Make sure that the receive port is closed if no other loads are pending.
+  _finishLoadRequest(null);
 }
 
 
diff --git a/runtime/bin/platform_macos.cc b/runtime/bin/platform_macos.cc
index 6c83fc8..e970d6e 100644
--- a/runtime/bin/platform_macos.cc
+++ b/runtime/bin/platform_macos.cc
@@ -12,7 +12,9 @@
 #include "bin/file.h"
 #include "bin/platform.h"
 
+#if !defined(TARGET_OS_IOS)
 #include <crt_externs.h>  // NOLINT
+#endif  // !defined(TARGET_OS_IOS)
 #include <signal.h>  // NOLINT
 #include <string.h>  // NOLINT
 #include <unistd.h>  // NOLINT
diff --git a/runtime/bin/vmservice/loader.dart b/runtime/bin/vmservice/loader.dart
index 68f63d9..cd94f57 100644
--- a/runtime/bin/vmservice/loader.dart
+++ b/runtime/bin/vmservice/loader.dart
@@ -397,7 +397,7 @@
           sp.send([packageRoot.toString()]);
         }
       } else {
-        sp.send("Unsupported base URI to identify .packages file: "
+        sp.send("Unsupported scheme used to locate .packages file: "
                 "'$resource'.");
       }
     } else if (id == -2) {
diff --git a/runtime/lib/array_patch.dart b/runtime/lib/array_patch.dart
index 109938b..abe5a19 100644
--- a/runtime/lib/array_patch.dart
+++ b/runtime/lib/array_patch.dart
@@ -45,6 +45,10 @@
       list.add(e);
     }
     if (growable) return list;
+    if (list.length == 0) {
+      // Avoid getting an immutable list from makeListFixedLength.
+      return new List<E>(0);
+    }
     return makeListFixedLength(list);
   }
 
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index 24c9523..d68e758 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -1077,7 +1077,7 @@
   Field& field = Field::Handle();
   for (intptr_t i = 0; i < num_fields; i++) {
     field ^= fields.At(i);
-    if (!field.is_synthetic()) {
+    if (field.is_reflectable()) {
       member_mirror = CreateVariableMirror(field, owner_mirror);
       member_mirrors.Add(member_mirror);
     }
@@ -1172,7 +1172,7 @@
       }
     } else if (entry.IsField()) {
       const Field& field = Field::Cast(entry);
-      if (!field.is_synthetic()) {
+      if (field.is_reflectable()) {
         member_mirror = CreateVariableMirror(field, owner_mirror);
         member_mirrors.Add(member_mirror);
       }
@@ -1609,7 +1609,7 @@
     return result.raw();
   }
 
-  if (field.is_final()) {
+  if (field.is_final() || !field.is_reflectable()) {
     ThrowNoSuchMethod(AbstractType::Handle(klass.RareType()),
                       internal_setter_name,
                       setter,
@@ -1906,7 +1906,7 @@
     return result.raw();
   }
 
-  if (field.is_final()) {
+  if (field.is_final() || !field.is_reflectable()) {
     ThrowNoSuchMethod(Instance::null_instance(),
                       internal_setter_name,
                       setter,
diff --git a/runtime/lib/mirrors_impl.dart b/runtime/lib/mirrors_impl.dart
index c64be5d..0c7b0bb 100644
--- a/runtime/lib/mirrors_impl.dart
+++ b/runtime/lib/mirrors_impl.dart
@@ -371,6 +371,25 @@
     this._invokeSetter(_reflectee, _n(memberName), value);
     return reflect(value);
   }
+
+  delegate(Invocation invocation) {
+    if (invocation.isMethod) {
+      return this.invoke(invocation.memberName,
+                         invocation.positionalArguments,
+                         invocation.namedArguments).reflectee;
+    }
+    if (invocation.isGetter) {
+      return this.getField(invocation.memberName).reflectee;
+    }
+    if (invocation.isSetter) {
+      var unwrapped = _n(invocation.memberName);
+      var withoutEqual = _s(unwrapped.substring(0, unwrapped.length - 1));
+      var arg = invocation.positionalArguments[0];
+      this.setField(withoutEqual, arg).reflectee;
+      return arg;
+    }
+    throw "UNREACHABLE";
+  }
 }
 
 class _LocalInstanceMirror extends _LocalObjectMirror
@@ -393,25 +412,6 @@
 
   get reflectee => _reflectee;
 
-  delegate(Invocation invocation) {
-    if (invocation.isMethod) {
-      return this.invoke(invocation.memberName,
-                         invocation.positionalArguments,
-                         invocation.namedArguments).reflectee;
-    }
-    if (invocation.isGetter) {
-      return this.getField(invocation.memberName).reflectee;
-    }
-    if (invocation.isSetter) {
-      var unwrapped = _n(invocation.memberName);
-      var withoutEqual = _s(unwrapped.substring(0, unwrapped.length - 1));
-      var arg = invocation.positionalArguments[0];
-      this.setField(withoutEqual, arg).reflectee;
-      return arg;
-    }
-    throw "UNREACHABLE";
-  }
-
   String toString() => 'InstanceMirror on ${Error.safeToString(_reflectee)}';
 
   bool operator ==(other) {
diff --git a/runtime/observatory/lib/object_graph.dart b/runtime/observatory/lib/object_graph.dart
index 60e720d..ddbfdc2 100644
--- a/runtime/observatory/lib/object_graph.dart
+++ b/runtime/observatory/lib/object_graph.dart
@@ -116,6 +116,14 @@
     }
   }
 
+  int get highUint32 {
+    return high * (1 << 24) + (mid >> 4);
+  }
+
+  int get lowUint32 {
+    return (mid & 0xF) * (1 << 28) + low;
+  }
+
   bool get isZero {
     return (high == 0) && (mid == 0) && (low == 0);
   }
@@ -224,22 +232,8 @@
   int get retainedSize => _graph._retainedSizes[_id];
   ObjectVertex get dominator => new ObjectVertex._(_graph._doms[_id], _graph);
 
-  int get shallowSize {
-    var stream = new ReadStream(_graph._chunks);
-    stream.position = _graph._positions[_id];
-    stream.skipUnsigned(); // addr
-    stream.readUnsigned(); // shallowSize
-    return stream.clampedUint32;
-  }
-
-  int get vmCid {
-    var stream = new ReadStream(_graph._chunks);
-    stream.position = _graph._positions[_id];
-    stream.skipUnsigned(); // addr
-    stream.skipUnsigned(); // shallowSize
-    stream.readUnsigned(); // cid
-    return stream.clampedUint32;
-  }
+  int get shallowSize => _graph._shallowSizes[_id];
+  int get vmCid => _graph._cids[_id];
 
   get successors => new _SuccessorsIterable(_graph, _id);
 
@@ -247,11 +241,11 @@
     // Note that everywhere else in this file, "address" really means an address
     // scaled down by kObjectAlignment. They were scaled down so they would fit
     // into Smis on the client.
-    var stream = new ReadStream(_graph._chunks);
-    stream.position = _graph._positions[_id];
-    stream.readUnsigned();
 
-    // Complicated way to do (high:mid:low * _kObjectAlignment).toHexString()
+    var high32 = _graph._addressesHigh[_id];
+    var low32 = _graph._addressesLow[_id];
+
+    // Complicated way to do (high:low * _kObjectAlignment).toHexString()
     // without intermediate values exceeding int32.
 
     var strAddr = "";
@@ -262,14 +256,13 @@
       nibble = nibble & 0xF;
       strAddr = nibble.toRadixString(16) + strAddr;
     }
-    combine28(twentyEightBits) {
-      for (int shift = 0; shift < 28; shift += 4) {
-        combine4((twentyEightBits >> shift) & 0xF);
+    combine32(thirtyTwoBits) {
+      for (int shift = 0; shift < 32; shift += 4) {
+        combine4((thirtyTwoBits >> shift) & 0xF);
       }
     }
-    combine28(stream.low);
-    combine28(stream.mid);
-    combine28(stream.high);
+    combine32(low32);
+    combine32(high32);
     return strAddr;
   }
 
@@ -301,27 +294,23 @@
 
 class _SuccessorsIterator implements Iterator<ObjectVertex> {
   final ObjectGraph _graph;
-  ReadStream _stream;
+  int _nextSuccIndex;
+  int _limitSuccIndex;
 
   ObjectVertex current;
 
   _SuccessorsIterator(this._graph, int id) {
-    _stream = new ReadStream(this._graph._chunks);
-    _stream.position = _graph._positions[id];
-    _stream.skipUnsigned(); // addr
-    _stream.skipUnsigned(); // shallowSize
-    _stream.skipUnsigned(); // cid
+    _nextSuccIndex = _graph._firstSuccs[id];
+    _limitSuccIndex = _graph._firstSuccs[id + 1];
   }
 
   bool moveNext() {
-    while (true) {
-      _stream.readUnsigned();
-      if (_stream.isZero) return false;
-      var nextId = _graph._addrToId.get(_stream.high, _stream.mid, _stream.low);
-      if (nextId == null) continue; // Reference to VM isolate's heap.
-      current = new ObjectVertex._(nextId, _graph);
+    if (_nextSuccIndex < _limitSuccIndex) {
+      var succId = _graph._succs[_nextSuccIndex++];
+      current = new ObjectVertex._(succId, _graph);
       return true;
     }
+    return false;
   }
 }
 
@@ -379,8 +368,14 @@
     // We build futures here instead of marking the steps as async to avoid the
     // heavy lifting being inside a transformed method.
 
-    statusReporter.add("Finding node positions...");
-    await new Future(() => _buildPositions());
+    statusReporter.add("Remapping $_N objects...");
+    await new Future(() => _remapNodes());
+
+    statusReporter.add("Remapping $_E references...");
+    await new Future(() => _remapEdges());
+
+    _addrToId = null;
+    _chunks = null;
 
     statusReporter.add("Finding post order...");
     await new Future(() => _buildPostOrder());
@@ -404,29 +399,42 @@
     return this;
   }
 
-  final List<ByteData> _chunks;
+  List<ByteData> _chunks;
 
   int _kObjectAlignment;
   int _N;
   int _E;
   int _size;
 
-  AddressMapper _addrToId;
-
   // Indexed by node id, with id 0 representing invalid/uninitialized.
-  Uint32List _positions; // Position of the node in the snapshot.
+  // From snapshot.
+  Uint16List _cids;
+  Uint32List _shallowSizes;
+  Uint32List _firstSuccs;
+  Uint32List _succs;
+  Uint32List _addressesLow; // No Uint64List in Javascript.
+  Uint32List _addressesHigh;
+
+  // Intermediates.
+  AddressMapper _addrToId;
   Uint32List _postOrderOrdinals; // post-order index -> id
   Uint32List _postOrderIndices; // id -> post-order index
   Uint32List _firstPreds; // Offset into preds.
   Uint32List _preds;
+
+  // Outputs.
   Uint32List _doms;
   Uint32List _retainedSizes;
 
-  void _buildPositions() {
+  void _remapNodes() {
     var N = _N;
+    var E = 0;
     var addrToId = new AddressMapper(N);
 
-    var positions = new Uint32List(N + 1);
+    var addressesHigh = new Uint32List(N + 1);
+    var addressesLow = new Uint32List(N + 1);
+    var shallowSizes = new Uint32List(N + 1);
+    var cids = new Uint16List(N + 1);
 
     var stream = new ReadStream(_chunks);
     stream.readUnsigned();
@@ -434,14 +442,18 @@
 
     var id = 1;
     while (stream.pendingBytes > 0) {
-      positions[id] = stream.position;
       stream.readUnsigned(); // addr
       addrToId.put(stream.high, stream.mid, stream.low, id);
-      stream.skipUnsigned(); // shallowSize
-      stream.skipUnsigned(); // cid
+      addressesHigh[id] = stream.highUint32;
+      addressesLow[id] = stream.lowUint32;
+      stream.readUnsigned(); // shallowSize
+      shallowSizes[id] = stream.clampedUint32;
+      stream.readUnsigned(); // cid
+      cids[id] = stream.clampedUint32;
 
       stream.readUnsigned();
       while (!stream.isZero) {
+        E++;
         stream.readUnsigned();
       }
       id++;
@@ -451,15 +463,62 @@
     var root = addrToId.get(0, 0, 0);
     assert(root == 1);
 
+    _E = E;
     _addrToId = addrToId;
-    _positions = positions;
+    _addressesLow = addressesLow;
+    _addressesHigh = addressesHigh;
+    _shallowSizes = shallowSizes;
+    _cids = cids;
+  }
+
+  void _remapEdges() {
+    var N = _N;
+    var E = _E;
+    var addrToId = _addrToId;
+
+    var firstSuccs = new Uint32List(N + 2);
+    var succs = new Uint32List(E);
+
+    var stream = new ReadStream(_chunks);
+    stream.skipUnsigned(); // addr alignment
+
+    var id = 1, edge = 0;
+    while (stream.pendingBytes > 0) {
+      stream.skipUnsigned(); // addr
+      stream.skipUnsigned(); // shallowSize
+      stream.skipUnsigned(); // cid
+
+      firstSuccs[id] = edge;
+
+      stream.readUnsigned();
+      while (!stream.isZero) {
+        var childId = addrToId.get(stream.high, stream.mid, stream.low);
+        if (childId != null) {
+          succs[edge] = childId;
+          edge++;
+        } else { 
+          // Reference into VM isolate's heap.
+        }
+        stream.readUnsigned();
+      }
+      id++;
+    }
+    firstSuccs[id] = edge; // Extra entry for cheap boundary detection.
+
+    assert(id == N + 1);
+    assert(edge <= E); // edge is smaller because E was computed before we knew
+                       // if references pointed into the VM isolate
+
+    _E = edge;
+    _firstSuccs = firstSuccs;
+    _succs = succs;
   }
 
   void _buildPostOrder() {
     var N = _N;
-    var E = 0;
-    var addrToId = _addrToId;
-    var positions = _positions;
+    var E = _E;
+    var firstSuccs = _firstSuccs;
+    var succs = _succs;
 
     var postOrderOrdinals = new Uint32List(N);
     var postOrderIndices = new Uint32List(N + 1);
@@ -472,36 +531,24 @@
     var root = 1;
 
     stackNodes[0] = root;
-
-    var stream = new ReadStream(_chunks);
-    stream.position = positions[root];
-    stream.skipUnsigned(); // addr
-    stream.skipUnsigned(); // shallowSize
-    stream.skipUnsigned(); // cid
-    stackCurrentEdgePos[0] = stream.position;
+    stackCurrentEdgePos[0] = firstSuccs[root];
     visited[root] = 1;
 
     while (stackTop >= 0) {
       var n = stackNodes[stackTop];
       var edgePos = stackCurrentEdgePos[stackTop];
 
-      stream.position = edgePos;
-      stream.readUnsigned();  // childAddr
-      if (!stream.isZero) {
-        stackCurrentEdgePos[stackTop] = stream.position;
-        var childId = addrToId.get(stream.high, stream.mid, stream.low);
-        if (childId == null) continue; // Reference to VM isolate's heap.
-        E++;
+      if (edgePos < firstSuccs[n + 1]) {
+        var childId = succs[edgePos];
+        edgePos++;
+        stackCurrentEdgePos[stackTop] = edgePos;
         if (visited[childId] == 1) continue;
 
+        // Push child.
         stackTop++;
         stackNodes[stackTop] = childId;
-
-        stream.position = positions[childId];
-        stream.skipUnsigned(); // addr
-        stream.skipUnsigned(); // shallowSize
-        stream.skipUnsigned(); // cid
-        stackCurrentEdgePos[stackTop] = stream.position; // i.e., first edge
+        edgePos = firstSuccs[childId];
+        stackCurrentEdgePos[stackTop] = edgePos;
         visited[childId] = 1;
       } else {
         // Done with all children.
@@ -522,8 +569,8 @@
   void _buildPredecessors() {
     var N = _N;
     var E = _E;
-    var addrToId = _addrToId;
-    var positions = _positions;
+    var firstSuccs = _firstSuccs;
+    var succs = _succs;
 
     // This is first filled with the predecessor counts, then reused to hold the
     // offset to the first predecessor (see alias below).
@@ -534,22 +581,9 @@
     var preds = new Uint32List(E);
 
     // Count predecessors of each node.
-    var stream = new ReadStream(_chunks);
-    for (var i = 1; i <= N; i++) {
-      stream.position = positions[i];
-      stream.skipUnsigned(); // addr
-      stream.skipUnsigned(); // shallowSize
-      stream.skipUnsigned(); // cid
-      stream.readUnsigned(); // succAddr
-      while (!stream.isZero) {
-        var succId = addrToId.get(stream.high, stream.mid, stream.low);
-        if (succId != null) {
-          numPreds[succId]++;
-        } else {
-          // Reference to VM isolate's heap.
-        }
-        stream.readUnsigned(); // succAddr
-      }
+    for (var succIndex = 0; succIndex < E; succIndex++) {
+      var succId = succs[succIndex];
+      numPreds[succId]++;
     }
 
     // Assign indices into predecessors array.
@@ -567,20 +601,14 @@
 
     // Fill predecessors array.
     for (var i = 1; i <= N; i++) {
-      stream.position = positions[i];
-      stream.skipUnsigned(); // addr
-      stream.skipUnsigned(); // shallowSize
-      stream.skipUnsigned(); // cid
-      stream.readUnsigned(); // succAddr
-      while (!stream.isZero) {
-        var succId = addrToId.get(stream.high, stream.mid, stream.low);
-        if (succId != null) {
-          var predIndex = nextPreds[succId]++;
-          preds[predIndex] = i;
-        } else {
-          // Reference to VM isolate's heap.
-        }
-        stream.readUnsigned(); // succAddr
+      var startSuccIndex = firstSuccs[i];
+      var limitSuccIndex = firstSuccs[i + 1];
+      for (var succIndex = startSuccIndex;
+           succIndex < limitSuccIndex;
+           succIndex++) {
+        var succId = succs[succIndex];
+        var predIndex = nextPreds[succId]++;
+        preds[predIndex] = i;
       }
     }
 
@@ -670,21 +698,17 @@
     var N = _N;
 
     var size = 0;
-    var positions = _positions;
+    var shallowSizes = _shallowSizes;
     var postOrderOrdinals = _postOrderOrdinals;
     var doms = _doms;
-    var retainedSizes = new Uint32List(N + 1);
+
+    // Sum shallow sizes.
+    for (var i = 1; i < N; i++) {
+      size += shallowSizes[i];
+    }
 
     // Start with retained size as shallow size.
-    var reader = new ReadStream(_chunks);
-    for (var i = 1; i <= N; i++) {
-      reader.position = positions[i];
-      reader.skipUnsigned(); // addr
-      reader.readUnsigned(); // shallowSize
-      var shallowSize = reader.clampedUint32;
-      retainedSizes[i] = shallowSize;
-      size += shallowSize;
-    }
+    var retainedSizes = new Uint32List.fromList(shallowSizes);
 
     // In post order (bottom up), add retained size to dominator's retained
     // size, skipping root.
diff --git a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
index a67b1db..237c154 100644
--- a/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
+++ b/runtime/observatory/lib/src/cpu_profile/cpu_profile.dart
@@ -135,15 +135,6 @@
   }
 
   setCodeAttributes() {
-    if (hasOptimizedCode()) {
-      attributes.add('optimized');
-    }
-    if (hasUnoptimizedCode()) {
-      attributes.add('unoptimized');
-    }
-    if (isInlined()) {
-      attributes.add('inlined');
-    }
   }
 }
 
diff --git a/runtime/observatory/lib/src/elements/class_view.dart b/runtime/observatory/lib/src/elements/class_view.dart
index 7ca6fb8..cfd535b 100644
--- a/runtime/observatory/lib/src/elements/class_view.dart
+++ b/runtime/observatory/lib/src/elements/class_view.dart
@@ -17,6 +17,10 @@
   @observable ServiceMap instances;
   @observable int retainedBytes;
   @observable ObservableList mostRetained;
+  SampleBufferControlElement sampleBufferControlElement;
+  StackTraceTreeConfigElement stackTraceTreeConfigElement;
+  CpuProfileTreeElement cpuProfileTreeElement;
+
   ClassViewElement.created() : super.created();
 
   Future<ServiceObject> evaluate(String expression) {
@@ -47,7 +51,22 @@
   }
 
   void attached() {
+    super.attached();
+    sampleBufferControlElement =
+        shadowRoot.querySelector('#sampleBufferControl');
+    assert(sampleBufferControlElement != null);
+    sampleBufferControlElement.onSampleBufferUpdate = onSampleBufferChange;
+    sampleBufferControlElement.state =
+        SampleBufferControlElement.kNotLoadedState;
+    stackTraceTreeConfigElement =
+        shadowRoot.querySelector('#stackTraceTreeConfig');
+    assert(stackTraceTreeConfigElement != null);
+    stackTraceTreeConfigElement.onTreeConfigChange = onTreeConfigChange;
+    cpuProfileTreeElement = shadowRoot.querySelector('#cpuProfileTree');
+    assert(cpuProfileTreeElement != null);
+    cpuProfileTreeElement.profile = sampleBufferControlElement.profile;
     cls.fields.forEach((field) => field.reload());
+    sampleBufferControlElement.allocationProfileClass = cls;
   }
 
   Future refresh() {
@@ -64,17 +83,26 @@
     return cls.refreshCoverage();
   }
 
-  final CpuProfile profile = new CpuProfile();
+  onSampleBufferChange(CpuProfile sampleBuffer) {
+    cpuProfileTreeElement.render();
+  }
+
+  onTreeConfigChange(String modeSelector, String directionSelector) {
+    ProfileTreeDirection direction = ProfileTreeDirection.Exclusive;
+    if (directionSelector != 'Up') {
+      direction = ProfileTreeDirection.Inclusive;
+    }
+    ProfileTreeMode mode = ProfileTreeMode.Function;
+    if (modeSelector == 'Code') {
+      mode = ProfileTreeMode.Code;
+    }
+    cpuProfileTreeElement.direction = direction;
+    cpuProfileTreeElement.mode = mode;
+    cpuProfileTreeElement.render();
+  }
 
   Future refreshAllocationProfile() async {
-    var profileResponse = await cls.getAllocationSamples('UserVM');
-    profile.load(profileResponse.isolate, profileResponse);
-    CpuProfileTreeElement cpuProfileTreeElement =
-        shadowRoot.querySelector('#cpuProfileTree');
-    cpuProfileTreeElement.profile = profile;
-    cpuProfileTreeElement.direction = ProfileTreeDirection.Exclusive;
-    cpuProfileTreeElement.mode = ProfileTreeMode.Function;
-    cpuProfileTreeElement.render();
+    return sampleBufferControlElement.reload(cls.isolate);
   }
 
   Future toggleAllocationTrace() {
diff --git a/runtime/observatory/lib/src/elements/class_view.html b/runtime/observatory/lib/src/elements/class_view.html
index beddb21..200c22a 100644
--- a/runtime/observatory/lib/src/elements/class_view.html
+++ b/runtime/observatory/lib/src/elements/class_view.html
@@ -229,6 +229,10 @@
           </div>
         </template>
       </div>
+      <sample-buffer-control id="sampleBufferControl"></sample-buffer-control>
+      <br>
+      <stack-trace-tree-config id="stackTraceTreeConfig"></stack-trace-tree-config>
+      <br>
       <div class="flex-row centered">
         <div class="flex-item-90-percent outlined" style="margin: 16px; margin-left: 8px; margin-right: 8px">
           <cpu-profile-tree id="cpuProfileTree"></cpu-profile-tree>
diff --git a/runtime/observatory/lib/src/elements/cpu_profile.dart b/runtime/observatory/lib/src/elements/cpu_profile.dart
index bb5a626..e1d6d47 100644
--- a/runtime/observatory/lib/src/elements/cpu_profile.dart
+++ b/runtime/observatory/lib/src/elements/cpu_profile.dart
@@ -448,8 +448,14 @@
     }
     await _changeState(kFetchingState);
     try {
-      var params = { 'tags': tagSelector };
-      var response = await isolate.invokeRpc('_getCpuProfile', params);
+      var response;
+      if (allocationProfileClass != null) {
+        response =
+            await allocationProfileClass.getAllocationSamples(tagSelector);
+      } else {
+        var params = { 'tags': tagSelector };
+        response = await isolate.invokeRpc('_getCpuProfile', params);
+      }
       await _changeState(kLoadingState);
       profile.load(isolate, response);
       _update(profile);
@@ -512,7 +518,7 @@
   @observable String fetchTime = '';
   @observable String loadTime = '';
   @observable String tagSelector = 'UserVM';
-  @observable String state = 'kFetching';
+  @observable String state = kFetchingState;
   @observable var exception;
   @observable var stackTrace;
 
@@ -521,8 +527,10 @@
   static const kFetchingState = 'kFetching';
   static const kLoadedState = 'kLoaded';
   static const kLoadingState = 'kLoading';
+  static const kNotLoadedState = 'kNotLoaded';
 
   Isolate isolate;
+  Class allocationProfileClass;
 
   final CpuProfile profile = new CpuProfile();
   final Stopwatch _stopWatch = new Stopwatch();
diff --git a/runtime/observatory/lib/src/elements/debugger.dart b/runtime/observatory/lib/src/elements/debugger.dart
index ae0ab6f..2034b95 100644
--- a/runtime/observatory/lib/src/elements/debugger.dart
+++ b/runtime/observatory/lib/src/elements/debugger.dart
@@ -7,6 +7,7 @@
 import 'dart:async';
 import 'dart:html';
 import 'observatory_element.dart';
+import 'package:observatory/app.dart';
 import 'package:observatory/cli.dart';
 import 'package:observatory/debugger.dart';
 import 'package:observatory/service.dart';
@@ -27,7 +28,9 @@
 // TODO(turnidge): Rewrite HelpCommand so that it is a general utility
 // provided by the cli library.
 class HelpCommand extends DebuggerCommand {
-  HelpCommand(Debugger debugger) : super(debugger, 'help', []);
+  HelpCommand(Debugger debugger) : super(debugger, 'help', [
+    new HelpHotkeysCommand(debugger),
+  ]);
 
   String _nameAndAlias(Command cmd) {
     if (cmd.alias == null) {
@@ -50,6 +53,7 @@
       }
       con.print(
           "\nFor more information on a specific command type 'help <command>'\n"
+          "For a list of hotkeys type 'help hotkeys'\n"
           "\n"
           "Command prefixes are accepted (e.g. 'h' for 'help')\n"
           "Hit [TAB] to complete a command (try 'is[TAB][TAB]')\n"
@@ -105,6 +109,38 @@
       '        help <command>  - Help for a specific command\n';
 }
 
+class HelpHotkeysCommand extends DebuggerCommand {
+  HelpHotkeysCommand(Debugger debugger) : super(debugger, 'hotkeys', []);
+
+  Future run(List<String> args) {
+    var con = debugger.console;
+    con.print("List of hotkeys:\n"
+              "\n"
+              "[TAB]        - complete a command\n"
+              "[Up Arrow]   - history previous\n"
+              "[Down Arrow] - history next\n"
+              "\n"
+              "[Page Up]    - move up one frame\n"
+              "[Page Down]  - move down one frame\n"
+              "\n"
+              "[F7]         - continue execution of the current isolate\n"
+              "[Ctrl ;]     - pause execution of the current isolate\n"
+              "\n"
+              "[F8]         - toggle breakpoint at current location\n"
+              "[F9]         - next\n"
+              "[F10]        - step\n"
+              "\n");
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Provide a list of hotkeys';
+
+  String helpLong =
+      'Provide a list of key hotkeys.\n'
+      '\n'
+      'Syntax: help hotkeys\n';
+}
+
 class PrintCommand extends DebuggerCommand {
   PrintCommand(Debugger debugger) : super(debugger, 'print', []) {
     alias = 'p';
@@ -155,7 +191,7 @@
       return new Future.value(null);
     }
     try {
-      debugger.currentFrame -= count;
+      debugger.downFrame(count);
       debugger.console.print('frame = ${debugger.currentFrame}');
     } catch (e) {
       debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
@@ -163,11 +199,13 @@
     return new Future.value(null);
   }
 
-  String helpShort = 'Move down one or more frames';
+  String helpShort = 'Move down one or more frames (hotkey: [Page Down])';
 
   String helpLong =
       'Move down one or more frames.\n'
       '\n'
+      'Hotkey: [Page Down]\n'
+      '\n'
       'Syntax: down\n'
       '        down <count>\n';
 }
@@ -188,7 +226,7 @@
       return new Future.value(null);
     }
     try {
-      debugger.currentFrame += count;
+      debugger.upFrame(count);
       debugger.console.print('frame = ${debugger.currentFrame}');
     } on RangeError catch (e) {
       debugger.console.print('frame must be in range [${e.start},${e.end-1}]');
@@ -196,11 +234,13 @@
     return new Future.value(null);
   }
 
-  String helpShort = 'Move up one or more frames';
+  String helpShort = 'Move up one or more frames (hotkey: [Page Up])';
 
   String helpLong =
       'Move up one or more frames.\n'
       '\n'
+      'Hotkey: [Page Up]\n'
+      '\n'
       'Syntax: up\n'
       '        up <count>\n';
 }
@@ -244,19 +284,16 @@
   PauseCommand(Debugger debugger) : super(debugger, 'pause', []);
 
   Future run(List<String> args) {
-    if (!debugger.isolatePaused()) {
-      return debugger.isolate.pause();
-    } else {
-      debugger.console.print('The program is already paused');
-      return new Future.value(null);
-    }
+    return debugger.pause();
   }
 
-  String helpShort = 'Pause the isolate';
+  String helpShort = 'Pause the isolate (hotkey: [Ctrl ;])';
 
   String helpLong =
       'Pause the isolate.\n'
       '\n'
+      'Hotkey: [Ctrl ;]\n'
+      '\n'
       'Syntax: pause\n';
 }
 
@@ -266,21 +303,16 @@
   }
 
   Future run(List<String> args) {
-    if (debugger.isolatePaused()) {
-      return debugger.isolate.resume().then((_) {
-          debugger.warnOutOfDate();
-        });
-    } else {
-      debugger.console.print('The program must be paused');
-      return new Future.value(null);
-    }
+    return debugger.resume();
   }
 
-  String helpShort = 'Resume execution of the isolate';
+  String helpShort = 'Resume execution of the isolate (hotkey: [F7])';
 
   String helpLong =
       'Continue running the isolate.\n'
       '\n'
+      'Hotkey: [F7]\n'
+      '\n'
       'Syntax: continue\n'
       '        c\n';
 }
@@ -289,31 +321,19 @@
   NextCommand(Debugger debugger) : super(debugger, 'next', []);
 
   Future run(List<String> args) {
-    if (debugger.isolatePaused()) {
-      var event = debugger.isolate.pauseEvent;
-      if (event.kind == ServiceEvent.kPauseStart) {
-        debugger.console.print("Type 'continue' to start the isolate");
-        return new Future.value(null);
-      }
-      if (event.kind == ServiceEvent.kPauseExit) {
-        debugger.console.print("Type 'continue' to exit the isolate");
-        return new Future.value(null);
-      }
-      return debugger.isolate.stepOver();
-    } else {
-      debugger.console.print('The program is already running');
-      return new Future.value(null);
-    }
+    return debugger.next();
   }
 
   String helpShort =
       'Continue running the isolate until it reaches the next source location '
-      'in the current function';
+      'in the current function (hotkey: [F9])';
 
   String helpLong =
       'Continue running the isolate until it reaches the next source location '
       'in the current function.\n'
       '\n'
+      'Hotkey: [F9]\n'
+      '\n'
       'Syntax: next\n';
 }
 
@@ -323,33 +343,39 @@
   }
 
   Future run(List<String> args) {
-    if (debugger.isolatePaused()) {
-      var event = debugger.isolate.pauseEvent;
-      if (event.kind == ServiceEvent.kPauseStart) {
-        debugger.console.print("Type 'continue' to start the isolate");
-        return new Future.value(null);
-      }
-      if (event.kind == ServiceEvent.kPauseExit) {
-        debugger.console.print("Type 'continue' to exit the isolate");
-        return new Future.value(null);
-      }
-      return debugger.isolate.stepInto();
-    } else {
-      debugger.console.print('The program is already running');
-      return new Future.value(null);
-    }
+    return debugger.step();
   }
 
   String helpShort =
-      'Continue running the isolate until it reaches the next source location';
+      'Continue running the isolate until it reaches the next source location'
+      ' (hotkey: [F10]';
 
   String helpLong =
       'Continue running the isolate until it reaches the next source '
       'location.\n'
       '\n'
+      'Hotkey: [F10]\n'
+      '\n'
       'Syntax: step\n';
 }
 
+class ClsCommand extends DebuggerCommand {
+  ClsCommand(Debugger debugger) : super(debugger, 'cls', []) {}
+
+  Future run(List<String> args) {
+    debugger.console.clear();
+    debugger.console.newline();
+    return new Future.value(null);
+  }
+
+  String helpShort = 'Clear the console';
+
+  String helpLong =
+      'Clear the console.\n'
+      '\n'
+      'Syntax: cls\n';
+}
+
 class LogCommand extends DebuggerCommand {
   LogCommand(Debugger debugger) : super(debugger, 'log', []);
 
@@ -451,6 +477,16 @@
 
   Future run(List<String> args) {
     if (debugger.isolatePaused()) {
+      var event = debugger.isolate.pauseEvent;
+      if (event.kind == ServiceEvent.kPauseStart) {
+        debugger.console.print(
+            "Type 'continue' [F7] or 'step' [F10] to start the isolate");
+        return new Future.value(null);
+      }
+      if (event.kind == ServiceEvent.kPauseExit) {
+        debugger.console.print("Type 'continue' [F7] to exit the isolate");
+        return new Future.value(null);
+      }
       return debugger.isolate.stepOut();
     } else {
       debugger.console.print('The program is already running');
@@ -471,29 +507,119 @@
   SetCommand(Debugger debugger)
       : super(debugger, 'set', []);
 
-  Future run(List<String> args) async {
-    if (args.length == 2) {
-      var option = args[0].trim();
-      if (option == 'break-on-exceptions') {
-        var result = await debugger.isolate.setExceptionPauseInfo(args[1]);
-        if (result.isError) {
-          debugger.console.print(result.toString());
-        }
-      } else {
-        debugger.console.print("unknown option '$option'");
-      }
+  static var _boeValues = ['all', 'none', 'unhandled'];
+  static var _boolValues = ['false', 'true'];
+
+  static var _options = {
+    'break-on-exception': [_boeValues,
+                           _setBreakOnException,
+                           (debugger, _) => debugger.breakOnException],
+    'up-is-down': [_boolValues,
+                   _setUpIsDown,
+                   (debugger, _) => debugger.upIsDown],
+  };
+
+  static Future _setBreakOnException(debugger, name, value) async {
+    var result = await debugger.isolate.setExceptionPauseInfo(value);
+    if (result.isError) {
+      debugger.console.print(result.toString());
     } else {
-      debugger.console.print("set expects 2 arguments");
+      // Printing will occur elsewhere.
+      debugger.breakOnException = value;
     }
   }
 
+  static Future _setUpIsDown(debugger, name, value) async {
+    if (value == 'true') {
+      debugger.upIsDown = true;
+    } else {
+      debugger.upIsDown = false;
+    }
+    debugger.console.print('${name} = ${value}');
+  }
+
+  Future run(List<String> args) async {
+    if (args.length == 0) {
+      for (var name in _options.keys) {
+        var getHandler = _options[name][2];
+        var value = await getHandler(debugger, name);
+        debugger.console.print("${name} = ${value}");
+      }
+    } else if (args.length == 1) {
+      var name = args[0].trim();
+      var optionInfo = _options[name];
+      if (optionInfo == null) {
+        debugger.console.print("unrecognized option: $name");
+        return;
+      } else {
+        var getHandler = optionInfo[2];
+        var value = await getHandler(debugger, name);
+        debugger.console.print("${name} = ${value}");
+      }
+    } else if (args.length == 2) {
+      var name = args[0].trim();
+      var value = args[1].trim();
+      var optionInfo = _options[name];
+      if (optionInfo == null) {
+        debugger.console.print("unrecognized option: $name");
+        return;
+      }
+      var validValues = optionInfo[0];
+      if (!validValues.contains(value)) {
+        debugger.console.print("'${value}' is not in ${validValues}");
+        return;
+      }
+      var setHandler = optionInfo[1];
+      await setHandler(debugger, name, value);
+    } else {
+      debugger.console.print("set expects 0, 1, or 2 arguments");
+    }
+  }
+
+  Future<List<String>> complete(List<String> args) {
+    if (args.length < 1 || args.length > 2) {
+      return new Future.value([args.join('')]);
+    }
+    var result = [];
+    if (args.length == 1) {
+      var prefix = args[0];
+      for (var option in _options.keys) {
+        if (option.startsWith(prefix)) {
+          result.add('${option} ');
+        }
+      }
+    }
+    if (args.length == 2) {
+      var name = args[0].trim();
+      var prefix = args[1];
+      var optionInfo = _options[name];
+      if (optionInfo != null) {
+        var validValues = optionInfo[0];
+        for (var value in validValues) {
+          if (value.startsWith(prefix)) {
+            result.add('${args[0]}${value} ');
+          }
+        }
+      }
+    }
+    return new Future.value(result);
+  }
+
   String helpShort =
       'Set a debugger option';
 
   String helpLong =
-      'Set a debugger option'
+      'Set a debugger option.\n'
       '\n'
-      'Syntax: set break-on-exceptions "all" | "none" | "unhandled"\n';
+      'Known options:\n'
+      '  break-on-exceptions   # Should the debugger break on exceptions?\n'
+      "                        # ${_boeValues}\n"
+      '  up-is-down            # Reverse meaning of up/down commands?\n'
+      "                        # ${_boolValues}\n"
+      '\n'
+      'Syntax: set                    # Display all option settings\n'
+      '        set <option>           # Get current value for option\n'
+      '        set <option> <value>   # Set value for option';
 }
 
 class BreakCommand extends DebuggerCommand {
@@ -548,11 +674,14 @@
     return new Future.value(DebuggerLocation.complete(debugger, args[0]));
   }
 
-  String helpShort = 'Add a breakpoint by source location or function name';
+  String helpShort = 'Add a breakpoint by source location or function name'
+      ' (hotkey: [F8])';
 
   String helpLong =
       'Add a breakpoint by source location or function name.\n'
       '\n'
+      'Hotkey: [F8]\n'
+      '\n'
       'Syntax: break                       '
       '# Break at the current position\n'
       '        break <line>                '
@@ -631,11 +760,14 @@
     return new Future.value(DebuggerLocation.complete(debugger, args[0]));
   }
 
-  String helpShort = 'Remove a breakpoint by source location or function name';
+  String helpShort = 'Remove a breakpoint by source location or function name'
+      ' (hotkey: [F8])';
 
   String helpLong =
       'Remove a breakpoint by source location or function name.\n'
       '\n'
+      'Hotkey: [F8]\n'
+      '\n'
       'Syntax: clear                       '
       '# Clear at the current position\n'
       '        clear <line>                '
@@ -805,17 +937,15 @@
   Future<List<String>> complete(List<String> args) {
     if (args.length != 1) {
       return new Future.value([args.join('')]);
-    }
-    var isolates = debugger.vm.isolates.toList();
-    isolates.sort((a, b) => a.startTime.compareTo(b.startTime));
+    } 
     var result = [];
-    for (var isolate in isolates) {
+    for (var isolate in debugger.vm.isolates) {
       var str = isolate.number.toString();
       if (str.startsWith(args[0])) {
         result.add('$str ');
       }
     }
-    for (var isolate in isolates) {
+    for (var isolate in debugger.vm.isolates) {
       if (isolate.name.startsWith(args[0])) {
         result.add('${isolate.name} ');
       }
@@ -840,9 +970,7 @@
           "Internal error: vm has not been set");
       return new Future.value(null);
     }
-    var isolates = debugger.vm.isolates.toList();
-    isolates.sort((a, b) => a.startTime.compareTo(b.startTime));
-    for (var isolate in isolates) {
+    for (var isolate in debugger.vm.isolates) {
       String current = (isolate == debugger.isolate ? ' *' : '');
       debugger.console.print(
           "Isolate ${isolate.number} '${isolate.name}'${current}");
@@ -1027,15 +1155,17 @@
 
 // Tracks the state for an isolate debugging session.
 class ObservatoryDebugger extends Debugger {
+  final SettingsGroup settings = new SettingsGroup('debugger');
   RootCommand cmd;
   DebuggerPageElement page;
   DebuggerConsoleElement console;
   DebuggerInputElement input;
   DebuggerStackElement stackElement;
   ServiceMap stack;
-  String exceptions = "none";  // Last known setting.
+  String breakOnException = "none";  // Last known setting.
 
   int get currentFrame => _currentFrame;
+
   void set currentFrame(int value) {
     if (value != null && (value < 0 || value >= stackDepth)) {
       throw new RangeError.range(value, 0, stackDepth);
@@ -1047,9 +1177,33 @@
   }
   int _currentFrame = null;
 
+  bool get upIsDown => _upIsDown;
+  void set upIsDown(bool value) {
+    settings.set('up-is-down', value);
+    _upIsDown = value;
+  }
+  bool _upIsDown;
+
+  void upFrame(int count) {
+    if (_upIsDown) {
+      currentFrame += count;
+    } else {
+      currentFrame -= count;
+    }
+  }
+
+  void downFrame(int count) {
+    if (_upIsDown) {
+      currentFrame -= count;
+    } else {
+      currentFrame += count;
+    }
+  }
+
   int get stackDepth => stack['frames'].length;
 
   ObservatoryDebugger() {
+    _loadSettings();
     cmd = new RootCommand([
         new HelpCommand(this),
         new PrintCommand(this),
@@ -1070,19 +1224,24 @@
         new IsolateCommand(this),
         new RefreshCommand(this),
         new LogCommand(this),
+        new ClsCommand(this),
     ]);
     _consolePrinter = new _ConsoleStreamPrinter(this);
   }
 
+  void _loadSettings() {
+    _upIsDown = settings.get('up-is-down');
+  }
+
   VM get vm => page.app.vm;
 
   void updateIsolate(Isolate iso) {
     _isolate = iso;
     if (_isolate != null) {
-      if ((exceptions != iso.exceptionsPauseInfo) &&
+      if ((breakOnException != iso.exceptionsPauseInfo) &&
           (iso.exceptionsPauseInfo != null)) {
-        exceptions = iso.exceptionsPauseInfo;
-        console.print("Now pausing for $exceptions exceptions");
+        breakOnException = iso.exceptionsPauseInfo;
+        console.print("Now pausing for exceptions: $breakOnException");
       }
 
       _isolate.reload().then((response) {
@@ -1190,10 +1349,10 @@
   void _reportPause(ServiceEvent event) {
     if (event.kind == ServiceEvent.kPauseStart) {
       console.print(
-          "Paused at isolate start (type 'continue' to start the isolate')");
+          "Paused at isolate start (type 'continue' [F7] or 'step' [F10] to start the isolate')");
     } else if (event.kind == ServiceEvent.kPauseExit) {
       console.print(
-          "Paused at isolate exit (type 'continue' to exit the isolate')");
+          "Paused at isolate exit (type 'continue' or [F7] to exit the isolate')");
     }
     if (stack['frames'].length > 0) {
       Frame frame = stack['frames'][0];
@@ -1277,9 +1436,9 @@
         break;
 
       case ServiceEvent.kDebuggerSettingsUpdate:
-        if (exceptions != event.exceptions) {
-          exceptions = event.exceptions;
-          console.print("Now pausing for $exceptions exceptions");
+        if (breakOnException != event.exceptions) {
+          breakOnException = event.exceptions;
+          console.print("Now pausing for exceptions: $breakOnException");
         }
         break;
 
@@ -1416,6 +1575,80 @@
   String historyNext(String command) {
     return cmd.historyNext(command);
   }
+
+  Future pause() {
+    if (!isolatePaused()) {
+      return isolate.pause();
+    } else {
+      console.print('The program is already paused');
+      return new Future.value(null);
+    }
+  }
+
+  Future resume() {
+    if (isolatePaused()) {
+      return isolate.resume().then((_) {
+          warnOutOfDate();
+        });
+    } else {
+      console.print('The program must be paused');
+      return new Future.value(null);
+    }
+  }
+
+  Future toggleBreakpoint() async {
+    var loc = await DebuggerLocation.parse(this, '');
+    var script = loc.script;
+    var line = loc.line;
+    if (script != null && line != null) {
+      var bpts = script.getLine(line).breakpoints;
+      if (bpts == null || bpts.isEmpty) {
+        // Set a new breakpoint.
+        // TODO(turnidge): Set this breakpoint at current column.
+        await isolate.addBreakpoint(script, line);
+      } else {
+        // TODO(turnidge): Clear this breakpoint at current column.
+        var pending = [];
+        for (var bpt in bpts) {
+          pending.add(isolate.removeBreakpoint(bpt));
+        }
+        await Future.wait(pending);
+      }
+    }
+    return new Future.value(null);
+  }
+
+  Future next() {
+    if (isolatePaused()) {
+      var event = isolate.pauseEvent;
+      if (event.kind == ServiceEvent.kPauseStart) {
+        console.print("Type 'continue' [F7] or 'step' [F10] to start the isolate");
+        return new Future.value(null);
+      }
+      if (event.kind == ServiceEvent.kPauseExit) {
+        console.print("Type 'continue' [F7] to exit the isolate");
+        return new Future.value(null);
+      }
+      return isolate.stepOver();
+    } else {
+      console.print('The program is already running');
+      return new Future.value(null);
+    }
+  }
+
+  Future step() {
+    if (isolatePaused()) {
+      var event = isolate.pauseEvent;
+      if (event.kind == ServiceEvent.kPauseExit) {
+        console.print("Type 'continue' [F7] to exit the isolate");
+        return new Future.value(null);
+      }
+      return isolate.stepInto();
+    } else {
+      console.print('The program is already running');
+      return new Future.value(null);
+    }
+  }
 }
 
 @CustomTag('debugger-page')
@@ -1484,7 +1717,8 @@
       // Random clicks should focus on the text box.  If the user selects
       // a range, don't interfere.
       var selection = window.getSelection();
-      if (selection == null || selection.type == 'Caret') {
+      if (selection == null ||
+          (selection.type != 'Range' && selection.type != 'text')) {
         debugger.input.focus();
       }
     });
@@ -1947,6 +2181,11 @@
   void newline() {
     _append(new BRElement());
   }
+
+  void clear() {
+    var consoleTextElement = $['consoleText'];
+    consoleTextElement.children.clear();
+  }
 }
 
 @CustomTag('debugger-input')
@@ -2000,6 +2239,66 @@
             busy = false;
             break;
 
+          case KeyCode.PAGE_UP:
+            e.preventDefault();
+            try {
+              debugger.upFrame(1);
+            } on RangeError catch (e) {
+              // Ignore.
+            }
+            busy = false;
+            break;
+
+          case KeyCode.PAGE_DOWN:
+            e.preventDefault();
+            try {
+              debugger.downFrame(1);
+            } on RangeError catch (e) {
+              // Ignore.
+            }
+            busy = false;
+            break;
+
+          case KeyCode.F7:
+            e.preventDefault();
+            debugger.resume().whenComplete(() {
+              busy = false;
+            });
+            break;
+
+          case KeyCode.F8:
+            e.preventDefault();
+            debugger.toggleBreakpoint().whenComplete(() {
+              busy = false;
+            });
+            break;
+
+          case KeyCode.F9:
+            e.preventDefault();
+            debugger.next().whenComplete(() {
+              busy = false;
+            });
+            break;
+
+          case KeyCode.F10:
+            e.preventDefault();
+            debugger.step().whenComplete(() {
+              busy = false;
+            });
+            break;
+
+          case KeyCode.SEMICOLON:
+            if (e.ctrlKey) {
+              e.preventDefault();
+              debugger.console.printRed('^;');
+              debugger.pause().whenComplete(() {
+                busy = false;
+              });
+            } else {
+              busy = false;
+            }
+            break;
+
           default:
             busy = false;
             break;
diff --git a/runtime/observatory/lib/src/elements/eval_box.dart b/runtime/observatory/lib/src/elements/eval_box.dart
index 957e74a..7447aca 100644
--- a/runtime/observatory/lib/src/elements/eval_box.dart
+++ b/runtime/observatory/lib/src/elements/eval_box.dart
@@ -17,6 +17,7 @@
 class EvalBoxElement extends ObservatoryElement {
   @observable String text;
   @observable String lineMode = "1-line";
+  int _exprCount = 0;
 
   @published evalType callback;
   @observable ObservableList results = toObservable([]);
@@ -39,6 +40,7 @@
     // Use provided callback to eval the expression.
     if (callback != null) {
       var map = toObservable({});
+      map['id'] = (_exprCount++).toString();
       map['expr'] = expr;
       results.insert(0, map);
       callback(expr).then((result) {
@@ -56,5 +58,12 @@
     text = targetElement.getAttribute('expr');
   }
 
+  void closeItem(MouseEvent e) {
+    assert(e.target is Element);
+    Element targetElement = e.target;
+    var closeId = targetElement.getAttribute('closeId');
+    results.removeWhere((item) => item['id'] == closeId);
+  }
+
   EvalBoxElement.created() : super.created();
 }
diff --git a/runtime/observatory/lib/src/elements/eval_box.html b/runtime/observatory/lib/src/elements/eval_box.html
index a6d61d3..ad9045a 100644
--- a/runtime/observatory/lib/src/elements/eval_box.html
+++ b/runtime/observatory/lib/src/elements/eval_box.html
@@ -42,6 +42,22 @@
         display: block;
         padding: 6px 6px;
       }
+      a.boxclose {
+        margin-left: 20px;
+        valign: top;
+        display: block;
+        height: 18px;
+        width: 18px;
+        line-height: 16px;
+        border-radius: 9px;
+        color: black;
+        font-size: 18px;
+        cursor: pointer;
+        text-align: center;
+      }
+      a.boxclose:hover {
+        background: lightgray;
+      }
     </style>
     <form style="display:flex; flex-direction:row; align-items:flex-end">
       <template if="{{ lineMode == '1-line' }}">
@@ -86,6 +102,10 @@
               </template>
             </template>
           </td>
+          <td>
+            <a class="boxclose" on-click="{{ closeItem }}"
+               closeId="{{ result['id'] }}">&times;</a>
+          </td>
         </tr>
       </table>
     </template>
diff --git a/runtime/observatory/lib/src/elements/library_view.html b/runtime/observatory/lib/src/elements/library_view.html
index e28ebad..a898ebb 100644
--- a/runtime/observatory/lib/src/elements/library_view.html
+++ b/runtime/observatory/lib/src/elements/library_view.html
@@ -59,7 +59,18 @@
             <template repeat="{{ dep in library.dependencies }}">
               <div class="memberItem">
                 <div class="memberValue">
-                  <library-ref ref="{{ dep.target }}"></library-ref>
+                  <template if="{{ dep.isImport }}">
+                    import <library-ref ref="{{ dep.target }}"></library-ref>
+                    <template if="{{ dep.prefix != null }}">
+                      as {{ dep.prefix.toString() }}
+                    </template>
+                    <template if="{{ dep.isDeferred }}">
+                      deferred
+                    </template>
+                  </template>
+                  <template if="{{ !dep.isImport }}">
+                    export <library-ref ref="{{ dep.target }}"></library-ref>
+                  </template>
                 </div>
               </div>
             </template>
diff --git a/runtime/observatory/lib/src/service/object.dart b/runtime/observatory/lib/src/service/object.dart
index 9124595..1c6fc0f 100644
--- a/runtime/observatory/lib/src/service/object.dart
+++ b/runtime/observatory/lib/src/service/object.dart
@@ -506,7 +506,8 @@
   final ObservableMap<String,Isolate> _isolateCache =
       new ObservableMap<String,Isolate>();
 
-  @reflectable Iterable<Isolate> get isolates => _isolateCache.values;
+  // The list of live isolates, ordered by isolate start time.
+  final ObservableList<Isolate> isolates = new ObservableList<Isolate>();
 
   @observable String version = 'unknown';
   @observable String targetCPU;
@@ -547,7 +548,8 @@
       var isolate = getFromMap(map['isolate']);
       event = new ServiceObject._fromMap(isolate, map);
       if (event.kind == ServiceEvent.kIsolateExit) {
-        _removeIsolate(isolate.id);
+        _isolateCache.remove(isolate.id);
+        _buildIsolateList();
       }
     }
     var eventStream = _eventStreams[streamId];
@@ -558,10 +560,27 @@
     }
   }
 
-  void _removeIsolate(String isolateId) {
-    assert(_isolateCache.containsKey(isolateId));
-    _isolateCache.remove(isolateId);
-    notifyPropertyChange(#isolates, true, false);
+  int _compareIsolates(Isolate a, Isolate b) {
+    var aStart = a.startTime;
+    var bStart = b.startTime;
+    if (aStart == null) {
+      if (bStart == null) {
+        return 0;
+      } else {
+        return 1;
+      }
+    }
+    if (bStart == null) {
+      return -1;
+    }
+    return aStart.compareTo(bStart);
+  }
+
+  void _buildIsolateList() {
+    var isolateList = _isolateCache.values.toList();
+    isolateList.sort(_compareIsolates);
+    isolates.clear();
+    isolates.addAll(isolateList);
   }
 
   void _removeDeadIsolates(List newIsolates) {
@@ -576,8 +595,8 @@
         toRemove.add(id);
       }
     });
-    toRemove.forEach((id) => _removeIsolate(id));
-    notifyPropertyChange(#isolates, true, false);
+    toRemove.forEach((id) => _isolateCache.remove(id));
+    _buildIsolateList();
   }
 
   static final String _isolateIdPrefix = 'isolates/';
@@ -598,7 +617,7 @@
       // Add new isolate to the cache.
       isolate = new ServiceObject._fromMap(this, map);
       _isolateCache[id] = isolate;
-      notifyPropertyChange(#isolates, true, false);
+      _buildIsolateList();
 
       // Eagerly load the isolate.
       isolate.load().catchError((e, stack) {
@@ -1201,6 +1220,7 @@
     if (map['entry'] != null) {
       entry = map['entry'];
     }
+    var savedStartTime = startTime;
     var startTimeInMillis = map['startTime'];
     startTime = new DateTime.fromMillisecondsSinceEpoch(startTimeInMillis);
     notifyPropertyChange(#upTime, 0, 1);
@@ -1250,6 +1270,9 @@
     libraries.clear();
     libraries.addAll(map['libraries']);
     libraries.sort(ServiceObject.LexicalSortName);
+    if (savedStartTime == null) {
+      vm._buildIsolateList();
+    }
   }
 
   Future<TagProfile> updateTagProfile() {
@@ -1568,7 +1591,7 @@
     return Future.wait([refreshDartMetrics(), refreshNativeMetrics()]);
   }
 
-  String toString() => "Isolate($_id)";
+  String toString() => "Isolate($name)";
 }
 
 /// A [ServiceObject] which implements [ObservableMap].
diff --git a/runtime/observatory/tests/service/isolate_lifecycle_test.dart b/runtime/observatory/tests/service/isolate_lifecycle_test.dart
index dbacef8..2160d0c 100644
--- a/runtime/observatory/tests/service/isolate_lifecycle_test.dart
+++ b/runtime/observatory/tests/service/isolate_lifecycle_test.dart
@@ -40,29 +40,21 @@
   return paused;
 }
 
-int numRunning(vm) {
-  int running = 0;
-  for (var isolate in vm.isolates) {
-    if (!isolate.paused) {
-      running++;
-    }
-  }
-  return running;
-}
-
 var tests = [
   (VM vm) async {
-    // Wait for the testee to start all of the isolates.
-    if (vm.isolates.length != spawnCount + 1) {
-      await processServiceEvents(vm, VM.kIsolateStream,
-                                 (event, sub, completer) {
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kIsolateStream);
+    if (vm.isolates.length < spawnCount + 1) {
+      var subscription;
+      subscription = stream.listen((ServiceEvent event) {
         if (event.kind == ServiceEvent.kIsolateStart) {
-          if (vm.isolates.length == spawnCount + 1) {
-            sub.cancel();
+          if (vm.isolates.length == (spawnCount + 1)) {
+            subscription.cancel();
             completer.complete(null);
           }
         }
       });
+      await completer.future;
     }
     expect(vm.isolates.length, spawnCount + 1);
   },
@@ -75,35 +67,40 @@
   },
 
   (VM vm) async {
-    // Wait for all spawned isolates to hit pause-at-exit.
-    if (numPaused(vm) != spawnCount) {
-      await processServiceEvents(vm, VM.kDebugStream,
-                                 (event, sub, completer) {
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kDebugStream);
+    if (numPaused(vm) < spawnCount) {
+      var subscription;
+      subscription = stream.listen((ServiceEvent event) {
         if (event.kind == ServiceEvent.kPauseExit) {
-          if (numPaused(vm) == spawnCount) {
-            sub.cancel();
+          if (numPaused(vm) == (spawnCount + 1)) {
+            subscription.cancel();
             completer.complete(null);
           }
         }
       });
+      await completer.future;
     }
-    expect(numPaused(vm), spawnCount);
-    expect(numRunning(vm), 1);
+    expect(numPaused(vm), spawnCount + 1);
   },
 
 
   (VM vm) async {
     var resumedReceived = 0;
-    var eventsDone = processServiceEvents(vm, VM.kIsolateStream,
-                                          (event, sub, completer) {
+    Completer completer = new Completer();
+    var stream = await vm.getEventStream(VM.kIsolateStream);
+    var subscription;
+    subscription = stream.listen((ServiceEvent event) {
       if (event.kind == ServiceEvent.kIsolateExit) {
         resumedReceived++;
-        if (resumedReceived == resumeCount) {
-          sub.cancel();
+        if (resumedReceived >= resumeCount) {
+          subscription.cancel();
           completer.complete(null);
         }
       }
     });
+
+    // Resume a subset of the isolates.
     var resumesIssued = 0;
     var isolateList = vm.isolates.toList();
     for (var isolate in isolateList) {
@@ -118,12 +115,11 @@
         break;
       }
     }
-    return eventsDone;
+    await completer.future;
   },
 
   (VM vm) async {
-    expect(numPaused(vm), spawnCount - resumeCount);
-    expect(numRunning(vm), 1);
+    expect(numPaused(vm), spawnCount + 1 - resumeCount); 
   },
 ];
 
diff --git a/runtime/observatory/tests/service/pause_on_start_and_exit_test.dart b/runtime/observatory/tests/service/pause_on_start_and_exit_test.dart
new file mode 100644
index 0000000..012a68f
--- /dev/null
+++ b/runtime/observatory/tests/service/pause_on_start_and_exit_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// VMOptions=--compile_all --error_on_bad_type --error_on_bad_override
+
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+void testMain() {
+  print('Hello');
+}
+
+var tests = [
+
+(Isolate isolate) async {
+  Completer completer = new Completer();
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  var subscription;
+  subscription = stream.listen((ServiceEvent event) {
+    if (event.kind == ServiceEvent.kPauseStart) {
+      print('Received PauseStart');
+      subscription.cancel();
+      completer.complete();
+    }
+  });
+
+  if (isolate.pauseEvent != null &&
+      isolate.pauseEvent.kind == ServiceEvent.kPauseStart) {
+    // Wait for the isolate to hit PauseStart.
+    subscription.cancel();
+  } else {
+    await completer.future;
+  }
+
+  completer = new Completer();
+  stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  subscription = stream.listen((ServiceEvent event) {
+    if (event.kind == ServiceEvent.kPauseExit) {
+      print('Received PauseExit');
+      subscription.cancel();
+      completer.complete();
+    }
+  });
+
+  print('Resuming...');
+  isolate.resume();
+
+  // Wait for the isolate to hit PauseExit.
+  await completer.future;
+},
+
+];
+
+main(args) => runIsolateTests(args, tests,
+                              testeeConcurrent: testMain,
+                              pause_on_start: true, pause_on_exit: true);
diff --git a/runtime/observatory/tests/service/pause_on_start_then_step_test.dart b/runtime/observatory/tests/service/pause_on_start_then_step_test.dart
new file mode 100644
index 0000000..2c7b3b6
--- /dev/null
+++ b/runtime/observatory/tests/service/pause_on_start_then_step_test.dart
@@ -0,0 +1,58 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// VMOptions=--compile_all --error_on_bad_type --error_on_bad_override
+
+import 'package:observatory/service_io.dart';
+import 'package:unittest/unittest.dart';
+import 'test_helper.dart';
+import 'dart:async';
+
+void testMain() {
+  print('Hello');
+}
+
+var tests = [
+
+(Isolate isolate) async {
+  Completer completer = new Completer();
+  var stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  var subscription;
+  subscription = stream.listen((ServiceEvent event) {
+    if (event.kind == ServiceEvent.kPauseStart) {
+      print('Received PauseStart');
+      subscription.cancel();
+      completer.complete();
+    }
+  });
+
+  if (isolate.pauseEvent != null &&
+      isolate.pauseEvent.kind == ServiceEvent.kPauseStart) {
+    // Wait for the isolate to hit PauseStart.
+    subscription.cancel();
+  } else {
+    await completer.future;
+  }
+
+  completer = new Completer();
+  stream = await isolate.vm.getEventStream(VM.kDebugStream);
+  subscription = stream.listen((ServiceEvent event) {
+    if (event.kind == ServiceEvent.kPauseBreakpoint) {
+      print('Received PauseBreakpoint');
+      subscription.cancel();
+      completer.complete();
+    }
+  });
+
+  print('Stepping...');
+  isolate.stepInto();
+
+  // Wait for the isolate to hit PauseBreakpoint.
+  await completer.future;
+},
+
+];
+
+main(args) => runIsolateTests(args, tests,
+                              testeeConcurrent: testMain,
+                              pause_on_start: true, pause_on_exit: true);
diff --git a/runtime/observatory/tests/service/test_helper.dart b/runtime/observatory/tests/service/test_helper.dart
index 4c492ed..d640dc5 100644
--- a/runtime/observatory/tests/service/test_helper.dart
+++ b/runtime/observatory/tests/service/test_helper.dart
@@ -26,9 +26,12 @@
                             Platform.script.toFilePath(),
                             _TESTEE_MODE_FLAG] {}
 
-  Future<int> launch(bool pause_on_exit) {
+  Future<int> launch(bool pause_on_start, bool pause_on_exit) {
     String dartExecutable = Platform.executable;
     var fullArgs = [];
+    if (pause_on_start == true) {
+      fullArgs.add('--pause-isolates-on-start');
+    }
     if (pause_on_exit == true) {
       fullArgs.add('--pause-isolates-on-exit');
     }
@@ -49,7 +52,7 @@
           var port = portExp.firstMatch(line).group(1);
           portNumber = int.parse(port);
         }
-        if (line == '') {
+        if (pause_on_start || line == '') {
           // Received blank line.
           blank = true;
         }
@@ -98,20 +101,26 @@
                      List<IsolateTest> tests,
                      {void testeeBefore(),
                       void testeeConcurrent(),
-                      bool pause_on_exit}) {
+                      bool pause_on_start: false,
+                      bool pause_on_exit: false}) {
+  assert(!pause_on_start || testeeBefore == null);
   if (mainArgs.contains(_TESTEE_MODE_FLAG)) {
-    if (testeeBefore != null) {
-      testeeBefore();
+    if (!pause_on_start) {
+      if (testeeBefore != null) {
+        testeeBefore();
+      }
+      print(''); // Print blank line to signal that we are ready.
     }
-    print(''); // Print blank line to signal that we are ready.
     if (testeeConcurrent != null) {
       testeeConcurrent();
     }
-    // Wait around for the process to be killed.
-    stdin.first.then((_) => exit(0));
+    if (!pause_on_exit) {
+      // Wait around for the process to be killed.
+      stdin.first.then((_) => exit(0));
+    }
   } else {
     var process = new _TestLauncher();
-    process.launch(pause_on_exit).then((port) {
+    process.launch(pause_on_start, pause_on_exit).then((port) {
       if (mainArgs.contains("--gdb")) {
         port = 8181;
       }
@@ -140,26 +149,6 @@
 }
 
 
-// Cancel the subscription and complete the completer when finished processing
-// events.
-typedef void ServiceEventHandler(ServiceEvent event,
-                                 StreamSubscription subscription,
-                                 Completer completer);
-
-Future processServiceEvents(VM vm,
-                            String streamId,
-                            ServiceEventHandler handler) {
-  Completer completer = new Completer();
-  vm.getEventStream(streamId).then((stream) {
-    var subscription;
-    subscription = stream.listen((ServiceEvent event) {
-      handler(event, subscription, completer);
-    });
-  });
-  return completer.future;
-}
-
-
 Future<Isolate> hasStoppedAtBreakpoint(Isolate isolate) {
   // Set up a listener to wait for breakpoint events.
   Completer completer = new Completer();
@@ -286,20 +275,25 @@
                   List<VMTest> tests,
                   {Future testeeBefore(),
                    Future testeeConcurrent(),
-                   bool pause_on_exit}) async {
+                   bool pause_on_start: false,
+                   bool pause_on_exit: false}) async {
   if (mainArgs.contains(_TESTEE_MODE_FLAG)) {
-    if (testeeBefore != null) {
-      await testeeBefore();
+    if (!pause_on_start) {
+      if (testeeBefore != null) {
+        await testeeBefore();
+      }
+      print(''); // Print blank line to signal that we are ready.
     }
-    print(''); // Print blank line to signal that we are ready.
     if (testeeConcurrent != null) {
       await testeeConcurrent();
     }
-    // Wait around for the process to be killed.
-    stdin.first.then((_) => exit(0));
+    if (!pause_on_exit) {
+      // Wait around for the process to be killed.
+      stdin.first.then((_) => exit(0));
+    }
   } else {
     var process = new _TestLauncher();
-    process.launch(pause_on_exit).then((port) async {
+    process.launch(pause_on_start, pause_on_exit).then((port) async {
       if (mainArgs.contains("--gdb")) {
         port = 8181;
       }
diff --git a/runtime/vm/dart_api_impl.cc b/runtime/vm/dart_api_impl.cc
index 038bfc8..3ccb68b 100644
--- a/runtime/vm/dart_api_impl.cc
+++ b/runtime/vm/dart_api_impl.cc
@@ -1568,6 +1568,11 @@
   } else {
     lib ^= Api::UnwrapHandle(library);
   }
+  isolate->heap()->CollectAllGarbage();
+#if defined(DEBUG)
+  FunctionVisitor check_canonical(isolate);
+  isolate->heap()->IterateObjects(&check_canonical);
+#endif  // #if defined(DEBUG).
   ScriptSnapshotWriter writer(buffer, ApiReallocate);
   writer.WriteScriptSnapshot(lib);
   *size = writer.BytesWritten();
diff --git a/runtime/vm/dart_api_message.cc b/runtime/vm/dart_api_message.cc
index aa08099..69fb891 100644
--- a/runtime/vm/dart_api_message.cc
+++ b/runtime/vm/dart_api_message.cc
@@ -535,6 +535,7 @@
                                                      intptr_t object_id) {
   switch (class_id) {
     case kClassCid: {
+      Read<bool>();  // Consume the is_in_fullsnapshot indicator.
       Dart_CObject_Internal* object = AllocateDartCObjectClass();
       AddBackRef(object_id, object, kIsDeserialized);
       object->internal.as_class.library_url = ReadObjectImpl();
@@ -768,10 +769,21 @@
       READ_TYPED_DATA(Float64, double);
 
     case kGrowableObjectArrayCid: {
-      // A GrowableObjectArray is serialized as its length followed by
-      // its backing store. The backing store is an array with a
-      // length which might be longer than the length of the
-      // GrowableObjectArray.
+      // A GrowableObjectArray is serialized as its type arguments and
+      // length followed by its backing store. The backing store is an
+      // array with a length which might be longer than the length of
+      // the GrowableObjectArray.
+
+      // Read and skip the type arguments field.
+      // TODO(sjesse): Remove this when message serialization format is
+      // updated (currently type_arguments is leaked).
+      Dart_CObject* type_arguments = ReadObjectImpl();
+      if (type_arguments != &type_arguments_marker &&
+          type_arguments->type != Dart_CObject_kNull) {
+        return AllocateDartCObjectUnsupported();
+      }
+
+      // Read the length field.
       intptr_t len = ReadSmiValue();
 
       Dart_CObject* value = GetBackRef(object_id);
diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
index c7d549c..5573f4b 100644
--- a/runtime/vm/debugger.cc
+++ b/runtime/vm/debugger.cc
@@ -1270,6 +1270,7 @@
   resume_action_ = kStepOut;
 }
 
+
 RawFunction* Debugger::ResolveFunction(const Library& library,
                                        const String& class_name,
                                        const String& function_name) {
@@ -2322,6 +2323,13 @@
 }
 
 
+void Debugger::EnterSingleStepMode() {
+  stepping_fp_ = 0;
+  DeoptimizeWorld();
+  isolate_->set_single_step(true);
+}
+
+
 void Debugger::HandleSteppingRequest(DebuggerStackTrace* stack_trace) {
   stepping_fp_ = 0;
   if (resume_action_ == kSingleStep) {
diff --git a/runtime/vm/debugger.h b/runtime/vm/debugger.h
index faaf558..79a620c 100644
--- a/runtime/vm/debugger.h
+++ b/runtime/vm/debugger.h
@@ -459,6 +459,12 @@
 
   bool IsPaused() const { return pause_event_ != NULL; }
 
+  // Put the isolate into single stepping mode when Dart code next runs.
+  //
+  // This is used by the vm service to allow the user to step while
+  // paused at isolate start.
+  void EnterSingleStepMode();
+
   // Indicates why the debugger is currently paused.  If the debugger
   // is not paused, this returns NULL.  Note that the debugger can be
   // paused for breakpoints, isolate interruption, and (sometimes)
diff --git a/runtime/vm/flow_graph_inliner.cc b/runtime/vm/flow_graph_inliner.cc
index a433948..a646017 100644
--- a/runtime/vm/flow_graph_inliner.cc
+++ b/runtime/vm/flow_graph_inliner.cc
@@ -1818,6 +1818,16 @@
 }
 
 
+// Use function name to determine if inlineable operator.
+// TODO(srdjan): add names as necessary
+static bool IsInlineableOperator(const Function& function) {
+  return (function.name() == Symbols::IndexToken().raw()) ||
+         (function.name() == Symbols::AssignIndexToken().raw()) ||
+         (function.name() == Symbols::Plus().raw()) ||
+         (function.name() == Symbols::Minus().raw());
+}
+
+
 bool FlowGraphInliner::AlwaysInline(const Function& function) {
   const char* kAlwaysInlineAnnotation = "AlwaysInline";
   if (FLAG_enable_inlining_annotations &&
@@ -1828,7 +1838,8 @@
   }
 
   if (function.IsImplicitGetterFunction() || function.IsGetterFunction() ||
-      function.IsImplicitSetterFunction() || function.IsSetterFunction()) {
+      function.IsImplicitSetterFunction() || function.IsSetterFunction() ||
+      IsInlineableOperator(function)) {
     const intptr_t count = function.optimized_instruction_count();
     if ((count != 0) && (count < FLAG_inline_getters_setters_smaller_than)) {
       return true;
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index f2572a1..cf07132 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -51,6 +51,7 @@
 
 DECLARE_FLAG(bool, polymorphic_with_deopt);
 DECLARE_FLAG(bool, source_lines);
+DECLARE_FLAG(bool, trace_field_guards);
 DECLARE_FLAG(bool, trace_type_check_elimination);
 DECLARE_FLAG(bool, warn_on_javascript_compatibility);
 
@@ -4596,14 +4597,25 @@
         Function::Handle(Z, owner.LookupGetterFunction(field_name));
     const Function& setter =
         Function::Handle(Z, owner.LookupSetterFunction(field_name));
-    bool result = !getter.IsNull()
-               && !setter.IsNull()
-               && (setter.usage_counter() > 0)
-               && (FLAG_getter_setter_ratio * setter.usage_counter() >=
-                   getter.usage_counter());
-    if (!result) {
-      if (FLAG_trace_optimization) {
+    bool unboxed_field = false;
+    if (!getter.IsNull() && !setter.IsNull()) {
+      if (field.is_double_initialized()) {
+        unboxed_field = true;
+      } else if ((setter.usage_counter() > 0) &&
+                 ((FLAG_getter_setter_ratio * setter.usage_counter()) >=
+                   getter.usage_counter())) {
+        unboxed_field = true;
+      }
+    }
+    if (!unboxed_field) {
+      if (FLAG_trace_optimization || FLAG_trace_field_guards) {
         ISL_Print("Disabling unboxing of %s\n", field.ToCString());
+        if (!setter.IsNull()) {
+          OS::Print("  setter usage count: %" Pd "\n", setter.usage_counter());
+        }
+        if (!getter.IsNull()) {
+          OS::Print("  getter usage count: %" Pd "\n", getter.usage_counter());
+        }
       }
       field.set_is_unboxing_candidate(false);
       field.DeoptimizeDependentCode();
diff --git a/runtime/vm/intermediate_language.h b/runtime/vm/intermediate_language.h
index 44919ff..9bae137 100644
--- a/runtime/vm/intermediate_language.h
+++ b/runtime/vm/intermediate_language.h
@@ -335,6 +335,8 @@
 
   void PrintTo(BufferFormatter* f) const;
 
+  const char* ToCString() const;
+
   const char* DebugName() const { return "Value"; }
 
   bool IsSmiValue() { return Type()->ToCid() == kSmiCid; }
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 83e45e9..4351b1d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -399,7 +399,7 @@
     uword address = heap->Allocate(Instance::InstanceSize(), Heap::kOld);
     null_ = reinterpret_cast<RawInstance*>(address + kHeapObjectTag);
     // The call below is using 'null_' to initialize itself.
-    InitializeObject(address, kNullCid, Instance::InstanceSize());
+    InitializeObject(address, kNullCid, Instance::InstanceSize(), true);
   }
 }
 
@@ -462,7 +462,7 @@
     intptr_t size = Class::InstanceSize();
     uword address = heap->Allocate(size, Heap::kOld);
     class_class_ = reinterpret_cast<RawClass*>(address + kHeapObjectTag);
-    InitializeObject(address, Class::kClassId, size);
+    InitializeObject(address, Class::kClassId, size, true);
 
     Class fake;
     // Initialization from Class::New<Class>.
@@ -635,7 +635,7 @@
   // Allocate and initialize the empty_array instance.
   {
     uword address = heap->Allocate(Array::InstanceSize(0), Heap::kOld);
-    InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0));
+    InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(0), true);
     Array::initializeHandle(
         empty_array_,
         reinterpret_cast<RawArray*>(address + kHeapObjectTag));
@@ -646,7 +646,7 @@
   // Allocate and initialize the zero_array instance.
   {
     uword address = heap->Allocate(Array::InstanceSize(1), Heap::kOld);
-    InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1));
+    InitializeObject(address, kImmutableArrayCid, Array::InstanceSize(1), true);
     Array::initializeHandle(
         zero_array_,
         reinterpret_cast<RawArray*>(address + kHeapObjectTag));
@@ -661,7 +661,8 @@
         heap->Allocate(ObjectPool::InstanceSize(0), Heap::kOld);
     InitializeObject(address,
                      kObjectPoolCid,
-                     ObjectPool::InstanceSize(0));
+                     ObjectPool::InstanceSize(0),
+                     true);
     ObjectPool::initializeHandle(
         empty_object_pool_,
         reinterpret_cast<RawObjectPool*>(address + kHeapObjectTag));
@@ -673,7 +674,8 @@
   {
     uword address = heap->Allocate(PcDescriptors::InstanceSize(0), Heap::kOld);
     InitializeObject(address, kPcDescriptorsCid,
-                     PcDescriptors::InstanceSize(0));
+                     PcDescriptors::InstanceSize(0),
+                     true);
     PcDescriptors::initializeHandle(
         empty_descriptors_,
         reinterpret_cast<RawPcDescriptors*>(address + kHeapObjectTag));
@@ -687,7 +689,8 @@
         heap->Allocate(LocalVarDescriptors::InstanceSize(0), Heap::kOld);
     InitializeObject(address,
                      kLocalVarDescriptorsCid,
-                     LocalVarDescriptors::InstanceSize(0));
+                     LocalVarDescriptors::InstanceSize(0),
+                     true);
     LocalVarDescriptors::initializeHandle(
         empty_var_descriptors_,
         reinterpret_cast<RawLocalVarDescriptors*>(address + kHeapObjectTag));
@@ -703,7 +706,8 @@
         heap->Allocate(ExceptionHandlers::InstanceSize(0), Heap::kOld);
     InitializeObject(address,
                      kExceptionHandlersCid,
-                     ExceptionHandlers::InstanceSize(0));
+                     ExceptionHandlers::InstanceSize(0),
+                     true);
     ExceptionHandlers::initializeHandle(
         empty_exception_handlers_,
         reinterpret_cast<RawExceptionHandlers*>(address + kHeapObjectTag));
@@ -809,6 +813,7 @@
     ASSERT(!obj->IsMarked());
     // Free list elements should never be marked.
     if (!obj->IsFreeListElement()) {
+      ASSERT(obj->IsVMHeapObject());
       obj->SetMarkBitUnsynchronized();
     }
   }
@@ -1473,7 +1478,7 @@
 
 #define ADD_SET_FIELD(clazz)                                                   \
   field_name = Symbols::New("cid"#clazz);                                      \
-  field = Field::New(field_name, true, false, true, true, cls, 0);             \
+  field = Field::New(field_name, true, false, true, false, cls, 0);            \
   value = Smi::New(k##clazz##Cid);                                             \
   field.set_value(value);                                                      \
   field.set_type(Type::Handle(Type::IntType()));                               \
@@ -1674,7 +1679,10 @@
 }
 
 
-void Object::InitializeObject(uword address, intptr_t class_id, intptr_t size) {
+void Object::InitializeObject(uword address,
+                              intptr_t class_id,
+                              intptr_t size,
+                              bool is_vm_object) {
   // TODO(iposva): Get a proper halt instruction from the assembler which
   // would be needed here for code objects.
   uword initial_value = reinterpret_cast<uword>(null_);
@@ -1688,7 +1696,9 @@
   ASSERT(class_id != kIllegalCid);
   tags = RawObject::ClassIdTag::update(class_id, tags);
   tags = RawObject::SizeTag::update(size, tags);
+  tags = RawObject::VMHeapObjectTag::update(is_vm_object, tags);
   reinterpret_cast<RawObject*>(address)->tags_ = tags;
+  ASSERT(is_vm_object == RawObject::IsVMHeapObject(tags));
   VerifiedMemory::Accept(address, size);
 }
 
@@ -1746,7 +1756,7 @@
     Profiler::RecordAllocation(isolate, cls_id);
   }
   NoSafepointScope no_safepoint;
-  InitializeObject(address, cls_id, size);
+  InitializeObject(address, cls_id, size, (isolate == Dart::vm_isolate()));
   RawObject* raw_obj = reinterpret_cast<RawObject*>(address + kHeapObjectTag);
   ASSERT(cls_id == RawObject::ClassIdTag::decode(raw_obj->ptr()->tags_));
   return raw_obj;
@@ -1840,6 +1850,12 @@
 }
 
 
+bool Class::IsInFullSnapshot() const {
+  NoSafepointScope no_safepoint;
+  return raw_ptr()->library_->ptr()->is_in_fullsnapshot_;
+}
+
+
 RawType* Class::SignatureType() const {
   ASSERT(IsSignatureClass());
   const Function& function = Function::Handle(signature_function());
@@ -7214,7 +7230,7 @@
                      bool is_static,
                      bool is_final,
                      bool is_const,
-                     bool is_synthetic,
+                     bool is_reflectable,
                      const Class& owner,
                      intptr_t token_pos) {
   ASSERT(!owner.IsNull());
@@ -7228,7 +7244,8 @@
   }
   result.set_is_final(is_final);
   result.set_is_const(is_const);
-  result.set_is_synthetic(is_synthetic);
+  result.set_is_reflectable(is_reflectable);
+  result.set_is_double_initialized(false);
   result.set_owner(owner);
   result.set_token_pos(token_pos);
   result.set_has_initializer(false);
@@ -7443,7 +7460,7 @@
                              true,  // is_static
                              true,  // is_final
                              true,  // is_const
-                             true,  // is_synthetic
+                             false,  // is_reflectable
                              field_owner,
                              this->token_pos());
   closure_field.set_value(Instance::Cast(result));
@@ -9032,7 +9049,7 @@
                                           true,   // is_static
                                           false,  // is_final
                                           false,  // is_const
-                                          true,   // is_synthetic
+                                          false,  // is_reflectable
                                           cls,
                                           token_pos));
   field.set_type(Type::Handle(Type::DynamicType()));
@@ -9825,6 +9842,7 @@
   result.StorePointer(&result.raw_ptr()->load_error_, Instance::null());
   result.set_native_entry_resolver(NULL);
   result.set_native_entry_symbol_resolver(NULL);
+  result.set_is_in_fullsnapshot(false);
   result.StoreNonPointer(&result.raw_ptr()->corelib_imported_, true);
   result.set_debuggable(false);
   result.set_is_dart_scheme(url.StartsWith(Symbols::DartScheme()));
@@ -10540,7 +10558,7 @@
                                           true,   // is_static
                                           false,  // is_final
                                           false,  // is_const
-                                          true,   // is_synthetic
+                                          false,  // is_reflectable
                                           owner_class,
                                           token_pos));
   field.set_type(Type::Handle(Type::DynamicType()));
@@ -15257,6 +15275,7 @@
 void Type::SetIsFinalized() const {
   ASSERT(!IsFinalized());
   if (IsInstantiated()) {
+    ASSERT(HasResolvedTypeClass());
     set_type_state(RawType::kFinalizedInstantiated);
   } else {
     set_type_state(RawType::kFinalizedUninstantiated);
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 5bfc041..8d3ef5e 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -249,23 +249,6 @@
     return AtomicOperations::CompareAndSwapWord(
         &raw()->ptr()->tags_, old_tags, new_tags);
   }
-  void set_tags(intptr_t value) const {
-    ASSERT(!IsNull());
-    // TODO(asiva): Remove the capability of setting tags in general. The mask
-    // here only allows for canonical and from_snapshot flags to be set.
-    value = value & 0x0000000c;
-    uword tags = raw()->ptr()->tags_;
-    uword old_tags;
-    do {
-      old_tags = tags;
-      uword new_tags = (old_tags & ~0x0000000c) | value;
-      tags = CompareAndSwapTags(old_tags, new_tags);
-    } while (tags != old_tags);
-  }
-  void SetCreatedFromSnapshot() const {
-    ASSERT(!IsNull());
-    raw()->SetCreatedFromSnapshot();
-  }
   bool IsCanonical() const {
     ASSERT(!IsNull());
     return raw()->IsCanonical();
@@ -728,7 +711,10 @@
     return -kWordSize;
   }
 
-  static void InitializeObject(uword address, intptr_t id, intptr_t size);
+  static void InitializeObject(uword address,
+                               intptr_t id,
+                               intptr_t size,
+                               bool is_vm_object);
 
   static void RegisterClass(const Class& cls,
                             const String& name,
@@ -945,6 +931,7 @@
   RawString* Name() const;
   RawString* PrettyName() const;
   RawString* UserVisibleName() const;
+  bool IsInFullSnapshot() const;
 
   virtual RawString* DictionaryName() const { return Name(); }
 
@@ -1115,6 +1102,10 @@
   static bool IsSignatureClass(RawClass* cls) {
     return cls->ptr()->signature_function_ != Object::null();
   }
+  static bool IsInFullSnapshot(RawClass* cls) {
+    NoSafepointScope no_safepoint;
+    return cls->ptr()->library_->ptr()->is_in_fullsnapshot_;
+  }
 
   // Check if this class represents a canonical signature class, i.e. not an
   // alias as defined in a typedef.
@@ -2814,8 +2805,14 @@
   bool is_static() const { return StaticBit::decode(raw_ptr()->kind_bits_); }
   bool is_final() const { return FinalBit::decode(raw_ptr()->kind_bits_); }
   bool is_const() const { return ConstBit::decode(raw_ptr()->kind_bits_); }
-  bool is_synthetic() const {
-    return SyntheticBit::decode(raw_ptr()->kind_bits_);
+  bool is_reflectable() const {
+    return ReflectableBit::decode(raw_ptr()->kind_bits_);
+  }
+  bool is_double_initialized() const {
+    return DoubleInitializedBit::decode(raw_ptr()->kind_bits_);
+  }
+  void set_is_double_initialized(bool value) const {
+    set_kind_bits(DoubleInitializedBit::update(value, raw_ptr()->kind_bits_));
   }
 
   inline intptr_t Offset() const;
@@ -2838,7 +2835,7 @@
                        bool is_static,
                        bool is_final,
                        bool is_const,
-                       bool is_synthetic,
+                       bool is_reflectable,
                        const Class& owner,
                        intptr_t token_pos);
 
@@ -2987,16 +2984,18 @@
     kFinalBit,
     kHasInitializerBit,
     kUnboxingCandidateBit,
-    kSyntheticBit
+    kReflectableBit,
+    kDoubleInitializedBit,
   };
   class ConstBit : public BitField<bool, kConstBit, 1> {};
   class StaticBit : public BitField<bool, kStaticBit, 1> {};
   class FinalBit : public BitField<bool, kFinalBit, 1> {};
   class HasInitializerBit : public BitField<bool, kHasInitializerBit, 1> {};
   class UnboxingCandidateBit : public BitField<bool,
-                                               kUnboxingCandidateBit, 1> {
-  };
-  class SyntheticBit : public BitField<bool, kSyntheticBit, 1> {};
+                                               kUnboxingCandidateBit, 1> {};
+  class ReflectableBit : public BitField<bool, kReflectableBit, 1> {};
+  class DoubleInitializedBit : public BitField<bool,
+                                               kDoubleInitializedBit, 1> {};
 
   // Update guarded cid and guarded length for this field. Returns true, if
   // deoptimization of dependent code is required.
@@ -3012,8 +3011,8 @@
   void set_is_const(bool value) const {
     set_kind_bits(ConstBit::update(value, raw_ptr()->kind_bits_));
   }
-  void set_is_synthetic(bool value) const {
-    set_kind_bits(SyntheticBit::update(value, raw_ptr()->kind_bits_));
+  void set_is_reflectable(bool value) const {
+    set_kind_bits(ReflectableBit::update(value, raw_ptr()->kind_bits_));
   }
   void set_owner(const Object& value) const {
     StorePointer(&raw_ptr()->owner_, value.raw());
@@ -3384,6 +3383,11 @@
                     native_symbol_resolver);
   }
 
+  bool is_in_fullsnapshot() const { return raw_ptr()->is_in_fullsnapshot_; }
+  void set_is_in_fullsnapshot(bool value) const {
+    StoreNonPointer(&raw_ptr()->is_in_fullsnapshot_, value);
+  }
+
   RawError* Patch(const Script& script) const;
 
   RawString* PrivateName(const String& name) const;
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index 156fb48..764de50 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -281,7 +281,7 @@
   const Array& one_fields = Array::Handle(Array::New(1));
   const String& field_name = String::Handle(Symbols::New("the_field"));
   const Field& field = Field::Handle(
-      Field::New(field_name, false, false, false, false, one_field_class, 0));
+      Field::New(field_name, false, false, false, true, one_field_class, 0));
   one_fields.SetAt(0, field);
   one_field_class.SetFields(one_fields);
   one_field_class.Finalize();
@@ -2991,7 +2991,7 @@
   const Class& cls = Class::Handle(CreateTestClass("global:"));
   const String& field_name = String::Handle(Symbols::New(name));
   const Field& field =
-      Field::Handle(Field::New(field_name, true, false, false, false, cls, 0));
+      Field::Handle(Field::New(field_name, true, false, false, true, cls, 0));
   return field.raw();
 }
 
diff --git a/runtime/vm/parser.cc b/runtime/vm/parser.cc
index 0179633..419cd4e 100644
--- a/runtime/vm/parser.cc
+++ b/runtime/vm/parser.cc
@@ -3972,7 +3972,8 @@
       // value is not assignable (assuming checked mode and disregarding actual
       // mode), the field value is reset and a kImplicitStaticFinalGetter is
       // created at finalization time.
-      if (LookaheadToken(1) == Token::kSEMICOLON) {
+      if ((LookaheadToken(1) == Token::kSEMICOLON) ||
+          (LookaheadToken(1) == Token::kCOMMA)) {
         has_simple_literal = IsSimpleLiteral(*field->type, &init_value);
       }
       SkipExpr();
@@ -3988,11 +3989,13 @@
     }
 
     // Create the field object.
+    const bool is_reflectable =
+        !(library_.is_dart_scheme() && library_.IsPrivate(*field->name));
     class_field = Field::New(*field->name,
                              field->has_static,
                              field->has_final,
                              field->has_const,
-                             false,  // Not synthetic.
+                             is_reflectable,
                              current_class(),
                              field->name_pos);
     class_field.set_type(*field->type);
@@ -4008,6 +4011,9 @@
     // and rules out many fields from being unnecessary unboxing candidates.
     if (!field->has_static && has_initializer && has_simple_literal) {
       class_field.RecordStore(init_value);
+      if (!init_value.IsNull() && init_value.IsDouble()) {
+        class_field.set_is_double_initialized(true);
+      }
     }
 
     // For static final fields (this includes static const fields), set value to
@@ -4671,7 +4677,7 @@
                            false,  // Not static.
                            true,  // Field is final.
                            false,  // Not const.
-                           false,  // Not synthetic.
+                           true,  // Is reflectable.
                            cls,
                            cls.token_pos());
   index_field.set_type(int_type);
@@ -4743,7 +4749,7 @@
                             /* is_static = */ true,
                             /* is_final = */ true,
                             /* is_const = */ true,
-                            /* is_synthetic = */ false,
+                            /* is_reflectable = */ true,
                             cls,
                             cls.token_pos());
     enum_value.set_type(dynamic_type);
@@ -4781,7 +4787,7 @@
                             /* is_static = */ true,
                             /* is_final = */ true,
                             /* is_const = */ true,
-                            /* is_synthetic = */ false,
+                            /* is_reflectable = */ true,
                             cls,
                             cls.token_pos());
   values_field.set_type(Type::Handle(Z, Type::ArrayType()));
@@ -5365,7 +5371,6 @@
   // Const fields are implicitly final.
   const bool is_final = is_const || (CurrentToken() == Token::kFINAL);
   const bool is_static = true;
-  const bool is_synthetic = false;
   const AbstractType& type = AbstractType::ZoneHandle(Z,
       ParseConstFinalVarOrType(ClassFinalizer::kResolveTypeParameters));
   Field& field = Field::Handle(Z);
@@ -5393,7 +5398,9 @@
                   var_name.ToCString());
     }
 
-    field = Field::New(var_name, is_static, is_final, is_const, is_synthetic,
+    const bool is_reflectable =
+        !(library_.is_dart_scheme() && library_.IsPrivate(var_name));
+    field = Field::New(var_name, is_static, is_final, is_const, is_reflectable,
                        current_class(), name_pos);
     field.set_type(type);
     field.set_value(Instance::Handle(Z, Instance::null()));
@@ -11184,18 +11191,6 @@
               ClassFinalizer::kCanonicalize);
           ASSERT(!type_parameter.IsMalformed());
           left = new(Z) TypeNode(primary->token_pos(), type_parameter);
-        } else if (is_conditional && primary_node->primary().IsClass()) {
-          // The left-hand side of ?. is interpreted as an expression
-          // of type Type, not as a class literal.
-          const Class& type_class = Class::Cast(primary_node->primary());
-          AbstractType& type = Type::ZoneHandle(Z,
-              Type::New(type_class, TypeArguments::Handle(Z),
-              primary_pos, Heap::kOld));
-          type ^= ClassFinalizer::FinalizeType(
-              current_class(), type, ClassFinalizer::kCanonicalize);
-          // Type may be malbounded, but not malformed.
-          ASSERT(!type.IsMalformed());
-          left = new(Z) TypeNode(primary_pos, type);
         } else {
           // Super field access handled in ParseSuperFieldAccess(),
           // super calls handled in ParseSuperCall().
@@ -11210,7 +11205,6 @@
         if (left->IsPrimaryNode() &&
             left->AsPrimaryNode()->primary().IsClass()) {
           // Static method call prefixed with class name.
-          ASSERT(!is_conditional);
           const Class& cls = Class::Cast(left->AsPrimaryNode()->primary());
           selector = ParseStaticCall(cls, *ident, ident_pos);
         } else {
@@ -11237,7 +11231,6 @@
                                                is_conditional);
         } else {
           // Static field access.
-          ASSERT(!is_conditional);
           selector = GenerateStaticFieldAccess(cls, *ident, ident_pos);
           ASSERT(selector != NULL);
           if (selector->IsLoadStaticFieldNode()) {
@@ -12865,9 +12858,17 @@
 RawFunction* Parser::BuildConstructorClosureFunction(const Function& ctr,
                                                      intptr_t token_pos) {
   ASSERT(ctr.kind() == RawFunction::kConstructor);
+  Function& closure = Function::Handle(Z);
+  closure = current_class().LookupClosureFunction(token_pos);
+  if (!closure.IsNull()) {
+    ASSERT(closure.IsConstructorClosureFunction());
+    return closure.raw();
+  }
+
   String& closure_name = String::Handle(Z, ctr.name());
   closure_name = Symbols::FromConcat(Symbols::ConstructorClosurePrefix(),
                                      closure_name);
+
   ParamList params;
   params.AddFinalParameter(token_pos,
                            &Symbols::ClosureParameter(),
@@ -12878,11 +12879,12 @@
   // Replace the types parsed from the constructor.
   params.EraseParameterTypes();
 
-  Function& closure = Function::Handle(Z);
   closure = Function::NewClosureFunction(closure_name,
                                          innermost_function(),
                                          token_pos);
   closure.set_is_generated_body(true);
+  closure.set_is_debuggable(false);
+  closure.set_is_visible(false);
   closure.set_result_type(AbstractType::Handle(Type::DynamicType()));
   AddFormalParamsToFunction(&params, closure);
 
diff --git a/runtime/vm/raw_object.cc b/runtime/vm/raw_object.cc
index 67f0afc..61c2994 100644
--- a/runtime/vm/raw_object.cc
+++ b/runtime/vm/raw_object.cc
@@ -18,10 +18,6 @@
 DEFINE_FLAG(bool, validate_overwrite, true, "Verify overwritten fields.");
 #endif  // DEBUG
 
-bool RawObject::IsVMHeapObject() const {
-  return Dart::vm_isolate()->heap()->Contains(ToAddr(this));
-}
-
 
 void RawObject::Validate(Isolate* isolate) const {
   if (Object::void_class_ == reinterpret_cast<RawClass*>(kHeapObjectTag)) {
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index fa91a14..517379b 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -236,7 +236,7 @@
     kWatchedBit = 0,
     kMarkBit = 1,
     kCanonicalBit = 2,
-    kFromSnapshotBit = 3,
+    kVMHeapObjectBit = 3,
     kRememberedBit = 4,
 #if defined(ARCH_IS_32_BIT)
     kReservedTagPos = 5,  // kReservedBit{100K,1M,10M}
@@ -315,8 +315,6 @@
     uword addr = reinterpret_cast<uword>(this);
     return (addr & kNewObjectAlignmentOffset) == kOldObjectAlignmentOffset;
   }
-  // Assumes this is a heap object.
-  bool IsVMHeapObject() const;
 
   // Like !IsHeapObject() || IsOldObject(), but compiles to a single branch.
   bool IsSmiOrOldObject() const {
@@ -372,11 +370,11 @@
   void ClearCanonical() {
     UpdateTagBit<CanonicalObjectTag>(false);
   }
-  bool IsCreatedFromSnapshot() const {
-    return CreatedFromSnapshotTag::decode(ptr()->tags_);
+  bool IsVMHeapObject() const {
+    return VMHeapObjectTag::decode(ptr()->tags_);
   }
-  void SetCreatedFromSnapshot() {
-    UpdateTagBit<CreatedFromSnapshotTag>(true);
+  void SetVMHeapObject() {
+    UpdateTagBit<VMHeapObjectTag>(true);
   }
 
   // Support for GC remembered bit.
@@ -445,8 +443,8 @@
     return reinterpret_cast<uword>(raw_obj->ptr());
   }
 
-  static bool IsCreatedFromSnapshot(intptr_t value) {
-    return CreatedFromSnapshotTag::decode(value);
+  static bool IsVMHeapObject(intptr_t value) {
+    return VMHeapObjectTag::decode(value);
   }
 
   static bool IsCanonical(intptr_t value) {
@@ -482,7 +480,7 @@
 
   class CanonicalObjectTag : public BitField<bool, kCanonicalBit, 1> {};
 
-  class CreatedFromSnapshotTag : public BitField<bool, kFromSnapshotBit, 1> {};
+  class VMHeapObjectTag : public BitField<bool, kVMHeapObjectBit, 1> {};
 
   class ReservedBits : public
       BitField<intptr_t, kReservedTagPos, kReservedTagSize> {};  // NOLINT
@@ -847,7 +845,7 @@
   // generated on platforms with weak addressing modes (ARM, MIPS).
   int8_t guarded_list_length_in_object_offset_;
 
-  uint8_t kind_bits_;  // static, final, const, has initializer.
+  uint8_t kind_bits_;  // static, final, const, has initializer....
 };
 
 
@@ -950,7 +948,9 @@
   bool corelib_imported_;
   bool is_dart_scheme_;
   bool debuggable_;             // True if debugger can stop in library.
+  bool is_in_fullsnapshot_;     // True if library is in a full snapshot.
 
+  friend class Class;
   friend class Isolate;
 };
 
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index cc193fa..9a12d08 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -31,8 +31,9 @@
   ASSERT(reader != NULL);
 
   Class& cls = Class::ZoneHandle(reader->zone(), Class::null());
+  bool is_in_fullsnapshot = reader->Read<bool>();
   if ((kind == Snapshot::kFull) ||
-      (kind == Snapshot::kScript && !RawObject::IsCreatedFromSnapshot(tags))) {
+      (kind == Snapshot::kScript && !is_in_fullsnapshot)) {
     // Read in the base information.
     classid_t class_id = reader->ReadClassIDValue();
 
@@ -49,9 +50,6 @@
     }
     reader->AddBackRef(object_id, &cls, kIsDeserialized);
 
-    // Set the object tags.
-    cls.set_tags(tags);
-
     // Set all non object fields.
     if (!RawObject::IsInternalVMdefinedClassId(class_id)) {
       // Instance size of a VM defined class is already set up.
@@ -76,8 +74,10 @@
       cls.StorePointer((cls.raw()->from() + i),
                        reader->PassiveObjectHandle()->raw());
     }
+    ASSERT(!cls.IsInFullSnapshot() || (kind == Snapshot::kFull));
   } else {
     cls ^= reader->ReadClassId(object_id);
+    ASSERT((kind == Snapshot::kMessage) || cls.IsInFullSnapshot());
   }
   return cls.raw();
 }
@@ -87,17 +87,22 @@
                        intptr_t object_id,
                        Snapshot::Kind kind) {
   ASSERT(writer != NULL);
+  bool is_in_fullsnapshot = Class::IsInFullSnapshot(this);
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
 
-  if ((kind == Snapshot::kFull) ||
-      (kind == Snapshot::kScript &&
-       !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this)))) {
-    // Write out the class and tags information.
-    writer->WriteVMIsolateObject(kClassCid);
-    writer->WriteTags(writer->GetObjectTags(this));
+  // Write out the class and tags information.
+  writer->WriteVMIsolateObject(kClassCid);
+  writer->WriteTags(writer->GetObjectTags(this));
 
+  // Write out the boolean is_in_fullsnapshot first as this will
+  // help the reader decide how the rest of the information needs
+  // to be interpreted.
+  writer->Write<bool>(is_in_fullsnapshot);
+
+  if ((kind == Snapshot::kFull) ||
+      (kind == Snapshot::kScript && !is_in_fullsnapshot)) {
     // Write out all the non object pointer fields.
     // NOTE: cpp_vtable_ is not written.
     classid_t class_id = ptr()->id_;
@@ -144,9 +149,6 @@
       reader->zone(), NEW_OBJECT(UnresolvedClass));
   reader->AddBackRef(object_id, &unresolved_class, kIsDeserialized);
 
-  // Set the object tags.
-  unresolved_class.set_tags(tags);
-
   // Set all non object fields.
   unresolved_class.set_token_pos(reader->Read<int32_t>());
 
@@ -210,9 +212,7 @@
   // Allocate type object.
   Type& type = Type::ZoneHandle(reader->zone(), NEW_OBJECT(Type));
   bool is_canonical = RawObject::IsCanonical(tags);
-  bool defer_canonicalization = is_canonical &&
-      ((kind == Snapshot::kScript && RawObject::IsCreatedFromSnapshot(tags)) ||
-       kind == Snapshot::kMessage);
+  bool defer_canonicalization = is_canonical && (kind != Snapshot::kFull);
   reader->AddBackRef(object_id, &type, kIsDeserialized, defer_canonicalization);
 
   // Set all non object fields.
@@ -226,13 +226,15 @@
   intptr_t from_offset = OFFSET_OF_FROM(type);
   for (intptr_t i = 0; i <= num_flds; i++) {
     (*reader->PassiveObjectHandle()) =
-        reader->ReadObjectImpl(kAsInlinedObject, object_id, (i + from_offset));
+        reader->ReadObjectImpl(kAsReference, object_id, (i + from_offset));
     type.StorePointer((type.raw()->from() + i),
                       reader->PassiveObjectHandle()->raw());
   }
 
-  // Set the object tags.
-  type.set_tags(tags);
+  // Set the canonical bit.
+  if (!defer_canonicalization && RawObject::IsCanonical(tags)) {
+    type.SetCanonical();
+  }
 
   return type.raw();
 }
@@ -246,6 +248,7 @@
   // Only resolved and finalized types should be written to a snapshot.
   ASSERT((ptr()->type_state_ == RawType::kFinalizedInstantiated) ||
          (ptr()->type_state_ == RawType::kFinalizedUninstantiated));
+  ASSERT(ptr()->type_class_ != Object::null());
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -261,7 +264,8 @@
   // Write out all the object pointer fields. Since we will be canonicalizing
   // the type object when reading it back we should write out all the fields
   // inline and not as references.
-  SnapshotWriterVisitor visitor(writer, false);
+  ASSERT(ptr()->type_class_ != Object::null());
+  SnapshotWriterVisitor visitor(writer);
   visitor.VisitPointers(from(), to());
 }
 
@@ -277,9 +281,6 @@
       reader->zone(), NEW_OBJECT(TypeRef));
   reader->AddBackRef(object_id, &type_ref, kIsDeserialized);
 
-  // Set the object tags.
-  type_ref.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -325,9 +326,6 @@
       reader->zone(), NEW_OBJECT(TypeParameter));
   reader->AddBackRef(object_id, &type_parameter, kIsDeserialized);
 
-  // Set the object tags.
-  type_parameter.set_tags(tags);
-
   // Set all non object fields.
   type_parameter.set_token_pos(reader->Read<int32_t>());
   type_parameter.set_index(reader->Read<int16_t>());
@@ -387,9 +385,6 @@
       reader->zone(), NEW_OBJECT(BoundedType));
   reader->AddBackRef(object_id, &bounded_type, kIsDeserialized);
 
-  // Set the object tags.
-  bounded_type.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -453,9 +448,7 @@
   TypeArguments& type_arguments = TypeArguments::ZoneHandle(
       reader->zone(), NEW_OBJECT_WITH_LEN_SPACE(TypeArguments, len, kind));
   bool is_canonical = RawObject::IsCanonical(tags);
-  bool defer_canonicalization = is_canonical &&
-      ((kind == Snapshot::kScript && RawObject::IsCreatedFromSnapshot(tags)) ||
-       kind == Snapshot::kMessage);
+  bool defer_canonicalization = is_canonical && (kind != Snapshot::kFull);
   reader->AddBackRef(object_id,
                      &type_arguments,
                      kIsDeserialized,
@@ -474,12 +467,14 @@
       reinterpret_cast<RawAbstractType**>(type_arguments.raw()->ptr());
   for (intptr_t i = 0; i < len; i++) {
     *reader->TypeHandle() ^=
-        reader->ReadObjectImpl(kAsInlinedObject, object_id, (i + offset));
+        reader->ReadObjectImpl(kAsReference, object_id, (i + offset));
     type_arguments.SetTypeAt(i, *reader->TypeHandle());
   }
 
-  // Set the object tags .
-  type_arguments.set_tags(tags);
+  // Set the canonical bit.
+  if (!defer_canonicalization && RawObject::IsCanonical(tags)) {
+    type_arguments.SetCanonical();
+  }
 
   return type_arguments.raw();
 }
@@ -508,7 +503,7 @@
   // Write out the individual types.
   intptr_t len = Smi::Value(ptr()->length_);
   for (intptr_t i = 0; i < len; i++) {
-    writer->WriteObjectImpl(ptr()->types()[i], kAsInlinedObject);
+    writer->WriteObjectImpl(ptr()->types()[i], kAsReference);
   }
 }
 
@@ -518,18 +513,12 @@
                                     intptr_t tags,
                                     Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
 
   // Allocate function object.
   PatchClass& cls = PatchClass::ZoneHandle(reader->zone(),
                                             NEW_OBJECT(PatchClass));
   reader->AddBackRef(object_id, &cls, kIsDeserialized);
 
-  // Set the object tags.
-  cls.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -539,6 +528,9 @@
     cls.StorePointer((cls.raw()->from() + i),
                      reader->PassiveObjectHandle()->raw());
   }
+  ASSERT(((kind == Snapshot::kScript) &&
+          !Class::IsInFullSnapshot(cls.source_class())) ||
+         (kind == Snapshot::kFull));
 
   return cls.raw();
 }
@@ -548,9 +540,7 @@
                             intptr_t object_id,
                             Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -569,18 +559,13 @@
                                       intptr_t tags,
                                       Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Allocate closure data object.
   ClosureData& data = ClosureData::ZoneHandle(
       reader->zone(), NEW_OBJECT(ClosureData));
   reader->AddBackRef(object_id, &data, kIsDeserialized);
 
-  // Set the object tags.
-  data.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -597,9 +582,7 @@
                              intptr_t object_id,
                              Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -629,18 +612,13 @@
                                               intptr_t tags,
                                               Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Allocate redirection data object.
   RedirectionData& data = RedirectionData::ZoneHandle(
       reader->zone(), NEW_OBJECT(RedirectionData));
   reader->AddBackRef(object_id, &data, kIsDeserialized);
 
-  // Set the object tags.
-  data.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -661,9 +639,7 @@
                                  intptr_t object_id,
                                  Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -683,18 +659,13 @@
                                 intptr_t tags,
                                 Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Allocate function object.
   Function& func = Function::ZoneHandle(
       reader->zone(), NEW_OBJECT(Function));
   reader->AddBackRef(object_id, &func, kIsDeserialized);
 
-  // Set the object tags.
-  func.set_tags(tags);
-
   // Set all the non object fields.
   func.set_token_pos(reader->Read<int32_t>());
   func.set_end_token_pos(reader->Read<int32_t>());
@@ -730,9 +701,7 @@
                           intptr_t object_id,
                           Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -764,17 +733,12 @@
                           intptr_t tags,
                           Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Allocate field object.
   Field& field = Field::ZoneHandle(reader->zone(), NEW_OBJECT(Field));
   reader->AddBackRef(object_id, &field, kIsDeserialized);
 
-  // Set the object tags.
-  field.set_tags(tags);
-
   // Set all non object fields.
   field.set_token_pos(reader->Read<int32_t>());
   field.set_guarded_cid(reader->Read<int32_t>());
@@ -803,9 +767,7 @@
                        intptr_t object_id,
                        Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -838,9 +800,6 @@
       reader->zone(), NEW_OBJECT(LiteralToken));
   reader->AddBackRef(object_id, &literal_token, kIsDeserialized);
 
-  // Set the object tags.
-  literal_token.set_tags(tags);
-
   // Read the token attributes.
   Token::Kind token_kind = static_cast<Token::Kind>(reader->Read<int32_t>());
   literal_token.set_kind(token_kind);
@@ -886,9 +845,7 @@
                                       intptr_t tags,
                                       Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Read the length so that we can determine number of tokens to read.
   intptr_t len = reader->ReadSmiValue();
@@ -898,9 +855,6 @@
       reader->zone(), NEW_OBJECT_WITH_LEN(TokenStream, len));
   reader->AddBackRef(object_id, &token_stream, kIsDeserialized);
 
-  // Set the object tags.
-  token_stream.set_tags(tags);
-
   // Read the stream of tokens into the TokenStream object for script
   // snapshots as we made a copy of token stream.
   if (kind == Snapshot::kScript) {
@@ -924,9 +878,7 @@
                              intptr_t object_id,
                              Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -953,17 +905,12 @@
                             intptr_t tags,
                             Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Allocate script object.
   Script& script = Script::ZoneHandle(reader->zone(), NEW_OBJECT(Script));
   reader->AddBackRef(object_id, &script, kIsDeserialized);
 
-  // Set the object tags.
-  script.set_tags(tags);
-
   script.StoreNonPointer(&script.raw_ptr()->line_offset_,
                          reader->Read<int32_t>());
   script.StoreNonPointer(&script.raw_ptr()->col_offset_,
@@ -993,9 +940,7 @@
                         Snapshot::Kind kind) {
   ASSERT(writer != NULL);
   ASSERT(tokens_ != TokenStream::null());
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -1025,18 +970,16 @@
   Library& library = Library::ZoneHandle(reader->zone(), Library::null());
   reader->AddBackRef(object_id, &library, kIsDeserialized);
 
-  if ((kind == Snapshot::kScript) && RawObject::IsCreatedFromSnapshot(tags)) {
-    ASSERT(kind != Snapshot::kFull);
+  bool is_in_fullsnapshot = reader->Read<bool>();
+  if ((kind == Snapshot::kScript) && is_in_fullsnapshot) {
     // Lookup the object as it should already exist in the heap.
     *reader->StringHandle() ^= reader->ReadObjectImpl(kAsInlinedObject);
     library = Library::LookupLibrary(*reader->StringHandle());
+    ASSERT(library.is_in_fullsnapshot());
   } else {
     // Allocate library object.
     library = NEW_OBJECT(Library);
 
-    // Set the object tags.
-    library.set_tags(tags);
-
     // Set all non object fields.
     library.StoreNonPointer(&library.raw_ptr()->index_,
                             reader->ReadClassIDValue());
@@ -1052,6 +995,11 @@
                             reader->Read<bool>());
     library.StoreNonPointer(&library.raw_ptr()->debuggable_,
                             reader->Read<bool>());
+    if (kind == Snapshot::kFull) {
+      is_in_fullsnapshot = true;
+    }
+    library.StoreNonPointer(&library.raw_ptr()->is_in_fullsnapshot_,
+                            is_in_fullsnapshot);
     // The native resolver and symbolizer are not serialized.
     library.set_native_entry_resolver(NULL);
     library.set_native_entry_symbol_resolver(NULL);
@@ -1088,12 +1036,16 @@
   writer->WriteVMIsolateObject(kLibraryCid);
   writer->WriteTags(writer->GetObjectTags(this));
 
-  if ((kind == Snapshot::kScript) &&
-      RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) {
-    ASSERT(kind != Snapshot::kFull);
+  // Write out the boolean is_in_fullsnapshot_ first as this will
+  // help the reader decide how the rest of the information needs
+  // to be interpreted.
+  writer->Write<bool>(ptr()->is_in_fullsnapshot_);
+
+  if ((kind == Snapshot::kScript) && ptr()->is_in_fullsnapshot_) {
     // Write out library URL so that it can be looked up when reading.
     writer->WriteObjectImpl(ptr()->url_, kAsInlinedObject);
   } else {
+    ASSERT((kind == Snapshot::kFull) || !ptr()->is_in_fullsnapshot_);
     // Write out all non object fields.
     writer->WriteClassIDValue(ptr()->index_);
     writer->WriteClassIDValue(ptr()->num_anonymous_);
@@ -1120,18 +1072,13 @@
                                           intptr_t tags,
                                           Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Allocate library prefix object.
   LibraryPrefix& prefix = LibraryPrefix::ZoneHandle(
       reader->zone(), NEW_OBJECT(LibraryPrefix));
   reader->AddBackRef(object_id, &prefix, kIsDeserialized);
 
-  // Set the object tags.
-  prefix.set_tags(tags);
-
   // Set all non object fields.
   prefix.StoreNonPointer(&prefix.raw_ptr()->num_imports_,
                          reader->Read<int16_t>());
@@ -1157,9 +1104,7 @@
                                intptr_t object_id,
                                Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -1184,18 +1129,13 @@
                                   intptr_t tags,
                                   Snapshot::Kind kind) {
   ASSERT(reader != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(tags)) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Allocate Namespace object.
   Namespace& ns = Namespace::ZoneHandle(
       reader->zone(), NEW_OBJECT(Namespace));
   reader->AddBackRef(object_id, &ns, kIsDeserialized);
 
-  // Set the object tags.
-  ns.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -1214,9 +1154,7 @@
                            intptr_t object_id,
                            Snapshot::Kind kind) {
   ASSERT(writer != NULL);
-  ASSERT(((kind == Snapshot::kScript) &&
-          !RawObject::IsCreatedFromSnapshot(writer->GetObjectTags(this))) ||
-         (kind == Snapshot::kFull));
+  ASSERT((kind == Snapshot::kScript) || (kind == Snapshot::kFull));
 
   // Write out the serialization header value for this object.
   writer->WriteInlinedObjectHeader(object_id);
@@ -1292,9 +1230,6 @@
                                                     PcDescriptors::New(length));
   reader->AddBackRef(object_id, &result, kIsDeserialized);
 
-  // Set the object tags.
-  result.set_tags(tags);
-
   if (result.Length() > 0) {
     NoSafepointScope no_safepoint;
     intptr_t len = result.Length();
@@ -1339,9 +1274,6 @@
         Stackmap::New(length, register_bit_count, pc_offset));
   reader->AddBackRef(object_id, &result, kIsDeserialized);
 
-  // Set the object tags.
-  result.set_tags(tags);
-
   if (result.Length() > 0) {
     NoSafepointScope no_safepoint;
     intptr_t len = (result.Length() + 7) / 8;
@@ -1386,9 +1318,6 @@
         LocalVarDescriptors::New(num_entries));
   reader->AddBackRef(object_id, &result, kIsDeserialized);
 
-  // Set the object tags.
-  result.set_tags(tags);
-
   for (intptr_t i = 0; i < num_entries; i++) {
     (*reader->StringHandle()) ^= reader->ReadObjectImpl(kAsReference);
     result.StorePointer(result.raw()->nameAddrAt(i),
@@ -1442,9 +1371,6 @@
         ExceptionHandlers::New(*reader->ArrayHandle()));
   reader->AddBackRef(object_id, &result, kIsDeserialized);
 
-  // Set the object tags.
-  result.set_tags(tags);
-
   if (result.num_entries() > 0) {
     NoSafepointScope no_safepoint;
     const intptr_t len =
@@ -1492,9 +1418,6 @@
   } else {
     context ^= NEW_OBJECT_WITH_LEN(Context, num_vars);
 
-    // Set the object tags.
-    context.set_tags(tags);
-
     // Set all the object fields.
     // TODO(5411462): Need to assert No GC can happen here, even though
     // allocations may happen.
@@ -1623,9 +1546,6 @@
       ApiError::ZoneHandle(reader->zone(), NEW_OBJECT(ApiError));
   reader->AddBackRef(object_id, &api_error, kIsDeserialized);
 
-  // Set the object tags.
-  api_error.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -1669,9 +1589,6 @@
       LanguageError::ZoneHandle(reader->zone(), NEW_OBJECT(LanguageError));
   reader->AddBackRef(object_id, &language_error, kIsDeserialized);
 
-  // Set the object tags.
-  language_error.set_tags(tags);
-
   // Set all non object fields.
   language_error.set_token_pos(reader->Read<int32_t>());
   language_error.set_kind(reader->Read<uint8_t>());
@@ -1721,9 +1638,6 @@
       reader->zone(), NEW_OBJECT(UnhandledException));
   reader->AddBackRef(object_id, &result, kIsDeserialized);
 
-  // Set the object tags.
-  result.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
@@ -1780,26 +1694,20 @@
   Instance& obj = Instance::ZoneHandle(reader->zone(), Instance::null());
   if (kind == Snapshot::kFull) {
     obj = reader->NewInstance();
+    // Set the canonical bit.
+    if (RawObject::IsCanonical(tags)) {
+      obj.SetCanonical();
+    }
   } else {
     obj ^= Object::Allocate(kInstanceCid,
                             Instance::InstanceSize(),
                             HEAP_SPACE(kind));
-    // When reading a script snapshot we need to canonicalize only those object
-    // references that are objects from the core library (loaded from a
-    // full snapshot). Objects that are only in the script need not be
-    // canonicalized as they are already canonical.
-    // When reading a message snapshot we always have to canonicalize.
-    if (RawObject::IsCanonical(tags) &&
-        (RawObject::IsCreatedFromSnapshot(tags) ||
-         (kind == Snapshot::kMessage))) {
+    if (RawObject::IsCanonical(tags)) {
       obj = obj.CheckAndCanonicalize(NULL);
     }
   }
   reader->AddBackRef(object_id, &obj, kIsDeserialized);
 
-  // Set the object tags.
-  obj.set_tags(tags);
-
   return obj.raw();
 }
 
@@ -1840,25 +1748,21 @@
   Mint& mint = Mint::ZoneHandle(reader->zone(), Mint::null());
   if (kind == Snapshot::kFull) {
     mint = reader->NewMint(value);
-    // Set the object tags.
-    mint.set_tags(tags);
+    // Set the canonical bit.
+    if (RawObject::IsCanonical(tags)) {
+      mint.SetCanonical();
+    }
   } else {
     // When reading a script snapshot we need to canonicalize only those object
     // references that are objects from the core library (loaded from a
     // full snapshot). Objects that are only in the script need not be
     // canonicalized as they are already canonical.
     // When reading a message snapshot we always have to canonicalize.
-    if (RawObject::IsCanonical(tags) &&
-        (RawObject::IsCreatedFromSnapshot(tags) ||
-         (kind == Snapshot::kMessage))) {
+    if (RawObject::IsCanonical(tags)) {
       mint = Mint::NewCanonical(value);
-      ASSERT(mint.IsCanonical() &&
-             (kind == Snapshot::kMessage ||
-              RawObject::IsCreatedFromSnapshot(mint.raw()->ptr()->tags_)));
+      ASSERT(mint.IsCanonical());
     } else {
       mint = Mint::New(value, HEAP_SPACE(kind));
-      // Set the object tags.
-      mint.set_tags(tags);
     }
   }
   reader->AddBackRef(object_id, &mint, kIsDeserialized);
@@ -1906,25 +1810,17 @@
   // If it is a canonical constant make it one.
   // When reading a full snapshot we don't need to canonicalize the object
   // as it would already be a canonical object.
-  // When reading a script snapshot we need to canonicalize only those object
-  // references that are objects from the core library (loaded from a
-  // full snapshot). Objects that are only in the script need not be
-  // canonicalized as they are already canonical.
-  // When reading a message snapshot we always have to canonicalize the object.
-  if (kind == Snapshot::kFull) {
-    // Set the object tags.
-    obj.set_tags(tags);
-  } else if (RawObject::IsCanonical(tags) &&
-             (RawObject::IsCreatedFromSnapshot(tags) ||
-              (kind == Snapshot::kMessage))) {
-    obj ^= obj.CheckAndCanonicalize(NULL);
-    ASSERT(!obj.IsNull());
-    ASSERT(obj.IsCanonical() &&
-           (kind == Snapshot::kMessage ||
-            RawObject::IsCreatedFromSnapshot(obj.raw()->ptr()->tags_)));
-  } else {
-    // Set the object tags.
-    obj.set_tags(tags);
+  // When reading a script snapshot or a message snapshot we always have
+  // to canonicalize the object.
+  if (RawObject::IsCanonical(tags)) {
+    if (kind == Snapshot::kFull) {
+      // Set the canonical bit.
+      obj.SetCanonical();
+    } else {
+      obj ^= obj.CheckAndCanonicalize(NULL);
+      ASSERT(!obj.IsNull());
+      ASSERT(obj.IsCanonical());
+    }
   }
   return obj.raw();
 }
@@ -1961,23 +1857,20 @@
   Double& dbl = Double::ZoneHandle(reader->zone(), Double::null());
   if (kind == Snapshot::kFull) {
     dbl = reader->NewDouble(value);
-    // Set the object tags.
-    dbl.set_tags(tags);
+    // Set the canonical bit.
+    if (RawObject::IsCanonical(tags)) {
+      dbl.SetCanonical();
+    }
   } else {
     // When reading a script snapshot we need to canonicalize only those object
     // references that are objects from the core library (loaded from a
     // full snapshot). Objects that are only in the script need not be
     // canonicalized as they are already canonical.
-    if (RawObject::IsCanonical(tags) &&
-        RawObject::IsCreatedFromSnapshot(tags)) {
+    if (RawObject::IsCanonical(tags)) {
       dbl = Double::NewCanonical(value);
-      ASSERT(dbl.IsCanonical() &&
-             (kind == Snapshot::kMessage ||
-              RawObject::IsCreatedFromSnapshot(dbl.raw()->ptr()->tags_)));
+      ASSERT(dbl.IsCanonical());
     } else {
       dbl = Double::New(value, HEAP_SPACE(kind));
-      // Set the object tags.
-      dbl.set_tags(tags);
     }
   }
   reader->AddBackRef(object_id, &dbl, kIsDeserialized);
@@ -2037,7 +1930,6 @@
   } else {
     // Set up the string object.
     *str_obj = StringType::New(len, HEAP_SPACE(kind));
-    str_obj->set_tags(tags);
     str_obj->SetHash(0);  // Will get computed when needed.
     if (len == 0) {
       return;
@@ -2068,7 +1960,9 @@
     ASSERT(Thread::Current()->no_safepoint_scope_depth() != 0);
     RawOneByteString* obj = reader->NewOneByteString(len);
     str_obj = obj;
-    str_obj.set_tags(tags);
+    if (RawObject::IsCanonical(tags)) {
+      str_obj.SetCanonical();
+    }
     str_obj.SetHash(hash);
     if (len > 0) {
       uint8_t* raw_ptr = CharAddr(str_obj, 0);
@@ -2097,7 +1991,9 @@
   if (kind == Snapshot::kFull) {
     RawTwoByteString* obj = reader->NewTwoByteString(len);
     str_obj = obj;
-    str_obj.set_tags(tags);
+    if (RawObject::IsCanonical(tags)) {
+      str_obj.SetCanonical();
+    }
     str_obj.SetHash(hash);
     NoSafepointScope no_safepoint;
     uint16_t* raw_ptr = (len > 0)? CharAddr(str_obj, 0) : NULL;
@@ -2286,7 +2182,11 @@
   }
   reader->ArrayReadFrom(object_id, *array, len, tags);
   if (RawObject::IsCanonical(tags)) {
-    *array ^= array->CheckAndCanonicalize(NULL);
+    if (kind == Snapshot::kFull) {
+      array->SetCanonical();
+    } else {
+      *array ^= array->CheckAndCanonicalize(NULL);
+    }
   }
   return raw(*array);
 }
@@ -2332,12 +2232,22 @@
     array = GrowableObjectArray::New(0, HEAP_SPACE(kind));
   }
   reader->AddBackRef(object_id, &array, kIsDeserialized);
-  intptr_t length = reader->ReadSmiValue();
-  array.SetLength(length);
+
+  // Read type arguments of growable array object.
+  const intptr_t typeargs_offset =
+      GrowableObjectArray::type_arguments_offset() / kWordSize;
+  *reader->TypeArgumentsHandle() ^=
+      reader->ReadObjectImpl(kAsInlinedObject, object_id, typeargs_offset);
+  array.StorePointer(&array.raw_ptr()->type_arguments_,
+                     reader->TypeArgumentsHandle()->raw());
+
+  // Read length of growable array object.
+  array.SetLength(reader->ReadSmiValue());
+
+  // Read the backing array of growable array object.
   *(reader->ArrayHandle()) ^= reader->ReadObjectImpl(kAsInlinedObject);
   array.SetData(*(reader->ArrayHandle()));
-  *(reader->TypeArgumentsHandle()) = reader->ArrayHandle()->GetTypeArguments();
-  array.SetTypeArguments(*(reader->TypeArgumentsHandle()));
+
   return array.raw();
 }
 
@@ -2354,6 +2264,9 @@
   writer->WriteIndexedObject(kGrowableObjectArrayCid);
   writer->WriteTags(writer->GetObjectTags(this));
 
+  // Write out the type arguments field.
+  writer->WriteObjectImpl(ptr()->type_arguments_, kAsInlinedObject);
+
   // Write out the used length field.
   writer->Write<RawObject*>(ptr()->length_);
 
@@ -2379,13 +2292,10 @@
     map = LinkedHashMap::NewUninitialized(HEAP_SPACE(kind));
   }
   reader->AddBackRef(object_id, &map, kIsDeserialized);
-  // Set the object tags.
-  map.set_tags(tags);
 
   // Read the type arguments.
-  intptr_t typeargs_offset =
-      reinterpret_cast<RawObject**>(&map.raw()->ptr()->type_arguments_) -
-      reinterpret_cast<RawObject**>(map.raw()->ptr());
+  const intptr_t typeargs_offset =
+      GrowableObjectArray::type_arguments_offset() / kWordSize;
   *reader->TypeArgumentsHandle() ^=
       reader->ReadObjectImpl(kAsInlinedObject, object_id, typeargs_offset);
   map.SetTypeArguments(*reader->TypeArgumentsHandle());
@@ -2493,8 +2403,6 @@
     simd = Float32x4::New(value0, value1, value2, value3, HEAP_SPACE(kind));
   }
   reader->AddBackRef(object_id, &simd, kIsDeserialized);
-  // Set the object tags.
-  simd.set_tags(tags);
   return simd.raw();
 }
 
@@ -2539,8 +2447,6 @@
     simd = Int32x4::New(value0, value1, value2, value3, HEAP_SPACE(kind));
   }
   reader->AddBackRef(object_id, &simd, kIsDeserialized);
-  // Set the object tags.
-  simd.set_tags(tags);
   return simd.raw();
 }
 
@@ -2583,8 +2489,6 @@
     simd = Float64x2::New(value0, value1, HEAP_SPACE(kind));
   }
   reader->AddBackRef(object_id, &simd, kIsDeserialized);
-  // Set the object tags.
-  simd.set_tags(tags);
   return simd.raw();
 }
 
@@ -2626,9 +2530,6 @@
                                 : TypedData::New(cid, len, HEAP_SPACE(kind)));
   reader->AddBackRef(object_id, &result, kIsDeserialized);
 
-  // Set the object tags.
-  result.set_tags(tags);
-
   // Setup the array elements.
   intptr_t element_size = ElementSizeInBytes(cid);
   intptr_t length_in_bytes = len * element_size;
@@ -2977,9 +2878,6 @@
       reader->zone(), JSRegExp::New(HEAP_SPACE(kind)));
   reader->AddBackRef(object_id, &regex, kIsDeserialized);
 
-  // Set the object tags.
-  regex.set_tags(tags);
-
   // Read and Set all the other fields.
   regex.StoreSmi(&regex.raw_ptr()->num_bracket_expressions_,
                  reader->ReadAsSmi());
@@ -3027,9 +2925,6 @@
       reader->zone(), WeakProperty::New(HEAP_SPACE(kind)));
   reader->AddBackRef(object_id, &weak_property, kIsDeserialized);
 
-  // Set the object tags.
-  weak_property.set_tags(tags);
-
   // Set all the object fields.
   // TODO(5411462): Need to assert No GC can happen here, even though
   // allocations may happen.
diff --git a/runtime/vm/service.cc b/runtime/vm/service.cc
index 6f4fd03..b40b477 100644
--- a/runtime/vm/service.cc
+++ b/runtime/vm/service.cc
@@ -2258,6 +2258,11 @@
 static bool Resume(Isolate* isolate, JSONStream* js) {
   const char* step_param = js->LookupParam("step");
   if (isolate->message_handler()->paused_on_start()) {
+    // If the user is issuing a 'Over' or an 'Out' step, that is the
+    // same as a regular resume request.
+    if ((step_param != NULL) && (strcmp(step_param, "Into") == 0)) {
+      isolate->debugger()->EnterSingleStepMode();
+    }
     isolate->message_handler()->set_pause_on_start(false);
     if (Service::debug_stream.enabled()) {
       ServiceEvent event(isolate, ServiceEvent::kResume);
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 98ae976..907e805 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -214,7 +214,9 @@
         (*backward_references_)[i].set_state(kIsDeserialized);
       }
     }
-    ProcessDeferredCanonicalizations();
+    if (kind() != Snapshot::kFull) {
+      ProcessDeferredCanonicalizations();
+    }
     return obj.raw();
   } else {
     // An error occurred while reading, return the error object.
@@ -323,6 +325,11 @@
 }
 
 
+bool SnapshotReader::is_vm_isolate() const {
+  return isolate_ == Dart::vm_isolate();
+}
+
+
 RawObject* SnapshotReader::ReadObjectImpl(bool as_reference,
                                           intptr_t patch_object_id,
                                           intptr_t patch_offset) {
@@ -461,9 +468,6 @@
 #undef SNAPSHOT_READ
     default: UNREACHABLE(); break;
   }
-  if (kind_ == Snapshot::kFull) {
-    pobj_.SetCreatedFromSnapshot();
-  }
   return pobj_.raw();
 }
 
@@ -534,10 +538,14 @@
         result->SetFieldAtOffset(offset, Object::null_object());
         offset += kWordSize;
       }
-      result->SetCreatedFromSnapshot();
-    } else if (RawObject::IsCanonical(tags)) {
-      *result = result->CheckAndCanonicalize(NULL);
-      ASSERT(!result->IsNull());
+    }
+    if (RawObject::IsCanonical(tags)) {
+      if (kind_ == Snapshot::kFull) {
+        result->SetCanonical();
+      } else {
+        *result = result->CheckAndCanonicalize(NULL);
+        ASSERT(!result->IsNull());
+      }
     }
     return result->raw();
   } else if (header_id == kStaticImplicitClosureObjectId) {
@@ -576,9 +584,6 @@
 #undef SNAPSHOT_READ
     default: UNREACHABLE(); break;
   }
-  if (kind_ == Snapshot::kFull) {
-    pobj_.SetCreatedFromSnapshot();
-  }
   AddPatchRecord(object_id, patch_object_id, patch_offset);
   return pobj_.raw();
 }
@@ -1069,6 +1074,7 @@
   ASSERT(class_id != kIllegalCid);
   tags = RawObject::ClassIdTag::update(class_id, tags);
   tags = RawObject::SizeTag::update(size, tags);
+  tags = RawObject::VMHeapObjectTag::update(is_vm_isolate(), tags);
   raw_obj->ptr()->tags_ = tags;
   return raw_obj;
 }
@@ -1155,28 +1161,36 @@
 
 
 void SnapshotReader::ProcessDeferredCanonicalizations() {
-  AbstractType& typeobj = AbstractType::Handle();
+  Type& typeobj = Type::Handle();
   TypeArguments& typeargs = TypeArguments::Handle();
   Object& newobj = Object::Handle();
   for (intptr_t i = 0; i < backward_references_->length(); i++) {
     BackRefNode& backref = (*backward_references_)[i];
     if (backref.defer_canonicalization()) {
       Object* objref = backref.reference();
+      bool needs_patching = false;
       // Object should either be an abstract type or a type argument.
-      if (objref->IsAbstractType()) {
+      if (objref->IsType()) {
         typeobj ^= objref->raw();
-        typeobj.ClearCanonical();
         newobj = typeobj.Canonicalize();
+        if ((newobj.raw() != typeobj.raw()) && !typeobj.IsRecursive()) {
+          needs_patching = true;
+        } else {
+          // Set Canonical bit.
+          objref->SetCanonical();
+        }
       } else {
         ASSERT(objref->IsTypeArguments());
         typeargs ^= objref->raw();
-        typeargs.ClearCanonical();
         newobj = typeargs.Canonicalize();
+        if ((newobj.raw() != typeargs.raw()) && !typeargs.IsRecursive()) {
+          needs_patching = true;
+        } else {
+          // Set Canonical bit.
+          objref->SetCanonical();
+        }
       }
-      if (newobj.raw() == objref->raw()) {
-        // Restore Canonical bit.
-        objref->SetCanonical();
-      } else {
+      if (needs_patching) {
         ZoneGrowableArray<intptr_t>* patches = backref.patch_records();
         ASSERT(newobj.IsCanonical());
         ASSERT(patches != NULL);
@@ -1199,13 +1213,9 @@
                                    const Array& result,
                                    intptr_t len,
                                    intptr_t tags) {
-  // Set the object tags.
-  result.set_tags(tags);
-
   // Setup the object fields.
   const intptr_t typeargs_offset =
-      reinterpret_cast<RawObject**>(&result.raw()->ptr()->type_arguments_) -
-      reinterpret_cast<RawObject**>(result.raw()->ptr());
+      GrowableObjectArray::type_arguments_offset() / kWordSize;
   *TypeArgumentsHandle() ^= ReadObjectImpl(kAsInlinedObject,
                                            object_id,
                                            typeargs_offset);
@@ -2044,11 +2054,6 @@
   ASSERT(kind_ != Snapshot::kFull);
   int class_id = cls->ptr()->id_;
   ASSERT(!IsSingletonClassId(class_id) && !IsObjectStoreClassId(class_id));
-  // TODO(5411462): Should restrict this to only core-lib classes in this
-  // case.
-  // Write out the class and tags information.
-  WriteVMIsolateObject(kClassCid);
-  WriteTags(GetObjectTags(cls));
 
   // Write out the library url and class name.
   RawLibrary* library = cls->ptr()->library_;
diff --git a/runtime/vm/snapshot.h b/runtime/vm/snapshot.h
index 161da59..4b3a278 100644
--- a/runtime/vm/snapshot.h
+++ b/runtime/vm/snapshot.h
@@ -450,6 +450,8 @@
 
   RawObject* VmIsolateSnapshotObject(intptr_t index) const;
 
+  bool is_vm_isolate() const;
+
   Snapshot::Kind kind_;  // Indicates type of snapshot(full, script, message).
   Isolate* isolate_;  // Current isolate.
   Zone* zone_;  // Zone for allocations while reading snapshot.
diff --git a/runtime/vm/snapshot_test.cc b/runtime/vm/snapshot_test.cc
index 2843561..0c4ccaa 100644
--- a/runtime/vm/snapshot_test.cc
+++ b/runtime/vm/snapshot_test.cc
@@ -1055,7 +1055,6 @@
 }
 
 
-#if 0
 UNIT_TEST_CASE(CanonicalizationInScriptSnapshots) {
   const char* kScriptChars =
       "\n"
@@ -1166,7 +1165,6 @@
   free(script_snapshot);
   free(full_snapshot);
 }
-#endif
 
 
 static void IterateScripts(const Library& lib) {
diff --git a/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart b/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart
index e5c8495..aa836ca 100644
--- a/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart
+++ b/sdk/lib/_internal/js_runtime/lib/js_mirrors.dart
@@ -443,6 +443,10 @@
         .invoke(#call, positionalArguments, namedArguments);
   }
 
+  delegate(Invocation invocation) {
+    throw new UnimplementedError();
+  }
+
   _loadField(String name) {
     // TODO(ahe): What about lazily initialized fields? See
     // [JsClassMirror.getField].
@@ -853,6 +857,10 @@
         null, setterSymbol(fieldName), [arg], null);
   }
 
+  delegate(Invocation invocation) {
+    throw new UnimplementedError();
+  }
+
   List<ClassMirror> get superinterfaces => [mixin];
 
   Map<Symbol, MethodMirror> get __constructors => _mixin.__constructors;
@@ -1512,6 +1520,10 @@
     return _class.invoke(memberName, positionalArguments, namedArguments);
   }
 
+  delegate(Invocation invocation) {
+    throw new UnimplementedError();
+  }
+
   bool get isOriginalDeclaration => false;
 
   ClassMirror get originalDeclaration => _class;
@@ -2028,6 +2040,10 @@
     return reflect(mirror._invoke(positionalArguments, namedArguments));
   }
 
+  delegate(Invocation invocation) {
+    throw new UnimplementedError();
+  }
+
   bool get isOriginalDeclaration => true;
 
   ClassMirror get originalDeclaration => this;
@@ -2609,6 +2625,7 @@
   InstanceMirror getField(Symbol fieldName) => throw new UnimplementedError();
   InstanceMirror setField(Symbol fieldName, Object value)
       => throw new UnimplementedError();
+  delegate(Invocation invocation) => throw new UnimplementedError();
   List<TypeVariableMirror> get typeVariables => throw new UnimplementedError();
   List<TypeMirror> get typeArguments => throw new UnimplementedError();
   TypeMirror get originalDeclaration => throw new UnimplementedError();
diff --git a/sdk/lib/core/list.dart b/sdk/lib/core/list.dart
index 9d9a093..8e837f8 100644
--- a/sdk/lib/core/list.dart
+++ b/sdk/lib/core/list.dart
@@ -186,7 +186,7 @@
    * Sorts this list according to the order specified by the [compare] function.
    *
    * The [compare] function must act as a [Comparator].
-
+   *
    *     List<String> numbers = ['one', 'two', 'three', 'four'];
    *     // Sort from shortest to longest.
    *     numbers.sort((x, y) => x.length.compareTo(y.length));
@@ -197,7 +197,7 @@
    *
    *     List<int> nums = [13, 2, -11];
    *     nums.sort();
-         nums.join(', '); // '-11, 2, 13'
+   *     nums.join(', '); // '-11, 2, 13'
    */
   void sort([int compare(E a, E b)]);
 
diff --git a/sdk/lib/mirrors/mirrors.dart b/sdk/lib/mirrors/mirrors.dart
index d0f8379..3480cde 100644
--- a/sdk/lib/mirrors/mirrors.dart
+++ b/sdk/lib/mirrors/mirrors.dart
@@ -446,6 +446,23 @@
    */
   /* TODO(turnidge): Handle ambiguous names.*/
   InstanceMirror setField(Symbol fieldName, Object value);
+
+  /**
+   * Perform [invocation] on [reflectee].
+   * Equivalent to
+   *
+   *     if (invocation.isGetter) {
+   *       return this.getField(invocation.memberName).reflectee;
+   *     } else if (invocation.isSetter) {
+   *       return this.setField(invocation.memberName,
+   *                            invocation.positionArguments[0]).reflectee;
+   *     } else {
+   *       return this.invoke(invocation.memberName,
+   *                          invocation.positionalArguments,
+   *                          invocation.namedArguments).reflectee;
+   *     }
+   */
+  delegate(Invocation invocation);
 }
 
 /**
@@ -499,23 +516,6 @@
    * by [other] are identical.
    */
   bool operator == (other);
-
-  /**
-   * Perform [invocation] on [reflectee].
-   * Equivalent to
-   *
-   *     if (invocation.isGetter) {
-   *       return this.getField(invocation.memberName).reflectee;
-   *     } else if (invocation.isSetter) {
-   *       return this.setField(invocation.memberName,
-   *                            invocation.positionArguments[0]).reflectee;
-   *     } else {
-   *       return this.invoke(invocation.memberName,
-   *                          invocation.positionalArguments,
-   *                          invocation.namedArguments).reflectee;
-   *     }
-   */
-  delegate(Invocation invocation);
 }
 
 /**
diff --git a/tests/isolate/isolate.status b/tests/isolate/isolate.status
index 41355d7..035100e 100644
--- a/tests/isolate/isolate.status
+++ b/tests/isolate/isolate.status
@@ -18,9 +18,6 @@
 ping_pause_test: Skip     # Resolve test issues
 kill3_test: Pass, Fail    # Bad test: expects total message order
 
-message3_test/constList_identical: RuntimeError # Issue 21816
-message3_test/constMap: RuntimeError  # Issue 21816
-message3_test/constInstance: RuntimeError # Issue 21816
 message3_test/byteBuffer: Crash # Issue 21818
 message3_test/int32x4: Crash # Issue 21818
 
diff --git a/tests/isolate/message3_test.dart b/tests/isolate/message3_test.dart
index c07a7e6..4267cee 100644
--- a/tests/isolate/message3_test.dart
+++ b/tests/isolate/message3_test.dart
@@ -394,7 +394,7 @@
     Expect.equals("field", f.field);
     Expect.isFalse(identical(nonConstF, f));
   });
-  checks.add((x) {  // g1.
+  checks.add((x) {  // g2.
     Expect.isTrue(x is G);
     Expect.isFalse(identical(g1, x));
     F f = x.field;
@@ -403,7 +403,7 @@
   });
   checks.add((x) {  // g3.
     Expect.isTrue(x is G);
-    Expect.identical(g1, x);  /// constInstance: continued
+    Expect.identical(g3, x);  /// constInstance: continued
     F f = x.field;
     Expect.equals("field", f.field);
     Expect.identical(constF, f);  /// constInstance: continued
diff --git a/tests/language/language.status b/tests/language/language.status
index 55e186d..ce9ff2e 100644
--- a/tests/language/language.status
+++ b/tests/language/language.status
@@ -44,61 +44,6 @@
 [ $compiler == none ]
 dynamic_prefix_core_test/01: RuntimeError # Issue 12478
 multiline_strings_test: Fail # Issue 23020
-conditional_method_invocation_test/12: Fail # Issue 23794
-conditional_method_invocation_test/13: Fail # Issue 23794
-conditional_method_invocation_test/14: Fail # Issue 23794
-conditional_method_invocation_test/15: Fail # Issue 23794
-conditional_method_invocation_test/16: Fail # Issue 23794
-conditional_method_invocation_test/17: Fail # Issue 23794
-conditional_method_invocation_test/18: Fail # Issue 23794
-conditional_method_invocation_test/19: Fail # Issue 23794
-conditional_property_access_test/10: Fail # Issue 23794
-conditional_property_access_test/11: Fail # Issue 23794
-conditional_property_access_test/12: Fail # Issue 23794
-conditional_property_access_test/13: Fail # Issue 23794
-conditional_property_access_test/14: Fail # Issue 23794
-conditional_property_access_test/15: Fail # Issue 23794
-conditional_property_access_test/16: Fail # Issue 23794
-conditional_property_access_test/17: Fail # Issue 23794
-conditional_property_assignment_test/23: Fail # Issue 23794
-conditional_property_assignment_test/24: Fail # Issue 23794
-conditional_property_assignment_test/25: Fail # Issue 23794
-conditional_property_assignment_test/26: Fail # Issue 23794
-conditional_property_assignment_test/27: Fail # Issue 23794
-conditional_property_assignment_test/28: Fail # Issue 23794
-conditional_property_assignment_test/29: Fail # Issue 23794
-conditional_property_assignment_test/30: Fail # Issue 23794
-conditional_property_assignment_test/31: Fail # Issue 23794
-conditional_property_assignment_test/32: Fail # Issue 23794
-conditional_property_assignment_test/33: Fail # Issue 23794
-conditional_property_assignment_test/34: Fail # Issue 23794
-conditional_property_assignment_test/35: Fail # Issue 23794
-conditional_property_increment_decrement_test/17: Fail # Issue 23794
-conditional_property_increment_decrement_test/18: Fail # Issue 23794
-conditional_property_increment_decrement_test/19: Fail # Issue 23794
-conditional_property_increment_decrement_test/20: Fail # Issue 23794
-conditional_property_increment_decrement_test/21: Fail # Issue 23794
-conditional_property_increment_decrement_test/22: Fail # Issue 23794
-conditional_property_increment_decrement_test/23: Fail # Issue 23794
-conditional_property_increment_decrement_test/24: Fail # Issue 23794
-conditional_property_increment_decrement_test/25: Fail # Issue 23794
-conditional_property_increment_decrement_test/26: Fail # Issue 23794
-conditional_property_increment_decrement_test/27: Fail # Issue 23794
-conditional_property_increment_decrement_test/28: Fail # Issue 23794
-conditional_property_increment_decrement_test/29: Fail # Issue 23794
-conditional_property_increment_decrement_test/30: Fail # Issue 23794
-conditional_property_increment_decrement_test/31: Fail # Issue 23794
-conditional_property_increment_decrement_test/32: Fail # Issue 23794
-conditional_property_increment_decrement_test/33: Fail # Issue 23794
-conditional_property_increment_decrement_test/34: Fail # Issue 23794
-conditional_property_increment_decrement_test/35: Fail # Issue 23794
-conditional_property_increment_decrement_test/36: Fail # Issue 23794
-conditional_property_increment_decrement_test/37: Fail # Issue 23794
-conditional_property_increment_decrement_test/38: Fail # Issue 23794
-conditional_property_increment_decrement_test/39: Fail # Issue 23794
-conditional_property_increment_decrement_test/40: Fail # Issue 23794
-if_null_assignment_behavior_test/31: Fail # Issue 23794
-if_null_assignment_behavior_test/32: Fail # Issue 23794
 
 [ $compiler == none && ($runtime == vm || $runtime == drt || $runtime == dartium || $runtime == ContentShellOnAndroid) ]
 dynamic_prefix_core_test/none: Fail # Issue 12478
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 69c9605..5e6a2b9 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -18,6 +18,8 @@
 mirrors/closurization_equivalence_test: RuntimeError # Issue 6490
 mirrors/constructor_kinds_test: RuntimeError # Issue 13799
 mirrors/constructor_private_name_test: CompileTimeError # Issue 13597
+mirrors/delegate_class_test: RuntimeError
+mirrors/delegate_library_test: RuntimeError
 mirrors/deferred_type_test: RuntimeError # Issue 6335
 mirrors/empty_test: RuntimeError # Issue 6490
 mirrors/enum_test: RuntimeError # Issue 6490
diff --git a/tests/lib/mirrors/delegate_class_test.dart b/tests/lib/mirrors/delegate_class_test.dart
new file mode 100644
index 0000000..652ee44
--- /dev/null
+++ b/tests/lib/mirrors/delegate_class_test.dart
@@ -0,0 +1,50 @@
+// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library test.delegate_class;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+class C {
+  static method(a, b, c) => "$a-$b-$c";
+  static methodWithNamed(a, {b:'B', c}) => "$a-$b-$c";
+  static methodWithOpt(a, [b, c='C']) => "$a-$b-$c";
+  static get getter => 'g';
+  static set setter(x) {
+    field = x*2;
+    return 'unobservable value';
+  }
+  static var field;
+}
+
+class Proxy {
+  var targetMirror;
+  Proxy(this.targetMirror);
+  noSuchMethod(invocation) => targetMirror.delegate(invocation);
+}
+
+main() {
+  var proxy = new Proxy(reflectClass(C));
+  var result;
+
+  Expect.equals('X-Y-Z', proxy.method('X', 'Y', 'Z'));
+
+  Expect.equals('X-B-null', proxy.methodWithNamed('X'));
+  Expect.equals('X-Y-null', proxy.methodWithNamed('X', b: 'Y'));
+  Expect.equals('X-Y-Z', proxy.methodWithNamed('X', b: 'Y', c: 'Z'));
+
+  Expect.equals('X-null-C', proxy.methodWithOpt('X'));
+  Expect.equals('X-Y-C', proxy.methodWithOpt('X', 'Y'));
+  Expect.equals('X-Y-Z', proxy.methodWithOpt('X', 'Y', 'Z'));
+
+  Expect.equals('g', proxy.getter);
+
+  Expect.equals(5, proxy.setter = 5);
+  Expect.equals(10, proxy.field);
+
+  Expect.equals(5, proxy.field = 5);
+  Expect.equals(5, proxy.field);
+}
diff --git a/tests/lib/mirrors/delegate_library_test.dart b/tests/lib/mirrors/delegate_library_test.dart
new file mode 100644
index 0000000..7eb5a84
--- /dev/null
+++ b/tests/lib/mirrors/delegate_library_test.dart
@@ -0,0 +1,49 @@
+// 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 test.delegate_library;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+method(a, b, c) => "$a-$b-$c";
+methodWithNamed(a, {b:'B', c}) => "$a-$b-$c";
+methodWithOpt(a, [b, c='C']) => "$a-$b-$c";
+get getter => 'g';
+set setter(x) {
+  field = x*2;
+  return 'unobservable value';
+}
+var field;
+
+
+class Proxy {
+  var targetMirror;
+  Proxy(this.targetMirror);
+  noSuchMethod(invocation) => targetMirror.delegate(invocation);
+}
+
+main() {
+  var proxy = new Proxy(reflectClass(Proxy).owner);
+  var result;
+
+  Expect.equals('X-Y-Z', proxy.method('X', 'Y', 'Z'));
+
+  Expect.equals('X-B-null', proxy.methodWithNamed('X'));
+  Expect.equals('X-Y-null', proxy.methodWithNamed('X', b: 'Y'));
+  Expect.equals('X-Y-Z', proxy.methodWithNamed('X', b: 'Y', c: 'Z'));
+
+  Expect.equals('X-null-C', proxy.methodWithOpt('X'));
+  Expect.equals('X-Y-C', proxy.methodWithOpt('X', 'Y'));
+  Expect.equals('X-Y-Z', proxy.methodWithOpt('X', 'Y', 'Z'));
+
+  Expect.equals('g', proxy.getter);
+
+  Expect.equals(5, proxy.setter = 5);
+  Expect.equals(10, proxy.field);
+
+  Expect.equals(5, proxy.field = 5);
+  Expect.equals(5, proxy.field);
+}
diff --git a/tools/VERSION b/tools/VERSION
index 6dd51cf..d45b9a4 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -28,4 +28,4 @@
 MINOR 12
 PATCH 0
 PRERELEASE 5
-PRERELEASE_PATCH 1
+PRERELEASE_PATCH 2