version 0.6.12.0 .
svn merge -r 25460:25521 https://dart.googlecode.com/svn/branches/bleeding_edge trunk

git-svn-id: http://dart.googlecode.com/svn/trunk@25524 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/pkg/docgen/lib/docgen.dart b/pkg/docgen/lib/docgen.dart
index 72f6a13..dc7069f 100644
--- a/pkg/docgen/lib/docgen.dart
+++ b/pkg/docgen/lib/docgen.dart
@@ -168,10 +168,6 @@
   return _analyzeLibraries(libraries, sdkRoot, packageRoot: packageRoot);
 }
 
-// TODO(janicejl): Should make docgen fail gracefully, or output a friendly
-// error message letting them know why it is failing to create a mirror system.
-// If there is conflicting library names, should modify it with a hash at the
-// end of it's library name.
 /**
  * Analyzes set of libraries and provides a mirror system which can be used
  * for static inspection of the source code.
@@ -308,7 +304,7 @@
     if (includePrivate || !mirror.isPrivate) {
       _currentMember = mirror;
       data[mirrorName] = new Variable(mirrorName, mirror.isFinal, 
-          mirror.isStatic, mirror.isConst, mirror.type.qualifiedName,
+          mirror.isStatic, mirror.isConst, _type(mirror.type),
           _getComment(mirror), _getAnnotations(mirror), mirror.qualifiedName);
     }
   });
@@ -330,7 +326,7 @@
   mirrorMap.forEach((String mirrorName, MethodMirror mirror) {
     if (includePrivate || !mirror.isPrivate) {
       var method = new Method(mirrorName, mirror.isStatic, mirror.isAbstract,
-          mirror.isConstConstructor, mirror.returnType.qualifiedName, 
+          mirror.isConstConstructor, _type(mirror.returnType), 
           _getComment(mirror), _getParameters(mirror.parameters), 
           _getAnnotations(mirror), mirror.qualifiedName);
       _currentMember = mirror;
@@ -414,7 +410,7 @@
     _currentMember = mirror;
     data[mirror.simpleName] = new Parameter(mirror.simpleName, 
         mirror.isOptional, mirror.isNamed, mirror.hasDefaultValue, 
-        mirror.type.qualifiedName, mirror.defaultValue, 
+        _type(mirror.type), mirror.defaultValue, 
         _getAnnotations(mirror));
   });
   return data;
@@ -430,6 +426,28 @@
 }
 
 /**
+ * Returns a single [Type] object constructed from the Method.returnType 
+ * Type mirror. 
+ */
+Type _type(TypeMirror mirror) {
+  return new Type(mirror.qualifiedName, _typeGenerics(mirror));
+}
+
+/**
+ * Returns a list of [Type] objects constructed from TypeMirrors. 
+ */
+List<Type> _typeGenerics(TypeMirror mirror) {
+  if (mirror is ClassMirror && !mirror.isTypedef) {
+    var innerList = [];
+    mirror.typeArguments.forEach((e) {
+      innerList.add(new Type(e.qualifiedName, _typeGenerics(e)));
+    });
+    return innerList;
+  }
+  return [];
+}
+
+/**
  * Writes text to a file in the 'docs' directory.
  */
 void _writeToFile(String text, String filename) {
@@ -512,7 +530,6 @@
 /**
  * A class containing contents of a Dart class.
  */
-// TODO(tmandel): Figure out how to do typedefs (what is needed)
 class Class extends Indexable {
 
   /// List of the names of interfaces that this class implements.
@@ -584,7 +601,7 @@
   bool isFinal;
   bool isStatic;
   bool isConst;
-  String type;
+  Type type;
 
   /// List of the meta annotations on the variable.
   List<String> annotations;
@@ -601,7 +618,7 @@
       'final': isFinal.toString(),
       'static': isStatic.toString(),
       'constant': isConst.toString(),
-      'type': type,
+      'type': new List.filled(1, type.toMap()),
       'annotations': new List.from(annotations)
     };
 }
@@ -617,13 +634,13 @@
   bool isStatic;
   bool isAbstract;
   bool isConst;
-  String returnType;
-
+  Type returnType;
+  
   /// List of the meta annotations on the method.
   List<String> annotations;
   
   Method(String name, this.isStatic, this.isAbstract, this.isConst, 
-      this.returnType, String comment, this.parameters, this.annotations, 
+      this.returnType, String comment, this.parameters, this.annotations,
       String qualifiedName) 
       : super(name, comment, qualifiedName);
   
@@ -635,7 +652,7 @@
       'static': isStatic.toString(),
       'abstract': isAbstract.toString(),
       'constant': isConst.toString(),
-      'return': returnType,
+      'return': new List.filled(1, returnType.toMap()),
       'parameters': recurseMap(parameters),
       'annotations': new List.from(annotations)
     };
@@ -650,7 +667,7 @@
   bool isOptional;
   bool isNamed;
   bool hasDefaultValue;
-  String type;
+  Type type;
   String defaultValue;
 
   /// List of the meta annotations on the parameter.
@@ -665,7 +682,7 @@
       'optional': isOptional.toString(),
       'named': isNamed.toString(),
       'default': hasDefaultValue.toString(),
-      'type': type,
+      'type': new List.filled(1, type.toMap()),
       'value': defaultValue,
       'annotations': new List.from(annotations)
     };
@@ -684,4 +701,46 @@
       'name': name,
       'type': type
     };
+}
+
+/**
+ * Holds the name of a return type, and its generic type parameters.
+ * 
+ * Return types are of a form [outer]<[inner]>.
+ * If there is no [inner] part, [inner] will be an empty list. 
+ * 
+ * For example:
+ *        int size()  
+ *          "return" : 
+ *            - "outer" : "dart.core.int"
+ *              "inner" :
+ * 
+ *        List<String> toList()
+ *          "return" :
+ *            - "outer" : "dart.core.List"
+ *              "inner" : 
+ *                - "outer" : "dart.core.String"
+ *                  "inner" :
+ *        
+ *        Map<String, List<int>>
+ *          "return" : 
+ *            - "outer" : "dart.core.Map"
+ *              "inner" :
+ *                - "outer" : "dart.core.String"
+ *                  "inner" :
+ *                - "outer" : "dart.core.List"
+ *                  "inner" :
+ *                    - "outer" : "dart.core.int"
+ *                      "inner" :
+ */
+class Type {
+  String outer;
+  List<Type> inner;
+  
+  Type(this.outer, this.inner);
+  
+  Map toMap() => {
+      'outer': outer,
+      'inner': new List.from(inner.map((e) => e.toMap()))
+    };
 }
\ No newline at end of file
diff --git a/pkg/html_import/README.md b/pkg/html_import/README.md
new file mode 100644
index 0000000..c394a6e
--- /dev/null
+++ b/pkg/html_import/README.md
@@ -0,0 +1,43 @@
+# HTML Imports polyfill
+
+[HTML Imports][1] are a way to include and reuse HTML documents in other HTML
+documents. As `<script>` tags let authors include external Javascript in their
+pages, imports let authors load full HTML resources.  In particular, imports let
+authors include [Custom Element](https://github.com/Polymer/CustomElements)
+definitions from external URLs.
+
+
+## Getting Started
+
+Include the `html_import.debug.js` or `html_import.min.js` (minified) file in
+your project.
+
+    <script src="packages/html_import/html_import.debug.js"></script>
+
+`html_import.debug.js` is the debug loader and uses `document.write` to load
+additional modules.
+
+Use the minified version (`html_import.min.js`) if you need to load the file
+dynamically.
+
+## Basic usage
+
+For HTML imports use the `import` relation on a standard `<link>` tag, for
+example:
+
+    <link rel="import" href="import-file.html">
+
+## Polyfill details
+
+You can read more about how the polyfill is implemented in JavaScript here:
+<https://github.com/Polymer/HTMLImports/tree/master#polyfill-details>
+
+## Getting the source code
+
+This package is built from:
+<https://github.com/Polymer/HTMLImports/tree/master>
+
+You'll need [node.js](http://nodejs.org) to rebuild the JS file. Use
+`npm install` to get dependencies and `grunt` to build.
+
+[1]: https://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/imports/index.html
diff --git a/pkg/html_import/lib/html_import.debug.js b/pkg/html_import/lib/html_import.debug.js
new file mode 100644
index 0000000..08f6438
--- /dev/null
+++ b/pkg/html_import/lib/html_import.debug.js
@@ -0,0 +1,36 @@
+/* 
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+(function() {
+  
+var thisFile = 'html_import.debug.js';
+var scopeName = 'HTMLImports';
+var modules = [
+    'src/Parser.js',
+    'src/HTMLImports.js',
+    'src/boot.js'
+];
+
+// export 
+
+window[scopeName] = {
+  entryPointName: thisFile,
+  modules: modules
+};
+
+// bootstrap
+
+var script = document.querySelector('script[src*="' + thisFile + '"]');
+var src = script.attributes.src.value;
+var basePath = src.slice(0, src.indexOf(thisFile));
+
+if (!window.Loader) {
+  var path = basePath + 'tools/loader/loader.js';
+  document.write('<script src="' + path + '"></script>');
+} 
+document.write('<script>Loader.load("' + scopeName + '")</script>');
+  
+})();
diff --git a/pkg/html_import/lib/html_import.min.js b/pkg/html_import/lib/html_import.min.js
new file mode 100644
index 0000000..cd012d0
--- /dev/null
+++ b/pkg/html_import/lib/html_import.min.js
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 The Polymer Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//    * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//    * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//    * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+!function(){function a(){HTMLImports.importer.load(document,function(){HTMLImports.parser.parse(document),HTMLImports.readyTime=(new Date).getTime(),document.dispatchEvent(new CustomEvent("HTMLImportsLoaded",{bubbles:!0}))})}"function"!=typeof window.CustomEvent&&(window.CustomEvent=function(a){var b=document.createEvent("HTMLEvents");return b.initEvent(a,!0,!0),b}),"complete"===document.readyState?a():window.addEventListener("DOMContentLoaded",a)}(),function(a){function b(a){return d(a,i)}function c(a){return d(a,j)}function d(a,b){return"link"===a.localName&&a.getAttribute("rel")===b}function e(a){return"script"===a.localName}function f(a,b){var c=a;c instanceof Document||(c=document.implementation.createHTMLDocument(i),c.body.innerHTML=a),c._URL=b;var d=c.createElement("base");return d.setAttribute("href",document.baseURI),c.head.appendChild(d),window.HTMLTemplateElement&&HTMLTemplateElement.bootstrap&&HTMLTemplateElement.bootstrap(c),c}a||(a=window.HTMLImports={flags:{}});var g,h=a.xhr,i="import",j="stylesheet",k={documents:{},cache:{},preloadSelectors:["link[rel="+i+"]","element link[rel="+j+"]","template","script[src]:not([type])",'script[src][type="text/javascript"]'].join(","),loader:function(a){return g=new l(k.loaded,a),g.cache=k.cache,g},load:function(a,b){g=k.loader(b),k.preload(a)},preload:function(a){var b=a.querySelectorAll(k.preloadSelectors);b=this.filterMainDocumentNodes(a,b),b=this.extractTemplateNodes(b),g.addNodes(b)},filterMainDocumentNodes:function(a,b){return a===document&&(b=Array.prototype.filter.call(b,function(a){return!e(a)})),b},extractTemplateNodes:function(a){var b=[];return a=Array.prototype.filter.call(a,function(a){if("template"===a.localName){if(a.content){var c=a.content.querySelectorAll("link[rel="+j+"]");c.length&&(b=b.concat(Array.prototype.slice.call(c,0)))}return!1}return!0}),b.length&&(a=a.concat(b)),a},loaded:function(a,d,e){if(b(d)){var g=k.documents[a];g||(g=f(e,a),p.resolvePathsInHTML(g),k.documents[a]=g,k.preload(g)),d.import={href:a,ownerNode:d,content:g},d.content=e=g}d.__resource=e,c(d)&&p.resolvePathsInStylesheet(d)}},l=function(a,b){this.onload=a,this.oncomplete=b,this.inflight=0,this.pending={},this.cache={}};l.prototype={addNodes:function(a){this.inflight+=a.length,q(a,this.require,this),this.checkDone()},require:function(a){var b=p.nodeUrl(a);a.__nodeUrl=b,this.dedupe(b,a)||this.fetch(b,a)},dedupe:function(a,b){return this.pending[a]?(this.pending[a].push(b),!0):this.cache[a]?(this.onload(a,b,g.cache[a]),this.tail(),!0):(this.pending[a]=[b],!1)},fetch:function(a,b){var c=function(c,d){this.receive(a,b,c,d)}.bind(this);h.load(a,c)},receive:function(a,b,c,d){c||(g.cache[a]=d),g.pending[a].forEach(function(b){c||this.onload(a,b,d),this.tail()},this),g.pending[a]=null},tail:function(){--this.inflight,this.checkDone()},checkDone:function(){this.inflight||this.oncomplete()}};var m=["href","src","action"],n="["+m.join("],[")+"]",o="{{.*}}",p={nodeUrl:function(a){return p.resolveUrl(p.getDocumentUrl(document),p.hrefOrSrc(a))},hrefOrSrc:function(a){return a.getAttribute("href")||a.getAttribute("src")},documentUrlFromNode:function(a){return p.getDocumentUrl(a.ownerDocument||a)},getDocumentUrl:function(a){var b=a&&(a._URL||a.impl&&a.impl._URL||a.baseURI||a.URL)||"";return b.split("#")[0]},resolveUrl:function(a,b,c){if(this.isAbsUrl(b))return b;var d=this.compressUrl(this.urlToPath(a)+b);return c&&(d=p.makeRelPath(p.getDocumentUrl(document),d)),d},isAbsUrl:function(a){return/(^data:)|(^http[s]?:)|(^\/)/.test(a)},urlToPath:function(a){var b=a.split("/");return b.pop(),b.push(""),b.join("/")},compressUrl:function(a){for(var b,c=a.split("/"),d=0;d<c.length;d++)b=c[d],".."===b&&(c.splice(d-1,2),d-=2);return c.join("/")},makeRelPath:function(a,b){var c,d;for(c=this.compressUrl(a).split("/"),d=this.compressUrl(b).split("/");c.length&&c[0]===d[0];)c.shift(),d.shift();for(var e=0,f=c.length-1;f>e;e++)d.unshift("..");var g=d.join("/");return g},resolvePathsInHTML:function(a,b){b=b||p.documentUrlFromNode(a),p.resolveAttributes(a,b),p.resolveStyleElts(a,b);var c=a.querySelectorAll("template");c&&q(c,function(a){a.content&&p.resolvePathsInHTML(a.content,b)})},resolvePathsInStylesheet:function(a){var b=p.nodeUrl(a);a.__resource=p.resolveCssText(a.__resource,b)},resolveStyleElts:function(a,b){var c=a.querySelectorAll("style");c&&q(c,function(a){a.textContent=p.resolveCssText(a.textContent,b)})},resolveCssText:function(a,b){return a.replace(/url\([^)]*\)/g,function(a){var c=a.replace(/["']/g,"").slice(4,-1);return c=p.resolveUrl(b,c,!0),"url("+c+")"})},resolveAttributes:function(a,b){var c=a&&a.querySelectorAll(n);c&&q(c,function(a){this.resolveNodeAttributes(a,b)},this)},resolveNodeAttributes:function(a,b){m.forEach(function(c){var d=a.attributes[c];if(d&&d.value&&d.value.search(o)<0){var e=p.resolveUrl(b,d.value,!0);d.value=e}})}};h=h||{async:!0,ok:function(a){return a.status>=200&&a.status<300||304===a.status||0===a.status},load:function(b,c,d){var e=new XMLHttpRequest;return(a.flags.debug||a.flags.bust)&&(b+="?"+Math.random()),e.open("GET",b,h.async),e.addEventListener("readystatechange",function(){4===e.readyState&&c.call(d,!h.ok(e)&&e,e.response,b)}),e.send(),e},loadDocument:function(a,b,c){this.load(a,b,c).responseType="document"}};var q=Array.prototype.forEach.call.bind(Array.prototype.forEach);a.path=p,a.xhr=h,a.importer=k,a.getDocumentUrl=p.getDocumentUrl,a.IMPORT_LINK_TYPE=i}(window.HTMLImports),function(a){function b(a){return"link"===a.localName&&a.getAttribute("rel")===f}function c(a){return a.parentNode&&!d(a)&&!e(a)}function d(a){return a.ownerDocument===document||a.ownerDocument.impl===document}function e(a){return a.parentNode&&"element"===a.parentNode.localName}var f="import",g={selectors:["link[rel="+f+"]","link[rel=stylesheet]","style","script:not([type])",'script[type="text/javascript"]'],map:{link:"parseLink",script:"parseScript",style:"parseGeneric"},parse:function(a){if(!a.__importParsed){a.__importParsed=!0;var b=a.querySelectorAll(g.selectors);h(b,function(a){g[g.map[a.localName]](a)})}},parseLink:function(a){b(a)?a.content&&g.parse(a.content):this.parseGeneric(a)},parseGeneric:function(a){c(a)&&document.head.appendChild(a)},parseScript:function(b){if(c(b)){var d=(b.__resource||b.textContent).trim();if(d){var e=b.__nodeUrl;if(!e){var e=a.path.documentUrlFromNode(b),f="["+Math.floor(1e3*(Math.random()+1))+"]",g=d.match(/Polymer\(['"]([^'"]*)/);f=g&&g[1]||f,e+="/"+f+".js"}d+="\n//# sourceURL="+e+"\n",eval.call(window,d)}}}},h=Array.prototype.forEach.call.bind(Array.prototype.forEach);a.parser=g}(HTMLImports);
diff --git a/pkg/html_import/lib/src/HTMLImports.js b/pkg/html_import/lib/src/HTMLImports.js
new file mode 100644
index 0000000..a77e7165
--- /dev/null
+++ b/pkg/html_import/lib/src/HTMLImports.js
@@ -0,0 +1,423 @@
+/*
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+(function(scope) {
+
+if (!scope) {
+  scope = window.HTMLImports = {flags:{}};
+}
+
+// imports
+
+var xhr = scope.xhr;
+
+// importer
+
+var IMPORT_LINK_TYPE = 'import';
+var STYLE_LINK_TYPE = 'stylesheet';
+
+// highlander object represents a primary document (the argument to 'load')
+// at the root of a tree of documents
+
+// for any document, importer:
+// - loads any linked documents (with deduping), modifies paths and feeds them back into importer
+// - loads text of external script tags
+// - loads text of external style tags inside of <element>, modifies paths
+
+// when importer 'modifies paths' in a document, this includes
+// - href/src/action in node attributes
+// - paths in inline stylesheets
+// - all content inside templates
+
+// linked style sheets in an import have their own path fixed up when their containing import modifies paths
+// linked style sheets in an <element> are loaded, and the content gets path fixups
+// inline style sheets get path fixups when their containing import modifies paths
+
+var loader;
+
+var importer = {
+  documents: {},
+  cache: {},
+  preloadSelectors: [
+    'link[rel=' + IMPORT_LINK_TYPE + ']',
+    'element link[rel=' + STYLE_LINK_TYPE + ']',
+    'template',
+    'script[src]:not([type])',
+    'script[src][type="text/javascript"]'
+  ].join(','),
+  loader: function(inNext) {
+    // construct a loader instance
+    loader = new Loader(importer.loaded, inNext);
+    // alias the loader cache (for debugging)
+    loader.cache = importer.cache;
+    return loader;
+  },
+  load: function(inDocument, inNext) {
+    // construct a loader instance
+    loader = importer.loader(inNext);
+    // add nodes from document into loader queue
+    importer.preload(inDocument);
+  },
+  preload: function(inDocument) {
+    // all preloadable nodes in inDocument
+    var nodes = inDocument.querySelectorAll(importer.preloadSelectors);
+    // from the main document, only load imports
+    // TODO(sjmiles): do this by altering the selector list instead
+    nodes = this.filterMainDocumentNodes(inDocument, nodes);
+    // extra link nodes from templates, filter templates out of the nodes list
+    nodes = this.extractTemplateNodes(nodes);
+    // add these nodes to loader's queue
+    loader.addNodes(nodes);
+  },
+  filterMainDocumentNodes: function(inDocument, nodes) {
+    if (inDocument === document) {
+      nodes = Array.prototype.filter.call(nodes, function(n) {
+        return !isScript(n);
+      });
+    }
+    return nodes;
+  },
+  extractTemplateNodes: function(nodes) {
+    var extra = [];
+    nodes = Array.prototype.filter.call(nodes, function(n) {
+      if (n.localName === 'template') {
+        if (n.content) {
+          var l$ = n.content.querySelectorAll('link[rel=' + STYLE_LINK_TYPE +
+            ']');
+          if (l$.length) {
+            extra = extra.concat(Array.prototype.slice.call(l$, 0));
+          }
+        }
+        return false;
+      }
+      return true;
+    });
+    if (extra.length) {
+      nodes = nodes.concat(extra);
+    }
+    return nodes;
+  },
+  loaded: function(url, elt, resource) {
+    if (isDocumentLink(elt)) {
+      var document = importer.documents[url];
+      // if we've never seen a document at this url
+      if (!document) {
+        // generate an HTMLDocument from data
+        document = makeDocument(resource, url);
+        // resolve resource paths relative to host document
+        path.resolvePathsInHTML(document);
+        // cache document
+        importer.documents[url] = document;
+        // add nodes from this document to the loader queue
+        importer.preload(document);
+      }
+      // store import record
+      elt.import = {
+        href: url,
+        ownerNode: elt,
+        content: document
+      };
+      // store document resource
+      elt.content = resource = document;
+    }
+    // store generic resource
+    // TODO(sorvell): fails for nodes inside <template>.content
+    // see https://code.google.com/p/chromium/issues/detail?id=249381.
+    elt.__resource = resource;
+    // css path fixups
+    if (isStylesheetLink(elt)) {
+      path.resolvePathsInStylesheet(elt);
+    }
+  }
+};
+
+function isDocumentLink(elt) {
+  return isLinkRel(elt, IMPORT_LINK_TYPE);
+}
+
+function isStylesheetLink(elt) {
+  return isLinkRel(elt, STYLE_LINK_TYPE);
+}
+
+function isLinkRel(elt, rel) {
+  return elt.localName === 'link' && elt.getAttribute('rel') === rel;
+}
+
+function isScript(elt) {
+  return elt.localName === 'script';
+}
+
+function makeDocument(resource, url) {
+  // create a new HTML document
+  var doc = resource;
+  if (!(doc instanceof Document)) {
+    doc = document.implementation.createHTMLDocument(IMPORT_LINK_TYPE);
+    // install html
+    doc.body.innerHTML = resource;
+  }
+  // cache the new document's source url
+  doc._URL = url;
+  // establish a relative path via <base>
+  var base = doc.createElement('base');
+  base.setAttribute('href', document.baseURI);
+  doc.head.appendChild(base);
+  // TODO(sorvell): MDV Polyfill intrusion: boostrap template polyfill
+  if (window.HTMLTemplateElement && HTMLTemplateElement.bootstrap) {
+    HTMLTemplateElement.bootstrap(doc);
+  }
+  return doc;
+}
+
+var Loader = function(inOnLoad, inOnComplete) {
+  this.onload = inOnLoad;
+  this.oncomplete = inOnComplete;
+  this.inflight = 0;
+  this.pending = {};
+  this.cache = {};
+};
+
+Loader.prototype = {
+  addNodes: function(inNodes) {
+    // number of transactions to complete
+    this.inflight += inNodes.length;
+    // commence transactions
+    forEach(inNodes, this.require, this);
+    // anything to do?
+    this.checkDone();
+  },
+  require: function(inElt) {
+    var url = path.nodeUrl(inElt);
+    // TODO(sjmiles): ad-hoc
+    inElt.__nodeUrl = url;
+    // deduplication
+    if (!this.dedupe(url, inElt)) {
+      // fetch this resource
+      this.fetch(url, inElt);
+    }
+  },
+  dedupe: function(inUrl, inElt) {
+    if (this.pending[inUrl]) {
+      // add to list of nodes waiting for inUrl
+      this.pending[inUrl].push(inElt);
+      // don't need fetch
+      return true;
+    }
+    if (this.cache[inUrl]) {
+      // complete load using cache data
+      this.onload(inUrl, inElt, loader.cache[inUrl]);
+      // finished this transaction
+      this.tail();
+      // don't need fetch
+      return true;
+    }
+    // first node waiting for inUrl
+    this.pending[inUrl] = [inElt];
+    // need fetch (not a dupe)
+    return false;
+  },
+  fetch: function(url, elt) {
+    var receiveXhr = function(err, resource) {
+      this.receive(url, elt, err, resource);
+    }.bind(this);
+    xhr.load(url, receiveXhr);
+    // TODO(sorvell): blocked on 
+    // https://code.google.com/p/chromium/issues/detail?id=257221
+    // xhr'ing for a document makes scripts in imports runnable; otherwise
+    // they are not; however, it requires that we have doctype=html in
+    // the import which is unacceptable. This is only needed on Chrome
+    // to avoid the bug above.
+    /*
+    if (isDocumentLink(elt)) {
+      xhr.loadDocument(url, receiveXhr);
+    } else {
+      xhr.load(url, receiveXhr);
+    }
+    */
+  },
+  receive: function(inUrl, inElt, inErr, inResource) {
+    if (!inErr) {
+      loader.cache[inUrl] = inResource;
+    }
+    loader.pending[inUrl].forEach(function(e) {
+      if (!inErr) {
+        this.onload(inUrl, e, inResource);
+      }
+      this.tail();
+    }, this);
+    loader.pending[inUrl] = null;
+  },
+  tail: function() {
+    --this.inflight;
+    this.checkDone();
+  },
+  checkDone: function() {
+    if (!this.inflight) {
+      this.oncomplete();
+    }
+  }
+};
+
+var URL_ATTRS = ['href', 'src', 'action'];
+var URL_ATTRS_SELECTOR = '[' + URL_ATTRS.join('],[') + ']';
+var URL_TEMPLATE_SEARCH = '{{.*}}';
+
+var path = {
+  nodeUrl: function(inNode) {
+    return path.resolveUrl(path.getDocumentUrl(document), path.hrefOrSrc(inNode));
+  },
+  hrefOrSrc: function(inNode) {
+    return inNode.getAttribute("href") || inNode.getAttribute("src");
+  },
+  documentUrlFromNode: function(inNode) {
+    return path.getDocumentUrl(inNode.ownerDocument || inNode);
+  },
+  getDocumentUrl: function(inDocument) {
+    var url = inDocument &&
+        // TODO(sjmiles): ShadowDOMPolyfill intrusion
+        (inDocument._URL || (inDocument.impl && inDocument.impl._URL)
+            || inDocument.baseURI || inDocument.URL)
+                || '';
+    // take only the left side if there is a #
+    return url.split('#')[0];
+  },
+  resolveUrl: function(inBaseUrl, inUrl, inRelativeToDocument) {
+    if (this.isAbsUrl(inUrl)) {
+      return inUrl;
+    }
+    var url = this.compressUrl(this.urlToPath(inBaseUrl) + inUrl);
+    if (inRelativeToDocument) {
+      url = path.makeRelPath(path.getDocumentUrl(document), url);
+    }
+    return url;
+  },
+  isAbsUrl: function(inUrl) {
+    return /(^data:)|(^http[s]?:)|(^\/)/.test(inUrl);
+  },
+  urlToPath: function(inBaseUrl) {
+    var parts = inBaseUrl.split("/");
+    parts.pop();
+    parts.push('');
+    return parts.join("/");
+  },
+  compressUrl: function(inUrl) {
+    var parts = inUrl.split("/");
+    for (var i=0, p; i<parts.length; i++) {
+      p = parts[i];
+      if (p === "..") {
+        parts.splice(i-1, 2);
+        i -= 2;
+      }
+    }
+    return parts.join("/");
+  },
+  // make a relative path from source to target
+  makeRelPath: function(inSource, inTarget) {
+    var s, t;
+    s = this.compressUrl(inSource).split("/");
+    t = this.compressUrl(inTarget).split("/");
+    while (s.length && s[0] === t[0]){
+      s.shift();
+      t.shift();
+    }
+    for(var i = 0, l = s.length-1; i < l; i++) {
+      t.unshift("..");
+    }
+    var r = t.join("/");
+    return r;
+  },
+  resolvePathsInHTML: function(root, url) {
+    url = url || path.documentUrlFromNode(root)
+    path.resolveAttributes(root, url);
+    path.resolveStyleElts(root, url);
+    // handle template.content
+    var templates = root.querySelectorAll('template');
+    if (templates) {
+      forEach(templates, function(t) {
+        if (t.content) {
+          path.resolvePathsInHTML(t.content, url);
+        }
+      });
+    }
+  },
+  resolvePathsInStylesheet: function(inSheet) {
+    var docUrl = path.nodeUrl(inSheet);
+    inSheet.__resource = path.resolveCssText(inSheet.__resource, docUrl);
+  },
+  resolveStyleElts: function(inRoot, inUrl) {
+    var styles = inRoot.querySelectorAll('style');
+    if (styles) {
+      forEach(styles, function(style) {
+        style.textContent = path.resolveCssText(style.textContent, inUrl);
+      });
+    }
+  },
+  resolveCssText: function(inCssText, inBaseUrl) {
+    return inCssText.replace(/url\([^)]*\)/g, function(inMatch) {
+      // find the url path, ignore quotes in url string
+      var urlPath = inMatch.replace(/["']/g, "").slice(4, -1);
+      urlPath = path.resolveUrl(inBaseUrl, urlPath, true);
+      return "url(" + urlPath + ")";
+    });
+  },
+  resolveAttributes: function(inRoot, inUrl) {
+    // search for attributes that host urls
+    var nodes = inRoot && inRoot.querySelectorAll(URL_ATTRS_SELECTOR);
+    if (nodes) {
+      forEach(nodes, function(n) {
+        this.resolveNodeAttributes(n, inUrl);
+      }, this);
+    }
+  },
+  resolveNodeAttributes: function(inNode, inUrl) {
+    URL_ATTRS.forEach(function(v) {
+      var attr = inNode.attributes[v];
+      if (attr && attr.value &&
+         (attr.value.search(URL_TEMPLATE_SEARCH) < 0)) {
+        var urlPath = path.resolveUrl(inUrl, attr.value, true);
+        attr.value = urlPath;
+      }
+    });
+  }
+};
+
+xhr = xhr || {
+  async: true,
+  ok: function(inRequest) {
+    return (inRequest.status >= 200 && inRequest.status < 300)
+        || (inRequest.status === 304)
+        || (inRequest.status === 0);
+  },
+  load: function(url, next, nextContext) {
+    var request = new XMLHttpRequest();
+    if (scope.flags.debug || scope.flags.bust) {
+      url += '?' + Math.random();
+    }
+    request.open('GET', url, xhr.async);
+    request.addEventListener('readystatechange', function(e) {
+      if (request.readyState === 4) {
+        next.call(nextContext, !xhr.ok(request) && request,
+          request.response, url);
+      }
+    });
+    request.send();
+    return request;
+  },
+  loadDocument: function(url, next, nextContext) {
+    this.load(url, next, nextContext).responseType = 'document';
+  }
+};
+
+var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+
+// exports
+
+scope.path = path;
+scope.xhr = xhr;
+scope.importer = importer;
+scope.getDocumentUrl = path.getDocumentUrl;
+scope.IMPORT_LINK_TYPE = IMPORT_LINK_TYPE;
+
+})(window.HTMLImports);
diff --git a/pkg/html_import/lib/src/Parser.js b/pkg/html_import/lib/src/Parser.js
new file mode 100644
index 0000000..2f00e05
--- /dev/null
+++ b/pkg/html_import/lib/src/Parser.js
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+
+(function(scope) {
+
+var IMPORT_LINK_TYPE = 'import';
+
+// highlander object for parsing a document tree
+
+var importParser = {
+  selectors: [
+    'link[rel=' + IMPORT_LINK_TYPE + ']',
+    'link[rel=stylesheet]',
+    'style',
+    'script:not([type])',
+    'script[type="text/javascript"]'
+  ],
+  map: {
+    link: 'parseLink',
+    script: 'parseScript',
+    style: 'parseGeneric'
+  },
+  parse: function(inDocument) {
+    if (!inDocument.__importParsed) {
+      // only parse once
+      inDocument.__importParsed = true;
+      // all parsable elements in inDocument (depth-first pre-order traversal)
+      var elts = inDocument.querySelectorAll(importParser.selectors);
+      // for each parsable node type, call the mapped parsing method
+      forEach(elts, function(e) {
+        importParser[importParser.map[e.localName]](e);
+      });
+    }
+  },
+  parseLink: function(linkElt) {
+    if (isDocumentLink(linkElt)) {
+      if (linkElt.content) {
+        importParser.parse(linkElt.content);
+      }
+    } else {
+      this.parseGeneric(linkElt);
+    }
+  },
+  parseGeneric: function(elt) {
+    if (needsMainDocumentContext(elt)) {
+      document.head.appendChild(elt);
+    }
+  },
+  parseScript: function(scriptElt) {
+    if (needsMainDocumentContext(scriptElt)) {
+      // acquire code to execute
+      var code = (scriptElt.__resource || scriptElt.textContent).trim();
+      if (code) {
+        // calculate source map hint
+        var moniker = scriptElt.__nodeUrl;
+        if (!moniker) {
+          var moniker = scope.path.documentUrlFromNode(scriptElt);
+          // there could be more than one script this url
+          var tag = '[' + Math.floor((Math.random()+1)*1000) + ']';
+          // TODO(sjmiles): Polymer hack, should be pluggable if we need to allow 
+          // this sort of thing
+          var matches = code.match(/Polymer\(['"]([^'"]*)/);
+          tag = matches && matches[1] || tag;
+          // tag the moniker
+          moniker += '/' + tag + '.js';
+        }
+        // source map hint
+        code += "\n//# sourceURL=" + moniker + "\n";
+        // evaluate the code
+        eval.call(window, code);
+      }
+    }
+  }
+};
+
+var forEach = Array.prototype.forEach.call.bind(Array.prototype.forEach);
+
+function isDocumentLink(elt) {
+  return elt.localName === 'link'
+      && elt.getAttribute('rel') === IMPORT_LINK_TYPE;
+}
+
+function needsMainDocumentContext(node) {
+  // nodes can be moved to the main document:
+  // if they are in a tree but not in the main document and not children of <element>
+  return node.parentNode && !inMainDocument(node) 
+      && !isElementElementChild(node);
+}
+
+function inMainDocument(elt) {
+  return elt.ownerDocument === document ||
+    // TODO(sjmiles): ShadowDOMPolyfill intrusion
+    elt.ownerDocument.impl === document;
+}
+
+function isElementElementChild(elt) {
+  return elt.parentNode && elt.parentNode.localName === 'element';
+}
+
+// exports
+
+scope.parser = importParser;
+
+})(HTMLImports);
\ No newline at end of file
diff --git a/pkg/html_import/lib/src/boot.js b/pkg/html_import/lib/src/boot.js
new file mode 100644
index 0000000..3ec4897
--- /dev/null
+++ b/pkg/html_import/lib/src/boot.js
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 The Polymer Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style
+ * license that can be found in the LICENSE file.
+ */
+(function(){
+
+// bootstrap
+
+// IE shim for CustomEvent
+if (typeof window.CustomEvent !== 'function') {
+  window.CustomEvent = function(inType) {
+     var e = document.createEvent('HTMLEvents');
+     e.initEvent(inType, true, true);
+     return e;
+  };
+}
+
+function bootstrap() {
+  // preload document resource trees
+  HTMLImports.importer.load(document, function() {
+    HTMLImports.parser.parse(document);
+    HTMLImports.readyTime = new Date().getTime();
+    // send HTMLImportsLoaded when finished
+    document.dispatchEvent(
+      new CustomEvent('HTMLImportsLoaded', {bubbles: true})
+    );
+  });
+};
+
+if (document.readyState === 'complete') {
+  bootstrap();
+} else {
+  window.addEventListener('DOMContentLoaded', bootstrap);
+}
+
+})();
diff --git a/pkg/html_import/lib/tools/loader/loader.js b/pkg/html_import/lib/tools/loader/loader.js
new file mode 100644
index 0000000..1aa3f9c
--- /dev/null
+++ b/pkg/html_import/lib/tools/loader/loader.js
@@ -0,0 +1,101 @@
+/*

+ * Copyright 2013 The Polymer Authors. All rights reserved.

+ * Use of this source code is governed by a BSD-style

+ * license that can be found in the LICENSE file.

+ */

+

+(function() {

+  

+var scope = window.Loader = {};

+var flags = {};

+

+// convert url arguments to flags

+

+if (!flags.noOpts) {

+  location.search.slice(1).split('&').forEach(function(o) {

+    o = o.split('=');

+    o[0] && (flags[o[0]] = o[1] || true);

+  });

+}

+

+// process global logFlags

+

+parseLogFlags(flags);

+

+function load(scopeName) {

+  // imports

+

+  var scope = window[scopeName];

+  var entryPointName = scope.entryPointName;

+  var processFlags = scope.processFlags;

+

+  // acquire attributes and base path from entry point

+

+  var entryPoint = findScript(entryPointName);

+  var base = entryPoint.basePath;

+  

+  // acquire common flags

+  var flags = Loader.flags;

+

+  // convert attributes to flags

+  var flags = Loader.flags;

+  for (var i=0, a; (a=entryPoint.attributes[i]); i++) {

+    if (a.name !== 'src') {

+      flags[a.name] = a.value || true;

+    }

+  }

+

+  // parse log flags into global

+  parseLogFlags(flags);

+

+  // exports

+

+  scope.basePath = base;

+  scope.flags = flags;

+

+  // process flags for dynamic dependencies

+

+  if (processFlags) {

+    processFlags.call(scope, flags);

+  }

+

+  // post-process imports

+

+  var modules = scope.modules || [];

+  var sheets = scope.sheets || [];

+

+  // write script tags for dependencies

+

+  modules.forEach(function(src) {

+    document.write('<script src="' + base + src + '"></script>');

+  });

+

+  // write link tags for styles

+

+  sheets.forEach(function(src) {

+    document.write('<link rel="stylesheet" href="' + base + src + '">');

+  }); 

+}

+

+// utility method

+

+function findScript(fileName) {

+  var script = document.querySelector('script[src*="' + fileName + '"]');

+  var src = script.attributes.src.value;

+  script.basePath = src.slice(0, src.indexOf(fileName));

+  return script;

+}

+

+function parseLogFlags(flags) {

+  var logFlags = window.logFlags = window.logFlags || {};

+  if (flags.log) {

+    flags.log.split(',').forEach(function(f) {

+      logFlags[f] = true;

+    });

+  }

+}

+

+scope.flags = flags;

+scope.load = load;  

+

+})();

diff --git a/pkg/html_import/pubspec.yaml b/pkg/html_import/pubspec.yaml
new file mode 100644
index 0000000..9aa328d
--- /dev/null
+++ b/pkg/html_import/pubspec.yaml
@@ -0,0 +1,8 @@
+name: html_import
+author: "Web UI Team <web-ui-dev@dartlang.org>"
+homepage: https://github.com/Polymer/HTMLImports/tree/master
+description: >
+  HTML Imports are a way to include and reuse HTML documents in other HTML
+  documents. As <script> tags let authors include external code in their
+  pages, imports let authors load full HTML resources. In particular, imports
+  let authors include Custom Element definitions from external URLs.
diff --git a/pkg/mdv/lib/mdv.dart b/pkg/mdv/lib/mdv.dart
index 0b8fc53..8c46580 100644
--- a/pkg/mdv/lib/mdv.dart
+++ b/pkg/mdv/lib/mdv.dart
@@ -13,6 +13,7 @@
 import 'dart:collection';
 import 'dart:html';
 import 'dart:math' as math;
+import 'dart:mirrors';
 import 'package:observe/observe.dart';
 
 // TODO(jmesserly): get this from somewhere else. See http://dartbug.com/4161.
diff --git a/pkg/mdv/lib/src/element.dart b/pkg/mdv/lib/src/element.dart
index 3e6e1b0..a126161 100644
--- a/pkg/mdv/lib/src/element.dart
+++ b/pkg/mdv/lib/src/element.dart
@@ -8,56 +8,41 @@
 class _ElementExtension extends _NodeExtension {
   _ElementExtension(Element node) : super(node);
 
-  Element get node => super.node;
-
-  Map<String, StreamSubscription> _attributeBindings;
-
   // TODO(jmesserly): should path be optional, and default to empty path?
   // It is used that way in at least one path in JS TemplateElement tests
   // (see "BindImperative" test in original JS code).
-  void bind(String name, model, String path) {
-    if (_attributeBindings == null) {
-      _attributeBindings = new Map<String, StreamSubscription>();
-    }
+  NodeBinding createBinding(String name, model, String path) =>
+      new _AttributeBinding(node, name, model, path);
+}
 
-    var changed;
-    if (name.endsWith('?')) {
+class _AttributeBinding extends NodeBinding {
+  final bool conditional;
+
+  _AttributeBinding._(node, name, model, path, this.conditional)
+      : super(node, name, model, path);
+
+  factory _AttributeBinding(Element node, name, model, path) {
+    bool conditional = name.endsWith('?');
+    if (conditional) {
       node.xtag.attributes.remove(name);
       name = name.substring(0, name.length - 1);
-
-      changed = (value) {
-        if (_toBoolean(value)) {
-          node.xtag.attributes[name] = '';
-        } else {
-          node.xtag.attributes.remove(name);
-        }
-      };
-    } else {
-      changed = (value) {
-        // TODO(jmesserly): escape value if needed to protect against XSS.
-        // See https://github.com/polymer-project/mdv/issues/58
-        node.xtag.attributes[name] = value == null ? '' : '$value';
-      };
     }
-
-    unbind(name);
-
-    _attributeBindings[name] = new PathObserver(model, path).bindSync(changed);
+    return new _AttributeBinding._(node, name, model, path, conditional);
   }
 
-  void unbind(String name) {
-    if (_attributeBindings != null) {
-      var binding = _attributeBindings.remove(name);
-      if (binding != null) binding.cancel();
-    }
-  }
+  Element get node => super.node;
 
-  void unbindAll() {
-    if (_attributeBindings != null) {
-      for (var binding in _attributeBindings.values) {
-        binding.cancel();
+  void boundValueChanged(value) {
+    if (conditional) {
+      if (_toBoolean(value)) {
+        node.xtag.attributes[property] = '';
+      } else {
+        node.xtag.attributes.remove(property);
       }
-      _attributeBindings = null;
+    } else {
+      // TODO(jmesserly): escape value if needed to protect against XSS.
+      // See https://github.com/polymer-project/mdv/issues/58
+      node.xtag.attributes[property] = sanitizeBoundValue(value);
     }
   }
 }
diff --git a/pkg/mdv/lib/src/input_bindings.dart b/pkg/mdv/lib/src/input_bindings.dart
index 3676691..1e39654 100644
--- a/pkg/mdv/lib/src/input_bindings.dart
+++ b/pkg/mdv/lib/src/input_bindings.dart
@@ -4,27 +4,21 @@
 
 part of mdv;
 
-abstract class _InputBinding {
-  // InputElement or SelectElement
-  final element;
-  PathObserver binding;
-  StreamSubscription _pathSub;
+abstract class _InputBinding extends NodeBinding {
   StreamSubscription _eventSub;
 
-  _InputBinding(this.element, model, String path) {
-    binding = new PathObserver(model, path);
-    _pathSub = binding.bindSync(valueChanged);
-    _eventSub = _getStreamForInputType(element).listen(updateBinding);
+  _InputBinding(node, name, model, path): super(node, name, model, path) {
+    _eventSub = _getStreamForInputType(node).listen(nodeValueChanged);
   }
 
-  void valueChanged(newValue);
+  void boundValueChanged(newValue);
 
-  void updateBinding(e);
+  void nodeValueChanged(e);
 
-  void unbind() {
-    binding = null;
-    _pathSub.cancel();
+  void close() {
+    if (closed) return;
     _eventSub.cancel();
+    super.close();
   }
 
   static EventStreamProvider<Event> _checkboxEventType = () {
@@ -61,39 +55,41 @@
 }
 
 class _ValueBinding extends _InputBinding {
-  _ValueBinding(element, model, path) : super(element, model, path);
+  _ValueBinding(node, model, path) : super(node, 'value', model, path);
 
-  void valueChanged(value) {
-    // Note: element can be an InputElement or TextAreaElement.
-    element.value = value == null ? '' : '$value';
+  get node => super.node;
+
+  void boundValueChanged(newValue) {
+    // Note: node can be an InputElement or TextAreaElement. Both have "value".
+    (node as dynamic).value = sanitizeBoundValue(newValue);
   }
 
-  void updateBinding(e) {
-    binding.value = element.value;
+  void nodeValueChanged(e) {
+    value = (node as dynamic).value;
   }
 }
 
 class _CheckedBinding extends _InputBinding {
-  _CheckedBinding(element, model, path) : super(element, model, path);
+  _CheckedBinding(node, model, path) : super(node, 'checked', model, path);
 
-  InputElement get element => super.element;
+  InputElement get node => super.node;
 
-  void valueChanged(value) {
-    element.checked = _toBoolean(value);
+  void boundValueChanged(newValue) {
+    node.checked = _toBoolean(newValue);
   }
 
-  void updateBinding(e) {
-    binding.value = element.checked;
+  void nodeValueChanged(e) {
+    value = node.checked;
 
     // Only the radio button that is getting checked gets an event. We
     // therefore find all the associated radio buttons and update their
     // CheckedBinding manually.
-    if (element is InputElement && element.type == 'radio') {
-      for (var r in _getAssociatedRadioButtons(element)) {
-        var checkedBinding = _mdv(r)._checkedBinding;
+    if (node is InputElement && node.type == 'radio') {
+      for (var r in _getAssociatedRadioButtons(node)) {
+        var checkedBinding = r.bindings['checked'];
         if (checkedBinding != null) {
           // Set the value directly to avoid an infinite call stack.
-          checkedBinding.binding.value = false;
+          checkedBinding.value = false;
         }
       }
     }
@@ -135,14 +131,15 @@
 }
 
 class _SelectedIndexBinding extends _InputBinding {
-  _SelectedIndexBinding(element, model, path) : super(element, model, path);
+  _SelectedIndexBinding(node, model, path)
+      : super(node, 'selectedIndex', model, path);
 
-  SelectElement get element => super.element;
+  SelectElement get node => super.node;
 
-  void valueChanged(value) {
+  void boundValueChanged(value) {
     var newValue = _toInt(value);
-    if (newValue <= element.length) {
-      element.selectedIndex = newValue;
+    if (newValue <= node.length) {
+      node.selectedIndex = newValue;
       return;
     }
 
@@ -163,18 +160,18 @@
     // The resulting 2 * 2 is our maxRetries.
     var maxRetries = 4;
     delaySetSelectedIndex() {
-      if (newValue > element.length && --maxRetries >= 0) {
+      if (newValue > node.length && --maxRetries >= 0) {
         runAsync(delaySetSelectedIndex);
       } else {
-        element.selectedIndex = newValue;
+        node.selectedIndex = newValue;
       }
     }
 
     runAsync(delaySetSelectedIndex);
   }
 
-  void updateBinding(e) {
-    binding.value = element.selectedIndex;
+  void nodeValueChanged(e) {
+    value = node.selectedIndex;
   }
 
   // TODO(jmesserly,sigmund): I wonder how many bindings typically convert from
diff --git a/pkg/mdv/lib/src/input_element.dart b/pkg/mdv/lib/src/input_element.dart
index 3876ddb..7e4855e 100644
--- a/pkg/mdv/lib/src/input_element.dart
+++ b/pkg/mdv/lib/src/input_element.dart
@@ -6,54 +6,20 @@
 
 /** Extensions to the [InputElement] API. */
 class _InputElementExtension extends _ElementExtension {
-  _ValueBinding _valueBinding;
-  _CheckedBinding _checkedBinding;
-
   _InputElementExtension(InputElement node) : super(node);
 
   InputElement get node => super.node;
 
-  void bind(String name, model, String path) {
-    switch (name.toLowerCase()) {
-      case 'value':
-        unbind('value');
-        node.attributes.remove('value');
-        _valueBinding = new _ValueBinding(node, model, path);
-        break;
-      case 'checked':
-        unbind('checked');
-        node.attributes.remove('checked');
-        _checkedBinding = new _CheckedBinding(node, model, path);
-        break;
-      default:
-        super.bind(name, model, path);
-        break;
+  NodeBinding createBinding(String name, model, String path) {
+    if (name == 'value') {
+      // TODO(rafaelw): Maybe template should remove all binding instructions.
+      node.attributes.remove(name);
+      return new _ValueBinding(node, model, path);
     }
-  }
-
-  void unbind(String name) {
-    switch (name.toLowerCase()) {
-      case 'value':
-        if (_valueBinding != null) {
-          _valueBinding.unbind();
-          _valueBinding = null;
-        }
-        break;
-      case 'checked':
-        if (_checkedBinding != null) {
-          _checkedBinding.unbind();
-          _checkedBinding = null;
-        }
-        break;
-      default:
-        super.unbind(name);
-        break;
+    if (name == 'checked') {
+      node.attributes.remove(name);
+      return new _CheckedBinding(node, model, path);
     }
-  }
-
-  void unbindAll() {
-    unbind('value');
-    unbind('checked');
-    super.unbindAll();
+    return super.createBinding(name, model, path);
   }
 }
diff --git a/pkg/mdv/lib/src/list_diff.dart b/pkg/mdv/lib/src/list_diff.dart
index 55246aa..8858c76 100644
--- a/pkg/mdv/lib/src/list_diff.dart
+++ b/pkg/mdv/lib/src/list_diff.dart
@@ -38,6 +38,18 @@
 
   int get removedCount => _removed.length;
 
+  /** Returns true if the provided index was changed by this operation. */
+  bool changes(key) {
+    // If key isn't an int, or before the index, then it wasn't changed.
+    if (key is! int || key < index) return false;
+
+    // If this was a shift operation, anything after index is changed.
+    if (addedCount != removedCount) return true;
+
+    // Otherwise, anything in the update range was changed.
+    return key < index + addedCount;
+  }
+
   String toString() => '#<$runtimeType index: $index, '
       'removed: $removed, addedCount: $addedCount>';
 }
diff --git a/pkg/mdv/lib/src/node.dart b/pkg/mdv/lib/src/node.dart
index 0379838..d2fa6d4 100644
--- a/pkg/mdv/lib/src/node.dart
+++ b/pkg/mdv/lib/src/node.dart
@@ -7,23 +7,50 @@
 /** Extensions to the [Node] API. */
 class _NodeExtension {
   final Node node;
+  Map<String, NodeBinding> _bindings;
 
   _NodeExtension(this.node);
 
+  NodeBinding createBinding(String name, model, String path) => null;
+
   /**
    * Binds the attribute [name] to the [path] of the [model].
    * Path is a String of accessors such as `foo.bar.baz`.
    */
-  void bind(String name, model, String path) {
-    window.console.error('Unhandled binding to Node: '
-        '$this $name $model $path');
+  NodeBinding bind(String name, model, String path) {
+    var binding = bindings[name];
+    if (binding != null)  binding.close();
+
+    binding = createBinding(name, model, path);
+    bindings[name] = binding;
+    if (binding == null) {
+      window.console.error('Unhandled binding to Node: '
+          '$this $name $model $path');
+    }
+    return binding;
   }
 
   /** Unbinds the attribute [name]. */
-  void unbind(String name) {}
+  void unbind(String name) {
+    if (_bindings == null) return;
+    var binding = bindings.remove(name);
+    if (binding != null) binding.close();
+  }
 
   /** Unbinds all bound attributes. */
-  void unbindAll() {}
+  void unbindAll() {
+    if (_bindings == null) return;
+    for (var binding in bindings.values) {
+      if (binding != null) binding.close();
+    }
+    _bindings = null;
+  }
+
+  // TODO(jmesserly): we should return a read-only wrapper here.
+  Map<String, NodeBinding> get bindings {
+    if (_bindings == null) _bindings = new LinkedHashMap<String, NodeBinding>();
+    return _bindings;
+  }
 
   TemplateInstance _templateInstance;
 
@@ -32,3 +59,71 @@
       _templateInstance != null ? _templateInstance :
       (node.parent != null ? node.parent.templateInstance : null);
 }
+
+/** A data binding on a [Node]. See [Node.bindings] and [Node.bind]. */
+abstract class NodeBinding {
+  Node _node;
+  var _model;
+  PathObserver _observer;
+  StreamSubscription _pathSub;
+
+  /** The property of [node] which will be data bound. */
+  final String property;
+
+  /** The property of [node] which will be data bound. */
+  final String path;
+
+  /** The node that has [property] which will be data bound. */
+  Node get node => _node;
+
+  /**
+   * The bound data model.
+   */
+  get model => _model;
+
+  /** True if this binding has been [closed]. */
+  bool get closed => _observer == null;
+
+  /** The value at the [path] on [model]. */
+  get value => _observer.value;
+
+  set value(newValue) {
+    _observer.value = newValue;
+  }
+
+  NodeBinding(this._node, this.property, this._model, this.path) {
+    // Create the path observer
+    _observer = new PathObserver(model, path);
+    _observePath();
+  }
+
+  void _observePath() {
+    _pathSub = _observer.bindSync(boundValueChanged);
+  }
+
+  /** Called when [value] changes to update the [node]. */
+  // TODO(jmesserly): the impl in MDV uses mirrors to set the property,
+  // but that isn't used except for specific known fields like "textContent",
+  // so I'm overridding this in the subclasses instead.
+  void boundValueChanged(newValue);
+
+  /** Called to sanitize the value before it is assigned into the property. */
+  sanitizeBoundValue(value) => value == null ? '' : '$value';
+
+  /**
+   * Called by [Node.unbind] to close this binding and unobserve the [path].
+   *
+   * This can be overridden in subclasses, but they must call `super.close()`
+   * to free associated resources. They must also check [closed] and return
+   * immediately if already closed.
+   */
+  void close() {
+    if (closed) return;
+
+    if (_pathSub != null) _pathSub.cancel();
+    _pathSub = null;
+    _observer = null;
+    _node = null;
+    _model = null;
+  }
+}
diff --git a/pkg/mdv/lib/src/select_element.dart b/pkg/mdv/lib/src/select_element.dart
index 64b599a..5ae74c8 100644
--- a/pkg/mdv/lib/src/select_element.dart
+++ b/pkg/mdv/lib/src/select_element.dart
@@ -6,33 +6,16 @@
 
 /** Extensions to the [SelectElement] API. */
 class _SelectElementExtension extends _ElementExtension {
-  _SelectedIndexBinding _valueBinding;
-
   _SelectElementExtension(SelectElement node) : super(node);
 
   SelectElement get node => super.node;
 
-  void bind(String name, model, String path) {
+  NodeBinding createBinding(String name, model, String path) {
     if (name.toLowerCase() == 'selectedindex') {
-      unbind('selectedindex');
-      node.attributes.remove('selectedindex');
-      _valueBinding = new _SelectedIndexBinding(node, model, path);
-      return;
+      // TODO(rafaelw): Maybe template should remove all binding instructions.
+      node.attributes.remove(name);
+      return new _SelectedIndexBinding(node, model, path);
     }
-    super.bind(name, model, path);
-  }
-
-  void unbind(String name) {
-    if (name.toLowerCase() == 'selectedindex' && _valueBinding != null) {
-      _valueBinding.unbind();
-      _valueBinding = null;
-      return;
-    }
-    super.unbind(name);
-  }
-
-  void unbindAll() {
-    unbind('selectedindex');
-    super.unbindAll();
+    return super.createBinding(name, model, path);
   }
 }
diff --git a/pkg/mdv/lib/src/template.dart b/pkg/mdv/lib/src/template.dart
index 5b8a467..7bdc0bb 100644
--- a/pkg/mdv/lib/src/template.dart
+++ b/pkg/mdv/lib/src/template.dart
@@ -13,7 +13,9 @@
 
   _TemplateExtension(Element node) : super(node);
 
-  void bind(String name, model, String path) {
+  Element get node => super.node;
+
+  NodeBinding createBinding(String name, model, String path) {
     switch (name) {
       case 'bind':
       case 'repeat':
@@ -21,34 +23,14 @@
         if (_templateIterator == null) {
           _templateIterator = new _TemplateIterator(node);
         }
-        _templateIterator.inputs.bind(name, model, path);
-        return;
+        // TODO(jmesserly): why do we do this here and nowhere else?
+        if (path == null) path = '';
+        return new _TemplateBinding(node, name, model, path);
       default:
-        super.bind(name, model, path);
+        return super.createBinding(name, model, path);
     }
   }
 
-  void unbind(String name) {
-    switch (name) {
-      case 'bind':
-      case 'repeat':
-      case 'if':
-        if (_templateIterator != null) {
-          _templateIterator.inputs.unbind(name);
-        }
-        return;
-      default:
-        super.unbind(name);
-    }
-  }
-
-  void unbindAll() {
-    unbind('bind');
-    unbind('repeat');
-    unbind('if');
-    super.unbindAll();
-  }
-
   /**
    * Creates an instance of the template.
    */
@@ -98,3 +80,23 @@
     _addBindings(node, _model, _bindingDelegate);
   }
 }
+
+class _TemplateBinding extends NodeBinding {
+  // TODO(jmesserly): MDV uses TemplateIterator as the node, see:
+  // https://github.com/Polymer/mdv/issues/127
+  _TemplateBinding(node, name, model, path)
+      : super(node, name, model, path) {
+    _mdv(node)._templateIterator.inputs.bind(property, model, path);
+  }
+
+  // These are no-ops because we don't use the underlying PathObserver.
+  void _observePath() {}
+  void boundValueChanged(newValue) {}
+
+  void close() {
+    if (closed) return;
+    var templateIterator = _mdv(node)._templateIterator;
+    if (templateIterator != null) templateIterator.inputs.unbind(property);
+    super.close();
+  }
+}
diff --git a/pkg/mdv/lib/src/template_iterator.dart b/pkg/mdv/lib/src/template_iterator.dart
index 1c76e61..8cca7ce 100644
--- a/pkg/mdv/lib/src/template_iterator.dart
+++ b/pkg/mdv/lib/src/template_iterator.dart
@@ -239,7 +239,7 @@
     iteratedValue = value;
 
     if (iteratedValue is Observable) {
-      _sub = iteratedValue.changes.listen(_handleChanges);
+      _sub = (iteratedValue as Observable).changes.listen(_handleChanges);
     }
 
     var splices = calculateSplices(
diff --git a/pkg/mdv/lib/src/text.dart b/pkg/mdv/lib/src/text.dart
index 6d2b380..f17ba2d 100644
--- a/pkg/mdv/lib/src/text.dart
+++ b/pkg/mdv/lib/src/text.dart
@@ -8,37 +8,16 @@
 class _TextExtension extends _NodeExtension {
   _TextExtension(Text node) : super(node);
 
-  Text get node => super.node;
-
-  StreamSubscription _textBinding;
-
-  void bind(String name, model, String path) {
-    if (name != 'text') {
-      super.bind(name, model, path);
-      return;
-    }
-
-    unbind('text');
-
-    _textBinding = new PathObserver(model, path).bindSync((value) {
-      node.text = value == null ? '' : '$value';
-    });
+  NodeBinding createBinding(String name, model, String path) {
+    if (name == 'text') return new _TextBinding(node, model, path);
+    return super.createBinding(name, model, path);
   }
+}
 
-  void unbind(String name) {
-    if (name != 'text') {
-      super.unbind(name);
-      return;
-    }
+class _TextBinding extends NodeBinding {
+  _TextBinding(node, model, path) : super(node, 'text', model, path);
 
-    if (_textBinding == null) return;
-
-    _textBinding.cancel();
-    _textBinding = null;
-  }
-
-  void unbindAll() {
-    unbind('text');
-    super.unbindAll();
+  void boundValueChanged(newValue) {
+    node.text = sanitizeBoundValue(newValue);
   }
 }
diff --git a/pkg/mdv/lib/src/text_area_element.dart b/pkg/mdv/lib/src/text_area_element.dart
index d809691..200cd74 100644
--- a/pkg/mdv/lib/src/text_area_element.dart
+++ b/pkg/mdv/lib/src/text_area_element.dart
@@ -6,29 +6,16 @@
 
 /** Extensions to the [TextAreaElement] API. */
 class _TextAreaElementExtension extends _ElementExtension {
-  _ValueBinding _valueBinding;
-
   _TextAreaElementExtension(TextAreaElement node) : super(node);
 
   TextAreaElement get node => super.node;
 
-  void bind(String name, model, String path) {
-    if (name.toLowerCase() == 'value') {
-      unbind('value');
-      node.attributes.remove('value');
-      _valueBinding = new _ValueBinding(node, model, path);
+  NodeBinding createBinding(String name, model, String path) {
+    if (name == 'value') {
+      // TODO(rafaelw): Maybe template should remove all binding instructions.
+      node.attributes.remove(name);
+      return new _ValueBinding(node, model, path);
     }
-  }
-
-  void unbind(String name) {
-    if (name.toLowerCase() == 'value' && _valueBinding != null) {
-      _valueBinding.unbind();
-      _valueBinding = null;
-    }
-  }
-
-  void unbindAll() {
-    unbind('value');
-    super.unbindAll();
+    return super.createBinding(name, model, path);
   }
 }
diff --git a/pkg/observe/lib/src/change_record.dart b/pkg/observe/lib/src/change_record.dart
index 652e89e..99e806d 100644
--- a/pkg/observe/lib/src/change_record.dart
+++ b/pkg/observe/lib/src/change_record.dart
@@ -6,8 +6,9 @@
 
 /** Records a change to an [Observable]. */
 abstract class ChangeRecord {
+  // TODO(jmesserly): rename this--it's confusing. Perhaps "matches"?
   /** True if the change affected the given item, otherwise false. */
-  bool change(key);
+  bool changes(key);
 }
 
 /** A change record to a field of an observable object. */
diff --git a/pkg/path/README.md b/pkg/path/README.md
index 4108ce0..eff60de 100644
--- a/pkg/path/README.md
+++ b/pkg/path/README.md
@@ -1,360 +1,39 @@
 A comprehensive, cross-platform path manipulation library for Dart.
 
-The path package provides common operations for manipulating file paths:
+The path package provides common operations for manipulating paths:
 joining, splitting, normalizing, etc.
 
 We've tried very hard to make this library do the "right" thing on whatever
-platform you run it on. When you use the top-level functions, it will assume the
-current platform's path style and work with that. If you want to specifically
-work with paths of a specific style, you can construct a `path.Builder` for that
-style.
+platform you run it on, including in the browser. When you use the top-level
+functions, it will assume the current platform's path style and work with
+that. If you want to explicitly work with paths of a specific style, you can
+construct a `path.Builder` for that style.
 
 ## Using
 
 The path library was designed to be imported with a prefix, though you don't
 have to if you don't want to:
 
-    import 'package:path/path.dart' as path; // TODO(bob): ???
-
-## Top-level functions
+    import 'package:path/path.dart' as path;
 
 The most common way to use the library is through the top-level functions.
-These manipulate path strings based on your current working directory and the
-path style (POSIX, Windows, or URLs) of the host platform.
+These manipulate path strings based on your current working directory and
+the path style (POSIX, Windows, or URLs) of the host platform. For example:
 
-### String get current
+    path.join("directory", "file.txt");
 
-Gets the path to the current working directory. In the browser, this means the
-current URL. When using dart2js, this currently returns `.` due to technical
-constraints. In the future, it will return the current URL.
+This calls the top-level [join] function to join "directory" and
+"file.txt" using the current platform's directory separator.
 
-### String get separator
+If you want to work with paths for a specific platform regardless of the
+underlying platform that the program is running on, you can create a
+[Builder] and give it an explicit [Style]:
 
-Gets the path separator for the current platform. On Mac, Linux, and the
-browser, this is `/`. On Windows, it's `\`.
+    var builder = new path.Builder(style: Style.windows);
+    builder.join("directory", "file.txt");
 
-### String absolute(String path)
-
-Converts [path] to an absolute path by resolving it relative to the current
-working directory. If [path] is already an absolute path, just returns it.
-
-    path.absolute('foo/bar.txt'); // -> /your/current/dir/foo/bar.txt
-
-### String basename(String path)
-
-Gets the part of [path] after the last separator.
-
-    path.basename('path/to/foo.dart'); // -> 'foo.dart'
-    path.basename('path/to');          // -> 'to'
-
-Trailing separators are ignored.
-
-    builder.basename('path/to/'); // -> 'to'
-
-### String basenameWithoutExtension(String path)
-
-Gets the part of [path] after the last separator, and without any trailing
-file extension.
-
-    path.basenameWithoutExtension('path/to/foo.dart'); // -> 'foo'
-
-Trailing separators are ignored.
-
-    builder.basenameWithoutExtension('path/to/foo.dart/'); // -> 'foo'
-
-### String dirname(String path)
-
-Gets the part of [path] before the last separator.
-
-    path.dirname('path/to/foo.dart'); // -> 'path/to'
-    path.dirname('path/to');          // -> 'to'
-
-Trailing separators are ignored.
-
-    builder.dirname('path/to/'); // -> 'path'
-
-If an absolute path contains no directories, only a root, then the root
-is returned.
-
-    path.dirname('/');    // -> '/' (posix)
-    path.dirname('c:\');  // -> 'c:\' (windows)
-
-If a relative path has no directories, then '.' is returned.
-    path.dirname('foo');  // -> '.'
-    path.dirname('');     // -> '.'
-
-### String extension(String path)
-
-Gets the file extension of [path]: the portion of [basename] from the last
-`.` to the end (including the `.` itself).
-
-    path.extension('path/to/foo.dart');    // -> '.dart'
-    path.extension('path/to/foo');         // -> ''
-    path.extension('path.to/foo');         // -> ''
-    path.extension('path/to/foo.dart.js'); // -> '.js'
-
-If the file name starts with a `.`, then that is not considered the
-extension:
-
-    path.extension('~/.bashrc');    // -> ''
-    path.extension('~/.notes.txt'); // -> '.txt'
-
-### String rootPrefix(String path)
-
-Returns the root of [path], if it's absolute, or the empty string if it's
-relative.
-
-    // Unix
-    path.rootPrefix('path/to/foo'); // -> ''
-    path.rootPrefix('/path/to/foo'); // -> '/'
-
-    // Windows
-    path.rootPrefix(r'path\to\foo'); // -> ''
-    path.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
-
-    // URL
-    path.rootPrefix('path/to/foo'); // -> ''
-    path.rootPrefix('http://dartlang.org/path/to/foo');
-      // -> 'http://dartlang.org'
-
-### bool isAbsolute(String path)
-
-Returns `true` if [path] is an absolute path and `false` if it is a relative
-path. On POSIX systems, absolute paths start with a `/` (forward slash). On
-Windows, an absolute path starts with `\\`, or a drive letter followed by `:/`
-or `:\`. For URLs, absolute paths either start with a protocol and optional
-hostname (e.g. `http://dartlang.org`, `file://`) or with a `/`.
-
-URLs that start with `/` are known as "root-relative", since they're relative to
-the root of the current URL. Since root-relative paths are still absolute in
-every other sense, [isAbsolute] will return true for them. They can be detected
-using [isRootRelative].
-
-### bool isRelative(String path)
-
-Returns `true` if [path] is a relative path and `false` if it is absolute.
-On POSIX systems, absolute paths start with a `/` (forward slash). On
-Windows, an absolute path starts with `\\`, or a drive letter followed by
-`:/` or `:\`.
-
-### bool isRootRelative(String path)
-
-Returns `true` if [path] is a root-relative path and `false` if it's not. URLs
-that start with `/` are known as "root-relative", since they're relative to the
-root of the current URL. Since root-relative paths are still absolute in every
-other sense, [isAbsolute] will return true for them. They can be detected using
-[isRootRelative].
-
-No POSIX and Windows paths are root-relative.
-
-### String join(String part1, [String part2, String part3, ...])
-
-Joins the given path parts into a single path using the current platform's
-[separator]. Example:
-
-    path.join('path', 'to', 'foo'); // -> 'path/to/foo'
-
-If any part ends in a path separator, then a redundant separator will not
-be added:
-
-    path.join('path/', 'to', 'foo'); // -> 'path/to/foo
-
-If a part is an absolute path, then anything before that will be ignored:
-
-    path.join('path', '/to', 'foo'); // -> '/to/foo'
-
-### List<String> split(String path)
-
-Splits [path] into its components using the current platform's [separator].
-
-    path.split('path/to/foo'); // -> ['path', 'to', 'foo']
-
-The path will *not* be normalized before splitting.
-
-    path.split('path/../foo'); // -> ['path', '..', 'foo']
-
-If [path] is absolute, the root directory will be the first element in the
-array. Example:
-
-    // Unix
-    path.split('/path/to/foo'); // -> ['/', 'path', 'to', 'foo']
-
-    // Windows
-    path.split(r'C:\path\to\foo'); // -> [r'C:\', 'path', 'to', 'foo']
-
-    // Browser
-    path.split('http://dartlang.org/path/to/foo');
-      // -> ['http://dartlang.org', 'path', 'to', 'foo']
-
-### String normalize(String path)
-
-Normalizes [path], simplifying it by handling `..`, and `.`, and
-removing redundant path separators whenever possible.
-
-    path.normalize('path/./to/..//file.text'); // -> 'path/file.txt'
-String normalize(String path) => _builder.normalize(path);
-
-### String relative(String path, {String from})
-
-Attempts to convert [path] to an equivalent relative path from the current
-directory.
-
-    // Given current directory is /root/path:
-    path.relative('/root/path/a/b.dart'); // -> 'a/b.dart'
-    path.relative('/root/other.dart'); // -> '../other.dart'
-
-If the [from] argument is passed, [path] is made relative to that instead.
-
-    path.relative('/root/path/a/b.dart',
-        from: '/root/path'); // -> 'a/b.dart'
-    path.relative('/root/other.dart',
-        from: '/root/path'); // -> '../other.dart'
-
-If [path] and/or [from] are relative paths, they are assumed to be relative
-to the current directory.
-
-Since there is no relative path from one drive letter to another on Windows,
-this will return an absolute path in that case.
-
-    // Windows
-    path.relative(r'D:\other', from: r'C:\home'); // -> 'D:\other'
-
-    // URL
-    path.relative('http://dartlang.org', from: 'http://pub.dartlang.org');
-      // -> 'http://dartlang.org'
-
-### String withoutExtension(String path)
-
-Removes a trailing extension from the last part of [path].
-
-    withoutExtension('path/to/foo.dart'); // -> 'path/to/foo'
-
-### String fromUri(Uri uri)
-
-Returns the path represented by [uri]. For POSIX and Windows styles, [uri] must
-be a `file:` URI. For the URL style, this will just convert [uri] to a string.
-
-    // POSIX
-    path.fromUri(Uri.parse('file:///path/to/foo'))
-      // -> '/path/to/foo'
-
-    // Windows
-    path.fromUri(Uri.parse('file:///C:/path/to/foo'))
-      // -> r'C:\path\to\foo'
-
-    // URL
-    path.fromUri(Uri.parse('http://dartlang.org/path/to/foo'))
-      // -> 'http://dartlang.org/path/to/foo'
-
-### Uri toUri(String path)
-
-Returns the URI that represents [path]. For POSIX and Windows styles, this will
-return a `file:` URI. For the URL style, this will just convert [path] to a
-[Uri].
-
-This will always convert relative paths to absolute ones before converting
-to a URI.
-
-    // POSIX
-    path.toUri('/path/to/foo')
-      // -> Uri.parse('file:///path/to/foo')
-
-    // Windows
-    path.toUri(r'C:\path\to\foo')
-      // -> Uri.parse('file:///C:/path/to/foo')
-
-    // URL
-    path.toUri('http://dartlang.org/path/to/foo')
-      // -> Uri.parse('http://dartlang.org/path/to/foo')
-
-## The path.Builder class
-
-In addition to the functions, path exposes a `path.Builder` class. This lets
-you configure the root directory and path style that paths are built using
-explicitly instead of assuming the current working directory and host OS's path
-style.
-
-You won't often use this, but it can be useful if you do a lot of path
-manipulation relative to some root directory.
-
-    var builder = new path.Builder(root: '/other/root');
-    builder.relative('/other/root/foo.txt'); // -> 'foo.txt'
-
-It exposes the same methods and getters as the top-level functions, with the
-addition of:
-
-### new Builder({Style style, String root})
-
-Creates a new path builder for the given style and root directory.
-
-If [style] is omitted, it uses the host operating system's path style. If
-[root] is omitted, it defaults to the current working directory. If [root]
-is relative, it is considered relative to the current working directory.
-
-### Style style
-
-The style of path that this builder works with.
-
-### String root
-
-The root directory that relative paths will be relative to.
-
-### String get separator
-
-Gets the path separator for the builder's [style]. On Mac and Linux,
-this is `/`. On Windows, it's `\`.
-
-### String rootPrefix(String path)
-
-Returns the root of [path], if it's absolute, or an empty string if it's
-relative.
-
-    // Unix
-    builder.rootPrefix('path/to/foo'); // -> ''
-    builder.rootPrefix('/path/to/foo'); // -> '/'
-
-    // Windows
-    builder.rootPrefix(r'path\to\foo'); // -> ''
-    builder.rootPrefix(r'C:\path\to\foo'); // -> r'C:\'
-
-    // URL
-    builder.rootPrefix('path/to/foo'); // -> ''
-    builder.rootPrefix('http://dartlang.org/path/to/foo');
-      // -> 'http://dartlang.org'
-
-### String resolve(String part1, [String part2, String part3, ...])
-
-Creates a new path by appending the given path parts to the [root].
-Equivalent to [join()] with [root] as the first argument. Example:
-
-    var builder = new Builder(root: 'root');
-    builder.resolve('path', 'to', 'foo'); // -> 'root/path/to/foo'
-
-## The path.Style class
-
-The path library can work with three different "flavors" of path: POSIX,
-Windows, and URLs. The differences between these are encapsulated by the
-`path.Style` enum class. There are three instances of it:
-
-### path.Style.posix
-
-POSIX-style paths use "/" (forward slash) as separators. Absolute paths
-start with "/". Used by UNIX, Linux, Mac OS X, and others.
-
-### path.Style.windows
-
-Windows paths use "\" (backslash) as separators. Absolute paths start with
-a drive letter followed by a colon (example, "C:") or two backslashes
-("\\") for UNC paths.
-
-### path.Style.url
-
-URLs aren't filesystem paths, but they're supported by Pathos to make it easier
-to manipulate URL paths in the browser.
-
-URLs use "/" (forward slash) as separators. Absolute paths either start with a
-protocol and optional hostname (e.g. `http://dartlang.org`, `file://`) or with
-"/".
+This will join "directory" and "file.txt" using the Windows path separator,
+even when the program is run on a POSIX machine.
 
 ## FAQ
 
@@ -382,7 +61,7 @@
 
  *  Taking both means you can't type your API. That defeats the purpose of
     having a path type: why have a type if your APIs can't annotate that they
-    use it?
+    expect it?
 
 Given that, we've decided this library should simply treat paths as strings.
 
@@ -402,5 +81,7 @@
  *  It knows that "C:\foo\one.txt" and "c:/foo\two.txt" are two files in the
     same directory.
 
-If you find a problem, surprise or something that's unclear, please don't
-hesitate to [file a bug](http://dartbug.com/new) and let us know.
+### What is a "path" in the browser?
+
+If you use this package in a browser, then it considers the "platform" to be
+the browser itself and uses URL strings to represent "browser paths".
diff --git a/pkg/path/lib/path.dart b/pkg/path/lib/path.dart
index bb88953..95bc90d 100644
--- a/pkg/path/lib/path.dart
+++ b/pkg/path/lib/path.dart
@@ -18,6 +18,32 @@
 ///
 /// [pub]: http://pub.dartlang.org
 /// [pkg]: http://pub.dartlang.org/packages/path
+///
+/// ## Usage ##
+///
+/// The path library was designed to be imported with a prefix, though you don't
+/// have to if you don't want to:
+///
+///     import 'package:path/path.dart' as path;
+///
+/// The most common way to use the library is through the top-level functions.
+/// These manipulate path strings based on your current working directory and
+/// the path style (POSIX, Windows, or URLs) of the host platform. For example:
+///
+///     path.join("directory", "file.txt");
+///
+/// This calls the top-level [join] function to join "directory" and "file.txt"
+/// using the current platform's directory separator.
+///
+/// If you want to work with paths for a specific platform regardless of the
+/// underlying platform that the program is running on, you can create a
+/// [Builder] and give it an explicit [Style]:
+///
+///     var builder = new path.Builder(style: Style.windows);
+///     builder.join("directory", "file.txt");
+///
+/// This will join "directory" and "file.txt" using the Windows path separator,
+/// even when the program is run on a POSIX machine.
 library path;
 
 @MirrorsUsed(targets: 'dart.dom.html.window, '
@@ -67,8 +93,8 @@
   }
 }
 
-/// Gets the path separator for the current platform. On Mac and Linux, this
-/// is `/`. On Windows, it's `\`.
+/// Gets the path separator for the current platform. This is `\` on Windows
+/// and `/` on other platforms (including the browser).
 String get separator => _builder.separator;
 
 /// Converts [path] to an absolute path by resolving it relative to the current
@@ -765,9 +791,9 @@
   ///       // -> Uri.parse('http://dartlang.org/path/to/foo')
   Uri toUri(String path) {
     if (isRelative(path)) {
-      return Uri.parse(path.replaceAll(style.separatorPattern, '/'));
+      return style.relativePathToUri(path);
     } else {
-      return style.pathToUri(join(root, path));
+      return style.absolutePathToUri(join(root, path));
     }
   }
 
@@ -821,8 +847,8 @@
   // just the "\\".
   static final windows = new _WindowsStyle();
 
-  /// URLs aren't filesystem paths, but they're supported by Pathos to make it
-  /// easier to manipulate URL paths in the browser.
+  /// URLs aren't filesystem paths, but they're supported to make it easier to
+  /// manipulate URL paths in the browser.
   ///
   /// URLs use "/" (forward slash) as separators. Absolute paths either start
   /// with a protocol and optional hostname (e.g. `http://dartlang.org`,
@@ -862,6 +888,9 @@
   /// paths.
   final Pattern relativeRootPattern = null;
 
+  /// A [Builder] that uses this style.
+  Builder get builder => new Builder(style: this);
+
   /// Gets the root prefix of [path] if path is absolute. If [path] is relative,
   /// returns `null`.
   String getRoot(String path) {
@@ -885,11 +914,12 @@
   /// Returns the path represented by [uri] in this style.
   String pathFromUri(Uri uri);
 
-  /// Returns the URI that represents [path].
-  ///
-  /// Pathos will always path an absolute path for [path]. Relative paths are
-  /// handled automatically by [Builder].
-  Uri pathToUri(String path);
+  /// Returns the URI that represents the relative path made of [parts].
+  Uri relativePathToUri(String path) =>
+      new Uri(pathSegments: builder.split(path));
+
+  /// Returns the URI that represents [path], which is assumed to be absolute.
+  Uri absolutePathToUri(String path);
 
   String toString() => name;
 }
@@ -898,8 +928,6 @@
 class _PosixStyle extends Style {
   _PosixStyle();
 
-  static final _builder = new Builder(style: Style.posix);
-
   final name = 'posix';
   final separator = '/';
   final separatorPattern = new RegExp(r'/');
@@ -913,9 +941,8 @@
     throw new ArgumentError("Uri $uri must have scheme 'file:'.");
   }
 
-  Uri pathToUri(String path) {
-    var parsed = _builder._parse(path);
-
+  Uri absolutePathToUri(String path) {
+    var parsed = builder._parse(path);
     if (parsed.parts.isEmpty) {
       // If the path is a bare root (e.g. "/"), [components] will
       // currently be empty. We add two empty components so the URL constructor
@@ -935,8 +962,6 @@
 class _WindowsStyle extends Style {
   _WindowsStyle();
 
-  static final _builder = new Builder(style: Style.windows);
-
   final name = 'windows';
   final separator = '\\';
   final separatorPattern = new RegExp(r'[/\\]');
@@ -960,8 +985,8 @@
     return Uri.decodeComponent(path.replaceAll("/", "\\"));
   }
 
-  Uri pathToUri(String path) {
-    var parsed = _builder._parse(path);
+  Uri absolutePathToUri(String path) {
+    var parsed = builder._parse(path);
     if (parsed.root == r'\\') {
       // Network paths become "file://hostname/path/to/file".
 
@@ -1013,7 +1038,8 @@
 
   String pathFromUri(Uri uri) => uri.toString();
 
-  Uri pathToUri(String path) => Uri.parse(path);
+  Uri relativePathToUri(String path) => Uri.parse(path);
+  Uri absolutePathToUri(String path) => Uri.parse(path);
 }
 
 // TODO(rnystrom): Make this public?
@@ -1043,7 +1069,8 @@
   /// path ends with a trailing separator.
   List<String> separators;
 
-  /// The file extension of the last part, or "" if it doesn't have one.
+  /// The file extension of the last non-empty part, or "" if it doesn't have
+  /// one.
   String get extension => _splitExtension()[1];
 
   /// `true` if this is an absolute path.
@@ -1059,12 +1086,7 @@
     return copy.parts.last;
   }
 
-  String get basenameWithoutExtension {
-    var copy = this.clone();
-    copy.removeTrailingSeparators();
-    if (copy.parts.isEmpty) return root == null ? '' : root;
-    return copy._splitExtension()[0];
-  }
+  String get basenameWithoutExtension => _splitExtension()[0];
 
   bool get hasTrailingSeparator =>
       !parts.isEmpty && (parts.last == '' || separators.last != '');
@@ -1137,13 +1159,15 @@
     return builder.toString();
   }
 
-  /// Splits the last part of the path into a two-element list. The first is
-  /// the name of the file without any extension. The second is the extension
-  /// or "" if it has none.
+  /// Splits the last non-empty part of the path into a `[basename, extension`]
+  /// pair.
+  ///
+  /// Returns a two-element list. The first is the name of the file without any
+  /// extension. The second is the extension or "" if it has none.
   List<String> _splitExtension() {
-    if (parts.isEmpty) return ['', ''];
+    var file = parts.lastWhere((p) => p != '', orElse: () => null);
 
-    var file = parts.last;
+    if (file == null) return ['', ''];
     if (file == '..') return ['..', ''];
 
     var lastDot = file.lastIndexOf('.');
diff --git a/pkg/path/pubspec.yaml b/pkg/path/pubspec.yaml
index 60fcec2..35047ac 100644
--- a/pkg/path/pubspec.yaml
+++ b/pkg/path/pubspec.yaml
@@ -1,6 +1,7 @@
 name: path
 author: "Dart Team <misc@dartlang.org>"
 homepage: http://www.dartlang.org
+documentation: http://api.dartlang.org/docs/pkg/path
 description: >
  A string-based path manipulation library. All of the path operations you know
  and love, with solid support on both Windows and POSIX (Linux and Mac OS X)
diff --git a/pkg/path/test/posix_test.dart b/pkg/path/test/posix_test.dart
index 3afb3bb..1156379 100644
--- a/pkg/path/test/posix_test.dart
+++ b/pkg/path/test/posix_test.dart
@@ -31,6 +31,8 @@
     expect(builder.extension('a.b/c.d'), '.d');
     expect(builder.extension('~/.bashrc'), '');
     expect(builder.extension(r'a.b\c'), r'.b\c');
+    expect(builder.extension('foo.dart/'), '.dart');
+    expect(builder.extension('foo.dart//'), '.dart');
   });
 
   test('rootPrefix', () {
@@ -462,6 +464,8 @@
     expect(builder.fromUri(Uri.parse('///path/to/foo')), '/path/to/foo');
     expect(builder.fromUri(Uri.parse('file:///path/to/foo%23bar')),
         '/path/to/foo#bar');
+    expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')),
+        r'_{_}_`_^_ _"_%_');
     expect(() => builder.fromUri(Uri.parse('http://dartlang.org')),
         throwsArgumentError);
   });
@@ -473,5 +477,9 @@
     expect(builder.toUri('foo/bar'), Uri.parse('foo/bar'));
     expect(builder.toUri('/path/to/foo#bar'),
         Uri.parse('file:///path/to/foo%23bar'));
+    expect(builder.toUri(r'/_{_}_`_^_ _"_%_'),
+        Uri.parse('file:///_%7B_%7D_%60_%5E_%20_%22_%25_'));
+    expect(builder.toUri(r'_{_}_`_^_ _"_%_'),
+        Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_'));
   });
 }
diff --git a/pkg/path/test/url_test.dart b/pkg/path/test/url_test.dart
index 1cd256d..d43e88c 100644
--- a/pkg/path/test/url_test.dart
+++ b/pkg/path/test/url_test.dart
@@ -20,6 +20,8 @@
     expect(builder.extension('a.b/c'), '');
     expect(builder.extension('a.b/c.d'), '.d');
     expect(builder.extension(r'a.b\c'), r'.b\c');
+    expect(builder.extension('foo.dart/'), '.dart');
+    expect(builder.extension('foo.dart//'), '.dart');
   });
 
   test('rootPrefix', () {
@@ -665,7 +667,6 @@
     expect(builder.withoutExtension('a/b.c//'), 'a/b//');
   });
 
-
   test('fromUri', () {
     expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo')),
         'http://dartlang.org/path/to/foo');
@@ -676,6 +677,10 @@
     expect(builder.fromUri(Uri.parse('foo/bar')), 'foo/bar');
     expect(builder.fromUri(Uri.parse('http://dartlang.org/path/to/foo%23bar')),
         'http://dartlang.org/path/to/foo%23bar');
+    // Since the resulting "path" is also a URL, special characters should
+    // remain percent-encoded in the result.
+    expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')),
+        r'_%7B_%7D_%60_%5E_%20_%22_%25_');
   });
 
   test('toUri', () {
@@ -688,5 +693,11 @@
     expect(builder.toUri('foo/bar'), Uri.parse('foo/bar'));
     expect(builder.toUri('http://dartlang.org/path/to/foo%23bar'),
         Uri.parse('http://dartlang.org/path/to/foo%23bar'));
+    // Since the input path is also a URI, special characters should already
+    // be percent encoded there too.
+    expect(builder.toUri(r'http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'),
+        Uri.parse('http://foo.com/_%7B_%7D_%60_%5E_%20_%22_%25_'));
+    expect(builder.toUri(r'_%7B_%7D_%60_%5E_%20_%22_%25_'),
+        Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_'));
   });
 }
diff --git a/pkg/path/test/windows_test.dart b/pkg/path/test/windows_test.dart
index 1966b7f..787adfc 100644
--- a/pkg/path/test/windows_test.dart
+++ b/pkg/path/test/windows_test.dart
@@ -35,6 +35,8 @@
     expect(builder.extension('a.b/c.d'), '.d');
     expect(builder.extension(r'~\.bashrc'), '');
     expect(builder.extension(r'a.b/c'), r'');
+    expect(builder.extension(r'foo.dart\'), '.dart');
+    expect(builder.extension(r'foo.dart\\'), '.dart');
   });
 
   test('rootPrefix', () {
@@ -503,6 +505,8 @@
         r'C:\path\to\foo#bar');
     expect(builder.fromUri(Uri.parse('file://hostname/path/to/foo%23bar')),
         r'\\hostname\path\to\foo#bar');
+    expect(builder.fromUri(Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_')),
+        r'_{_}_`_^_ _"_%_');
     expect(() => builder.fromUri(Uri.parse('http://dartlang.org')),
         throwsArgumentError);
   });
@@ -519,5 +523,9 @@
         Uri.parse('file:///C:/path/to/foo%23bar'));
     expect(builder.toUri(r'\\hostname\path\to\foo#bar'),
         Uri.parse('file://hostname/path/to/foo%23bar'));
+    expect(builder.toUri(r'C:\_{_}_`_^_ _"_%_'),
+        Uri.parse('file:///C:/_%7B_%7D_%60_%5E_%20_%22_%25_'));
+    expect(builder.toUri(r'_{_}_`_^_ _"_%_'),
+        Uri.parse('_%7B_%7D_%60_%5E_%20_%22_%25_'));
   });
 }
diff --git a/pkg/source_maps/lib/span.dart b/pkg/source_maps/lib/span.dart
index 1d2152d..e5057fc 100644
--- a/pkg/source_maps/lib/span.dart
+++ b/pkg/source_maps/lib/span.dart
@@ -69,7 +69,7 @@
   bool operator ==(Span other) =>
     sourceUrl == other.sourceUrl && start == other.start && end == other.end;
 
-  int get hashCode => sourceUrl.hashCode + start + (31 * (end - start));
+  int get hashCode => sourceUrl.hashCode + start.offset + (31 * length);
 
   String toString() => '<$runtimeType: $start $end $formatLocation $text>';
 }
@@ -99,6 +99,11 @@
     return offset - other.offset;
   }
 
+  bool operator ==(Location other) =>
+      sourceUrl == other.sourceUrl && offset == other.offset;
+
+  int get hashCode => sourceUrl.hashCode + offset;
+
   String toString() => '(Location $offset)';
   String get formatString => '$sourceUrl:${line + 1}:${column + 1}';
 }
diff --git a/pkg/source_maps/test/span_test.dart b/pkg/source_maps/test/span_test.dart
index c7b9cde..da3e063 100644
--- a/pkg/source_maps/test/span_test.dart
+++ b/pkg/source_maps/test/span_test.dart
@@ -316,6 +316,16 @@
     expect(file.span(8, 9, null).isIdentifier, false);
     expect(new FixedSpan('', 8, 1, 8, isIdentifier: null).isIdentifier, false);
   });
+
+  test('span/location implement == and hashCode', () {
+    expect(identical(span(10, 14), span(10, 14)), isFalse);
+    expect(span(10, 14), equals(span(10, 14)));
+    expect(span(10, 14).hashCode, span(10, 14).hashCode);
+
+    expect(identical(loc(13), loc(13)), isFalse);
+    expect(loc(13), equals(loc(13)));
+    expect(loc(13).hashCode, loc(13).hashCode);
+  });
 }
 
 class TestSpan extends Span {
diff --git a/pkg/unittest/lib/html_individual_config.dart b/pkg/unittest/lib/html_individual_config.dart
index a031b78..1c1d11e 100644
--- a/pkg/unittest/lib/html_individual_config.dart
+++ b/pkg/unittest/lib/html_individual_config.dart
@@ -30,7 +30,7 @@
 
       if(!groups.isEmpty) {
         if(groups.length > 1) {
-          throw 'More than one "group" parameter provided.';
+          throw new ArgumentError('More than one "group" parameter provided.');
         }
 
         var testGroupName = groups.single.split('=')[1];
diff --git a/pkg/unittest/lib/matcher.dart b/pkg/unittest/lib/matcher.dart
index 3263314a..09dd7eb 100644
--- a/pkg/unittest/lib/matcher.dart
+++ b/pkg/unittest/lib/matcher.dart
@@ -28,6 +28,7 @@
 library matcher;
 
 import 'dart:async';
+import 'package:meta/meta.dart';
 
 import 'src/pretty_print.dart';
 import 'src/utils.dart';
diff --git a/pkg/unittest/lib/src/config.dart b/pkg/unittest/lib/src/config.dart
index a08ee16..d79306f 100644
--- a/pkg/unittest/lib/src/config.dart
+++ b/pkg/unittest/lib/src/config.dart
@@ -194,7 +194,7 @@
       String uncaughtError) {
     // Print each test's result.
     for (final t in results) {
-      print(formatResult(t));
+      print(formatResult(t).trim());
     }
 
     // Show the summary.
diff --git a/pkg/unittest/lib/src/core_matchers.dart b/pkg/unittest/lib/src/core_matchers.dart
index 11a843c..8fb9e83e 100644
--- a/pkg/unittest/lib/src/core_matchers.dart
+++ b/pkg/unittest/lib/src/core_matchers.dart
@@ -616,6 +616,67 @@
   bool matches(item, Map matchState) => item is StateError;
 }
 
+/** A matcher for FallThroughError. */
+const isFallThroughError = const _FallThroughError();
+
+/** A matcher for functions that throw FallThroughError. */
+const Matcher throwsFallThroughError =
+    const Throws(isFallThroughError);
+
+class _FallThroughError extends TypeMatcher {
+  const _FallThroughError() : super("FallThroughError");
+  bool matches(item, Map matchState) => item is FallThroughError;
+}
+
+/** A matcher for NullThrownError. */
+const isNullThrownError = const _NullThrownError();
+
+/** A matcher for functions that throw NullThrownError. */
+const Matcher throwsNullThrownError =
+    const Throws(isNullThrownError);
+
+class _NullThrownError extends TypeMatcher {
+  const _NullThrownError() : super("NullThrownError");
+  bool matches(item, Map matchState) => item is NullThrownError;
+}
+
+/** A matcher for ConcurrentModificationError. */
+const isConcurrentModificationError = const _ConcurrentModificationError();
+
+/** A matcher for functions that throw ConcurrentModificationError. */
+const Matcher throwsConcurrentModificationError =
+    const Throws(isConcurrentModificationError);
+
+class _ConcurrentModificationError extends TypeMatcher {
+  const _ConcurrentModificationError() : super("ConcurrentModificationError");
+  bool matches(item, Map matchState) => item is ConcurrentModificationError;
+}
+
+/** A matcher for AbstractClassInstantiationError. */
+const isAbstractClassInstantiationError =
+    const _AbstractClassInstantiationError();
+
+/** A matcher for functions that throw AbstractClassInstantiationError. */
+const Matcher throwsAbstractClassInstantiationError =
+    const Throws(isAbstractClassInstantiationError);
+
+class _AbstractClassInstantiationError extends TypeMatcher {
+  const _AbstractClassInstantiationError() :
+  super("AbstractClassInstantiationError");
+  bool matches(item, Map matchState) => item is AbstractClassInstantiationError;
+}
+
+/** A matcher for CyclicInitializationError. */
+const isCyclicInitializationError = const _CyclicInitializationError();
+
+/** A matcher for functions that throw CyclicInitializationError. */
+const Matcher throwsCyclicInitializationError =
+    const Throws(isCyclicInitializationError);
+
+class _CyclicInitializationError extends TypeMatcher {
+  const _CyclicInitializationError() : super("CyclicInitializationError");
+  bool matches(item, Map matchState) => item is CyclicInitializationError;
+}
 
 /** A matcher for Map types. */
 const isMap = const _IsMap();
diff --git a/pkg/unittest/lib/src/iterable_matchers.dart b/pkg/unittest/lib/src/iterable_matchers.dart
index 9989bed..f0107be 100644
--- a/pkg/unittest/lib/src/iterable_matchers.dart
+++ b/pkg/unittest/lib/src/iterable_matchers.dart
@@ -58,15 +58,21 @@
 }
 
 /**
+ * Deprecated form of [anyElement].
+ */
+@deprecated
+Matcher someElement(matcher) => new _AnyElement(wrapMatcher(matcher));
+
+/**
  * Returns a matcher which matches [Iterable]s in which at least one
  * element matches the given [matcher].
  */
-Matcher someElement(matcher) => new _SomeElement(wrapMatcher(matcher));
+Matcher anyElement(matcher) => new _AnyElement(wrapMatcher(matcher));
 
-class _SomeElement extends _IterableMatcher {
+class _AnyElement extends _IterableMatcher {
   Matcher _matcher;
 
-  _SomeElement(this._matcher);
+  _AnyElement(this._matcher);
 
   bool matches(item, Map matchState) {
     return item.any((e) => _matcher.matches(e, matchState));
diff --git a/pkg/unittest/lib/unittest.dart b/pkg/unittest/lib/unittest.dart
index bead6f1..081439f 100644
--- a/pkg/unittest/lib/unittest.dart
+++ b/pkg/unittest/lib/unittest.dart
@@ -200,6 +200,12 @@
 final List<TestCase> testCases = new UnmodifiableListView<TestCase>(_testCases);
 
 /**
+ * Interval (in msecs) after which synchronous tests will insert an async
+ * delay to allow DOM or other updates.
+ */
+const int BREATH_INTERVAL = 200;
+
+/**
  * The set of tests to run can be restricted by using [solo_test] and
  * [solo_group].
  * As groups can be nested we use a counter to keep track of the nest level
@@ -293,6 +299,9 @@
 
 String _uncaughtErrorMessage = null;
 
+/** Time since we last gave non-sync code a chance to be scheduled. */
+var _lastBreath = new DateTime.now().millisecondsSinceEpoch;
+
 /** Test case result strings. */
 // TODO(gram) we should change these constants to use a different string
 // (so that writing 'FAIL' in the middle of a test doesn't
@@ -644,10 +653,8 @@
 
 /** Advance to the next test case. */
 void _nextTestCase() {
-  runAsync(() {
-    _currentTestCaseIndex++;
-    _nextBatch();
-  });
+  _currentTestCaseIndex++;
+  _runTest();
 }
 
 /**
@@ -691,12 +698,8 @@
 void runTests() {
   _ensureInitialized(false);
   _currentTestCaseIndex = 0;
-
   _config.onStart();
-
-  runAsync(() {
-    _nextBatch();
-  });
+  _runTest();
 }
 
 /**
@@ -740,25 +743,23 @@
 }
 
 /**
- * Runs a batch of tests, yielding whenever an asynchronous test starts
- * running. Tests will resume executing when such asynchronous test calls
- * [done] or if it fails with an exception.
+ * Runs the next test.
  */
-void _nextBatch() {
-  while (true) {
-    if (_currentTestCaseIndex >= testCases.length) {
-      _completeTests();
-      break;
-    }
+void _runTest() {
+  if (_currentTestCaseIndex >= testCases.length) {
+    _completeTests();
+  } else {
     final testCase = testCases[_currentTestCaseIndex];
     var f = _guardAsync(testCase._run, null, testCase);
-    if (f != null) {
-      f.whenComplete(() {
-        _nextTestCase(); // Schedule the next test.
-      });
-      break;
-    }
-    _currentTestCaseIndex++;
+    f.whenComplete(() {
+      var now = new DateTime.now().millisecondsSinceEpoch;
+      if ((now - _lastBreath) >= BREATH_INTERVAL) {
+        _lastBreath = now;
+        Timer.run(_nextTestCase);
+      } else {
+        runAsync(_nextTestCase); // Schedule the next test.
+      }
+    });
   }
 }
 
diff --git a/pkg/unittest/test/breath_test.dart b/pkg/unittest/test/breath_test.dart
new file mode 100644
index 0000000..84435a6
--- /dev/null
+++ b/pkg/unittest/test/breath_test.dart
@@ -0,0 +1,34 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:async';
+
+import 'package:unittest/unittest.dart';
+
+main() {
+  // Test the sync test 'breath' feature of unittest.
+
+  group('breath', () {
+    var sentinel = 0;
+    var start;
+
+    test('initial', () {
+      Timer.run(() { sentinel = 1; });
+    });
+
+    test('starve', () {
+      start = new DateTime.now().millisecondsSinceEpoch;
+      var now;
+      do {
+        expect(sentinel, 0);
+        now = new DateTime.now().millisecondsSinceEpoch;
+      } while (now - start <= BREATH_INTERVAL);
+    });
+
+    test('breathed', () {
+      expect(sentinel, 1);
+    });
+  });
+}
+
diff --git a/pkg/unittest/test/matchers_test.dart b/pkg/unittest/test/matchers_test.dart
index 51020b7..6f4338c 100644
--- a/pkg/unittest/test/matchers_test.dart
+++ b/pkg/unittest/test/matchers_test.dart
@@ -450,11 +450,11 @@
           "at index 2");
     });
 
-    test('someElement', () {
+    test('anyElement', () {
       var d = [1, 2];
       var e = [1, 1, 1];
-      shouldPass(d, someElement(2));
-      shouldFail(e, someElement(2),
+      shouldPass(d, anyElement(2));
+      shouldFail(e, anyElement(2),
           "Expected: some element <2> Actual: [1, 1, 1]");
     });
 
@@ -706,5 +706,61 @@
       shouldPass('cow', predicate((x) => x is String, "an instance of String"));
     });
   });
+
+  group('exception/error matchers', () {
+    // TODO(gram): extend this to more types; for now this is just
+    // the types being added in this CL.
+
+    // TODO: enable this test when it works.
+    // See issue 12052.
+    skip_test('throwsCyclicInitializationError', () {
+      expect(() => new Bicycle(), throwsCyclicInitializationError);
+    });
+
+    test('throwsAbstractClassInstantiationError', () {
+      expect(() => new Abstraction(), throwsAbstractClassInstantiationError);
+    });
+
+    test('throwsConcurrentModificationError', () {
+      expect(() {
+        var a = { 'foo': 'bar' };
+        for (var k in a.keys) {
+          a.remove(k);
+        }
+      }, throwsConcurrentModificationError);
+      });
+
+    test('throwsNullThrownError', () {
+      expect(() => throw null, throwsNullThrownError);
+    });
+
+    test('throwsFallThroughError', () {
+      expect(() {
+        var a = 0;
+        switch (a) {
+          case 0:
+            a += 1;
+          case 1:
+            return;
+        }
+      }, throwsFallThroughError);
+    });
+  });
+}
+
+class Bicycle {
+  static var foo = bar();
+
+  static bar() {
+    return foo + 1;
+  }
+
+  X() {
+    print(foo);
+  }
+}
+ 
+abstract class Abstraction {
+  void norealization();
 }
 
diff --git a/runtime/bin/secure_socket.cc b/runtime/bin/secure_socket.cc
index 7b5cd37..9cf0d7d 100644
--- a/runtime/bin/secure_socket.cc
+++ b/runtime/bin/secure_socket.cc
@@ -641,13 +641,14 @@
   SSLFilter* ssl_filter = static_cast<SSLFilter*>(filter);
   Dart_Handle callback = ssl_filter->bad_certificate_callback();
   if (Dart_IsNull(callback)) return SECFailure;
-
-  Dart_EnterScope();
   Dart_Handle x509_object = ssl_filter->PeerCertificate();
-  Dart_Handle result =
-      ThrowIfError(Dart_InvokeClosure(callback, 1, &x509_object));
-  bool c_result = Dart_IsBoolean(result) && DartUtils::GetBooleanValue(result);
-  Dart_ExitScope();
+  Dart_Handle result = Dart_InvokeClosure(callback, 1, &x509_object);
+  if (Dart_IsError(result)) {
+    ssl_filter->callback_error = result;
+    return SECFailure;
+  }
+  // Our wrapper is guaranteed to return a boolean.
+  bool c_result = DartUtils::GetBooleanValue(result);
   return c_result ? SECSuccess : SECFailure;
 }
 
@@ -824,6 +825,9 @@
       in_handshake_ = false;
     }
   } else {
+    if (callback_error != NULL) {
+      Dart_PropagateError(callback_error);
+    }
     PRErrorCode error = PR_GetError();
     if (error == PR_WOULD_BLOCK_ERROR) {
       if (!in_handshake_) {
diff --git a/runtime/bin/secure_socket.h b/runtime/bin/secure_socket.h
index 817be4a..304ccd5 100644
--- a/runtime/bin/secure_socket.h
+++ b/runtime/bin/secure_socket.h
@@ -47,7 +47,8 @@
   };
 
   SSLFilter()
-      : string_start_(NULL),
+      : callback_error(NULL),
+        string_start_(NULL),
         string_length_(NULL),
         handshake_complete_(NULL),
         bad_certificate_callback_(NULL),
@@ -88,6 +89,7 @@
                                 bool use_builtin_root_certificates,
                                 bool report_duplicate_initialization = true);
   static Dart_Port GetServicePort();
+  Dart_Handle callback_error;
 
  private:
   static const int kMemioBufferSize = 20 * KB;
diff --git a/runtime/lib/errors_patch.dart b/runtime/lib/errors_patch.dart
index 84ccac3..62148ac 100644
--- a/runtime/lib/errors_patch.dart
+++ b/runtime/lib/errors_patch.dart
@@ -7,8 +7,9 @@
     return Object._toString(object);
   }
 
-  // TODO(11680): implement stackTrace on Error.
-  /* patch */ StackTrace get stackTrace => null;
+  /* patch */ StackTrace get stackTrace => _stackTrace;
+
+  StackTrace _stackTrace;
 }
 
 patch class NoSuchMethodError {
diff --git a/runtime/lib/integers.cc b/runtime/lib/integers.cc
index ff6cd81..dc29527 100644
--- a/runtime/lib/integers.cc
+++ b/runtime/lib/integers.cc
@@ -9,6 +9,7 @@
 #include "vm/exceptions.h"
 #include "vm/native_entry.h"
 #include "vm/object.h"
+#include "vm/object_store.h"
 #include "vm/symbols.h"
 
 namespace dart {
@@ -318,6 +319,17 @@
 }
 
 
+DEFINE_NATIVE_ENTRY(Mint_shlFromInt, 2) {
+  // Use the preallocated out of memory exception to avoid calling
+  // into dart code or allocating any code.
+  const Instance& exception =
+      Instance::Handle(isolate->object_store()->out_of_memory());
+  Exceptions::Throw(exception);
+  UNREACHABLE();
+  return 0;
+}
+
+
 // Bigint natives.
 
 DEFINE_NATIVE_ENTRY(Bigint_bitNegate, 1) {
@@ -328,4 +340,15 @@
   return result.AsValidInteger();
 }
 
+
+DEFINE_NATIVE_ENTRY(Bigint_shlFromInt, 2) {
+  // Use the preallocated out of memory exception to avoid calling
+  // into dart code or allocating any code.
+  const Instance& exception =
+      Instance::Handle(isolate->object_store()->out_of_memory());
+  Exceptions::Throw(exception);
+  UNREACHABLE();
+  return 0;
+}
+
 }  // namespace dart
diff --git a/runtime/lib/integers.dart b/runtime/lib/integers.dart
index e57976b..c687e09 100644
--- a/runtime/lib/integers.dart
+++ b/runtime/lib/integers.dart
@@ -273,9 +273,7 @@
       return 0;
     }
   }
-  int _shlFromInt(int other) {
-    throw const OutOfMemoryError();
-  }
+  int _shlFromInt(int other) native "Mint_shlFromInt";
 }
 
 // A number that can be represented as Smi or Mint will never be represented as
@@ -298,9 +296,7 @@
       return 0;
     }
   }
-  int _shlFromInt(int other) {
-    throw const OutOfMemoryError();
-  }
+  int _shlFromInt(int other) native "Bigint_shlFromInt";
 
   int pow(int exponent) {
     throw "Bigint.pow not implemented";
diff --git a/runtime/lib/mirrors.cc b/runtime/lib/mirrors.cc
index 0d1a4af..609c38f 100644
--- a/runtime/lib/mirrors.cc
+++ b/runtime/lib/mirrors.cc
@@ -511,12 +511,7 @@
     return type;
   }
 
-  // TODO(turnidge): Why am I getting Null when I expect Object?
-  // TODO(gbracha): this is probably the root of bug 7868
-  Dart_Handle super_class = Dart_GetSuperclass(intf);
-  if (Dart_IsNull(super_class)) {
-    super_class = Dart_GetClass(CoreLib(), NewString("Object"));
-  }
+  Dart_Handle super_class = Dart_Null();
   // TODO(turnidge): Simplify code, now that default classes have been removed.
   Dart_Handle default_class = Dart_Null();
 
@@ -538,7 +533,7 @@
     Dart_Null(),  // "name"
     Dart_NewBoolean(Dart_IsClass(intf)),
     lib_mirror,
-    CreateLazyMirror(super_class),
+    super_class,
     CreateImplementsList(intf),
     CreateLazyMirror(default_class),
     constructor_map,
@@ -549,36 +544,31 @@
 }
 
 
+static RawInstance* CreateMethodMirror(const Function& func,
+                                       const Instance& owner_mirror) {
+  const Array& args = Array::Handle(Array::New(11));
+  args.SetAt(0, MirrorReference::Handle(MirrorReference::New(func)));
+  args.SetAt(1, owner_mirror);
+  args.SetAt(2, func.is_static() ? Bool::True() : Bool::False());
+  args.SetAt(3, func.is_abstract() ? Bool::True() : Bool::False());
+  args.SetAt(4, func.IsGetterFunction() ? Bool::True() : Bool::False());
+  args.SetAt(5, func.IsSetterFunction() ? Bool::True() : Bool::False());
+  args.SetAt(6, func.IsConstructor() ? Bool::True() : Bool::False());
+  // TODO(mlippautz): Implement different constructor kinds.
+  args.SetAt(7, Bool::False());
+  args.SetAt(8, Bool::False());
+  args.SetAt(9, Bool::False());
+  args.SetAt(10, Bool::False());
+  return CreateMirror(Symbols::_LocalMethodMirrorImpl(), args);
+}
+
+
 static Dart_Handle CreateMethodMirrorUsingApi(Dart_Handle func,
                                               Dart_Handle owner_mirror) {
-  // TODO(11742): Unwrapping is needed until the whole method is converted.
   Isolate* isolate = Isolate::Current();
-  const Function& func_obj = Api::UnwrapFunctionHandle(isolate, func);
-
-  Dart_Handle mirror_cls_name = NewString("_LocalMethodMirrorImpl");
-  Dart_Handle mirror_type = Dart_GetType(MirrorLib(), mirror_cls_name, 0, NULL);
-  if (Dart_IsError(mirror_type)) {
-    return mirror_type;
-  }
-
-  // TODO(turnidge): Implement constructor kinds (arguments 7 - 10).
-  Dart_Handle args[] = {
-    CreateMirrorReference(func),
-    owner_mirror,
-    CreateParameterMirrorListUsingApi(func),
-    func_obj.is_static() ? Api::True() : Api::False(),
-    func_obj.is_abstract() ? Api::True() : Api::False(),
-    func_obj.IsGetterFunction() ? Api::True() : Api::False(),
-    func_obj.IsSetterFunction() ? Api::True() : Api::False(),
-    func_obj.IsConstructor() ? Api::True() : Api::False(),
-    Api::False(),
-    Api::False(),
-    Api::False(),
-    Api::False()
-  };
-  Dart_Handle mirror =
-      Dart_New(mirror_type, Dart_Null(), ARRAY_SIZE(args), args);
-  return mirror;
+  return Api::NewHandle(isolate, CreateMethodMirror(
+      Api::UnwrapFunctionHandle(isolate, func),
+      Api::UnwrapInstanceHandle(isolate, owner_mirror)));
 }
 
 static RawInstance* CreateVariableMirror(const Field& field,
@@ -881,25 +871,6 @@
 }
 
 
-static RawInstance* CreateMethodMirror(const Function& func,
-                                       const Instance& owner_mirror) {
-  Instance& retvalue = Instance::Handle();
-  Dart_EnterScope();
-  Isolate* isolate = Isolate::Current();
-  Dart_Handle func_handle = Api::NewHandle(isolate, func.raw());
-  Dart_Handle owner_handle = Api::NewHandle(isolate, owner_mirror.raw());
-  // TODO(11742): At some point the handle calls will be replaced by inlined
-  // functionality.
-  Dart_Handle result = CreateMethodMirrorUsingApi(func_handle, owner_handle);
-  if (Dart_IsError(result)) {
-    Dart_PropagateError(result);
-  }
-  retvalue ^= Api::UnwrapHandle(result);
-  Dart_ExitScope();
-  return retvalue.raw();
-}
-
-
 static RawInstance* CreateTypeMirror(const AbstractType& type) {
   ASSERT(!type.IsMalformed());
   if (type.HasResolvedTypeClass()) {
@@ -981,34 +952,6 @@
 }
 
 
-DEFINE_NATIVE_ENTRY(DeclarationMirror_metadata, 1) {
-  const MirrorReference& decl_ref =
-      MirrorReference::CheckedHandle(arguments->NativeArgAt(0));
-  const Object& decl = Object::Handle(decl_ref.referent());
-
-  Class& klass = Class::Handle();
-  if (decl.IsClass()) {
-    klass ^= decl.raw();
-  } else if (decl.IsFunction()) {
-    klass = Function::Cast(decl).origin();
-  } else if (decl.IsField()) {
-    klass = Field::Cast(decl).origin();
-  } else {
-    return Object::empty_array().raw();
-  }
-
-  const Library& library = Library::Handle(klass.library());
-  return library.GetMetadata(decl);
-}
-
-
-void HandleMirrorsMessage(Isolate* isolate,
-                          Dart_Port reply_port,
-                          const Instance& message) {
-  UNIMPLEMENTED();
-}
-
-
 static void ThrowMirroredCompilationError(const String& message) {
   Array& args = Array::Handle(Array::New(1));
   args.SetAt(0, message);
@@ -1031,6 +974,38 @@
 }
 
 
+DEFINE_NATIVE_ENTRY(DeclarationMirror_metadata, 1) {
+  const MirrorReference& decl_ref =
+      MirrorReference::CheckedHandle(arguments->NativeArgAt(0));
+  const Object& decl = Object::Handle(decl_ref.referent());
+
+  Class& klass = Class::Handle();
+  if (decl.IsClass()) {
+    klass ^= decl.raw();
+  } else if (decl.IsFunction()) {
+    klass = Function::Cast(decl).origin();
+  } else if (decl.IsField()) {
+    klass = Field::Cast(decl).origin();
+  } else {
+    return Object::empty_array().raw();
+  }
+
+  const Library& library = Library::Handle(klass.library());
+  const Object& metadata = Object::Handle(library.GetMetadata(decl));
+  if (metadata.IsError()) {
+    ThrowInvokeError(Error::Cast(metadata));
+  }
+  return metadata.raw();
+}
+
+
+void HandleMirrorsMessage(Isolate* isolate,
+                          Dart_Port reply_port,
+                          const Instance& message) {
+  UNIMPLEMENTED();
+}
+
+
 static bool FieldIsUninitialized(const Field& field) {
   ASSERT(!field.IsNull());
 
@@ -1057,6 +1032,12 @@
 }
 
 
+DEFINE_NATIVE_ENTRY(ClassMirror_supertype, 1) {
+  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
+  const Class& klass = Class::Handle(ref.GetClassReferent());
+  return klass.super_type();
+}
+
 DEFINE_NATIVE_ENTRY(ClassMirror_members, 2) {
   GET_NON_NULL_NATIVE_ARGUMENT(Instance,
                                owner_mirror,
@@ -1748,6 +1729,13 @@
 }
 
 
+DEFINE_NATIVE_ENTRY(MethodMirror_parameters, 1) {
+  GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
+  const Function& func = Function::Handle(ref.GetFunctionReferent());
+  return CreateParameterMirrorList(func);
+}
+
+
 DEFINE_NATIVE_ENTRY(MethodMirror_return_type, 1) {
   GET_NON_NULL_NATIVE_ARGUMENT(MirrorReference, ref, arguments->NativeArgAt(0));
   const Function& func = Function::Handle(ref.GetFunctionReferent());
diff --git a/runtime/lib/mirrors_impl.dart b/runtime/lib/mirrors_impl.dart
index acd568d..97d9262 100644
--- a/runtime/lib/mirrors_impl.dart
+++ b/runtime/lib/mirrors_impl.dart
@@ -20,7 +20,7 @@
 }
 
 Map _makeMemberMap(List mirrors) {
-  Map<Symbol, dynamic> result = new Map();
+  Map result = new Map<Symbol, dynamic>();
   mirrors.forEach((mirror) => result[mirror.simpleName] = mirror);
   return result;
 }
@@ -450,6 +450,14 @@
 
   var _superclass;
   ClassMirror get superclass {
+    if (_superclass == null) {
+      Type supertype = _supertype(_reflectee);
+      if (supertype == null) {
+        // Object has no superclass.
+        return null;
+      }
+      _superclass = reflectClass(supertype);
+    }
     if (_superclass is! Mirror) {
       _superclass = _superclass.resolve(mirrors);
     }
@@ -592,6 +600,9 @@
   static _library(reflectee)
       native "ClassMirror_library";
 
+  static _supertype(reflectee)
+      native "ClassMirror_supertype";
+
   _computeMembers(reflectee)
       native "ClassMirror_members";
 
@@ -636,7 +647,7 @@
               const {},
               const {});
 
-  Map<Symbol, Mirror> get members => const {};
+  Map<Symbol, Mirror> get members => new Map<Symbol,Mirror>();
 
   var _returnType;
   TypeMirror get returnType {
@@ -900,7 +911,6 @@
     implements MethodMirror {
   _LocalMethodMirrorImpl(reflectee,
                          this._owner,
-                         this.parameters,
                          this.isStatic,
                          this.isAbstract,
                          this.isGetter,
@@ -965,7 +975,13 @@
     return _returnType;
   }
 
-  final List<ParameterMirror> parameters;
+  List<ParameterMirror> _parameters = null;
+  List<ParameterMirror> get parameters {
+    if (_parameters == null) {
+      _parameters = _MethodMirror_parameters(_reflectee);
+    }
+    return _parameters;
+  }
 
   final bool isStatic;
   final bool isAbstract;
@@ -1017,6 +1033,9 @@
 
   static dynamic _MethodMirror_return_type(reflectee)
       native "MethodMirror_return_type";
+
+  static List<MethodMirror> _MethodMirror_parameters(reflectee)
+      native "MethodMirror_parameters";
 }
 
 class _LocalVariableMirrorImpl extends _LocalDeclarationMirrorImpl
@@ -1152,6 +1171,8 @@
     }
     return this.simpleName == other.simpleName;
   }
+
+  int get hashCode => simpleName.hashCode;
 }
 
 class _Mirrors {
diff --git a/runtime/tests/vm/dart/isolate_mirror_local_test.dart b/runtime/tests/vm/dart/isolate_mirror_local_test.dart
index cc7d3e6..54241c4 100644
--- a/runtime/tests/vm/dart/isolate_mirror_local_test.dart
+++ b/runtime/tests/vm/dart/isolate_mirror_local_test.dart
@@ -407,6 +407,7 @@
 class MyInterface {
 }
 
+@notDefined
 class MyClass extends MySuperClass implements MyInterface {
   MyClass(this.value) {}
   MyClass.named() {}
@@ -445,6 +446,7 @@
   Expect.equals(const Symbol('isolate_mirror_local_test'), cls.owner.simpleName);
   Expect.isTrue(cls.isClass);
   Expect.equals(const Symbol('MyInterface'), cls.superinterfaces[0].simpleName);
+  Expect.throws(() => cls.metadata, (e) => e is MirroredCompilationError, 'Bad metadata');
   // TODO(ahe): toString() test disabled for now as Symbols are 100% opaque.
   // Expect.equals("ClassMirror on 'MyClass'", cls.toString());
 
diff --git a/runtime/vm/benchmark_test.cc b/runtime/vm/benchmark_test.cc
index ec82fc9..888efb4 100644
--- a/runtime/vm/benchmark_test.cc
+++ b/runtime/vm/benchmark_test.cc
@@ -465,11 +465,10 @@
       "}\n";
 
   Dart_Handle lib = TestCase::LoadTestScript(kScriptChars, NULL);
-  Dart_Handle args[0];
 
   Timer timer(true, "currentMirrorSystem() benchmark");
   timer.Start();
-  Dart_Invoke(lib, NewString("benchmark"), 0, args);
+  Dart_Invoke(lib, NewString("benchmark"), 0, NULL);
   timer.Stop();
   int64_t elapsed_time = timer.TotalElapsedTime();
   benchmark->set_score(elapsed_time);
diff --git a/runtime/vm/bootstrap_natives.h b/runtime/vm/bootstrap_natives.h
index edb6211..7223916 100644
--- a/runtime/vm/bootstrap_natives.h
+++ b/runtime/vm/bootstrap_natives.h
@@ -43,7 +43,9 @@
   V(Smi_shrFromInt, 2)                                                         \
   V(Smi_bitNegate, 1)                                                          \
   V(Mint_bitNegate, 1)                                                         \
+  V(Mint_shlFromInt, 2)                                                        \
   V(Bigint_bitNegate, 1)                                                       \
+  V(Bigint_shlFromInt, 2)                                                      \
   V(Double_getIsNegative, 1)                                                   \
   V(Double_getIsInfinite, 1)                                                   \
   V(Double_getIsNaN, 1)                                                        \
@@ -247,6 +249,7 @@
   V(ClosureMirror_apply, 2)                                                    \
   V(ClassMirror_name, 1)                                                       \
   V(ClassMirror_library, 1)                                                    \
+  V(ClassMirror_supertype, 1)                                                  \
   V(ClassMirror_members, 2)                                                    \
   V(LibraryMirror_members, 2)                                                  \
   V(ClassMirror_invoke, 4)                                                     \
@@ -259,6 +262,7 @@
   V(DeclarationMirror_metadata, 1)                                             \
   V(MethodMirror_name, 1)                                                      \
   V(MethodMirror_owner, 1)                                                     \
+  V(MethodMirror_parameters, 1)                                                \
   V(MethodMirror_return_type, 1)                                               \
   V(ParameterMirror_type, 2)                                                   \
   V(VariableMirror_type, 1)                                                    \
diff --git a/runtime/vm/exceptions.cc b/runtime/vm/exceptions.cc
index af5ca32..0f7b8a4 100644
--- a/runtime/vm/exceptions.cc
+++ b/runtime/vm/exceptions.cc
@@ -46,35 +46,72 @@
 
   virtual void AddFrame(const Function& func,
                         const Code& code,
-                        const Smi& offset) = 0;
+                        const Smi& offset,
+                        bool is_catch_frame) = 0;
+
+  virtual bool FullStacktrace() const = 0;
 };
 
 
 class RegularStacktraceBuilder : public StacktraceBuilder {
  public:
-  RegularStacktraceBuilder()
+  explicit RegularStacktraceBuilder(bool full_stacktrace)
       : func_list_(GrowableObjectArray::Handle(GrowableObjectArray::New())),
         code_list_(GrowableObjectArray::Handle(GrowableObjectArray::New())),
         pc_offset_list_(
-            GrowableObjectArray::Handle(GrowableObjectArray::New())) { }
+            GrowableObjectArray::Handle(GrowableObjectArray::New())),
+        catch_func_list_(
+            full_stacktrace ?
+                GrowableObjectArray::Handle(GrowableObjectArray::New()) :
+                GrowableObjectArray::Handle()),
+        catch_code_list_(
+            full_stacktrace ?
+                GrowableObjectArray::Handle(GrowableObjectArray::New()) :
+                GrowableObjectArray::Handle()),
+        catch_pc_offset_list_(
+            full_stacktrace ?
+                GrowableObjectArray::Handle(GrowableObjectArray::New()) :
+                GrowableObjectArray::Handle()),
+        full_stacktrace_(full_stacktrace) { }
   ~RegularStacktraceBuilder() { }
 
   const GrowableObjectArray& func_list() const { return func_list_; }
   const GrowableObjectArray& code_list() const { return code_list_; }
   const GrowableObjectArray& pc_offset_list() const { return pc_offset_list_; }
+  const GrowableObjectArray& catch_func_list() const {
+    return catch_func_list_;
+  }
+  const GrowableObjectArray& catch_code_list() const {
+    return catch_code_list_;
+  }
+  const GrowableObjectArray& catch_pc_offset_list() const {
+    return catch_pc_offset_list_;
+  }
+  virtual bool FullStacktrace() const { return full_stacktrace_; }
 
   virtual void AddFrame(const Function& func,
                         const Code& code,
-                        const Smi& offset) {
-    func_list_.Add(func);
-    code_list_.Add(code);
-    pc_offset_list_.Add(offset);
+                        const Smi& offset,
+                        bool is_catch_frame) {
+    if (is_catch_frame) {
+      catch_func_list_.Add(func);
+      catch_code_list_.Add(code);
+      catch_pc_offset_list_.Add(offset);
+    } else {
+      func_list_.Add(func);
+      code_list_.Add(code);
+      pc_offset_list_.Add(offset);
+    }
   }
 
  private:
   const GrowableObjectArray& func_list_;
   const GrowableObjectArray& code_list_;
   const GrowableObjectArray& pc_offset_list_;
+  const GrowableObjectArray& catch_func_list_;
+  const GrowableObjectArray& catch_code_list_;
+  const GrowableObjectArray& catch_pc_offset_list_;
+  bool full_stacktrace_;
 
   DISALLOW_COPY_AND_ASSIGN(RegularStacktraceBuilder);
 };
@@ -92,7 +129,10 @@
 
   virtual void AddFrame(const Function& func,
                         const Code& code,
-                        const Smi& offset);
+                        const Smi& offset,
+                        bool is_catch_frame);
+
+  virtual bool FullStacktrace() const { return false; }
 
  private:
   static const int kNumTopframes = 3;
@@ -106,7 +146,8 @@
 
 void PreallocatedStacktraceBuilder::AddFrame(const Function& func,
                                              const Code& code,
-                                             const Smi& offset) {
+                                             const Smi& offset,
+                                             bool is_catch_frame) {
   if (cur_index_ >= Stacktrace::kPreallocatedStackdepth) {
     // The number of frames is overflowing the preallocated stack trace object.
     Function& frame_func = Function::Handle();
@@ -160,6 +201,7 @@
   Function& func = Function::Handle();
   Code& code = Code::Handle();
   Smi& offset = Smi::Handle();
+  bool handler_found = false;
   while (!frame->IsEntryFrame()) {
     if (frame->IsDartFrame()) {
       code = frame->LookupDartCode();
@@ -175,30 +217,35 @@
           ASSERT(pc < (code.EntryPoint() + code.Size()));
           if (ShouldShowFunction(func)) {
             offset = Smi::New(pc - code.EntryPoint());
-            builder->AddFrame(func, code, offset);
+            builder->AddFrame(func, code, offset, handler_found);
           }
         }
       } else {
         offset = Smi::New(frame->pc() - code.EntryPoint());
         func = code.function();
         if (ShouldShowFunction(func)) {
-          builder->AddFrame(func, code, offset);
+          builder->AddFrame(func, code, offset, handler_found);
         }
       }
-      if (frame->FindExceptionHandler(handler_pc)) {
+      if (!handler_found && frame->FindExceptionHandler(handler_pc)) {
         *handler_sp = frame->sp();
         *handler_fp = frame->fp();
-        return true;
+        handler_found = true;
+        if (!builder->FullStacktrace()) {
+          return handler_found;
+        }
       }
     }
     frame = frames.NextFrame();
     ASSERT(frame != NULL);
   }
   ASSERT(frame->IsEntryFrame());
-  *handler_pc = frame->pc();
-  *handler_sp = frame->sp();
-  *handler_fp = frame->fp();
-  return false;
+  if (!handler_found) {
+    *handler_pc = frame->pc();
+    *handler_sp = frame->sp();
+    *handler_fp = frame->fp();
+  }
+  return handler_found;
 }
 
 
@@ -268,6 +315,29 @@
 }
 
 
+static RawField* LookupStacktraceField(const Instance& instance) {
+  Isolate* isolate = Isolate::Current();
+  Class& error_class = Class::Handle(isolate,
+                                     isolate->object_store()->error_class());
+  if (error_class.IsNull()) {
+    const Library& core_lib = Library::Handle(isolate, Library::CoreLibrary());
+    error_class = core_lib.LookupClass(Symbols::Error(), NULL);
+    ASSERT(!error_class.IsNull());
+    isolate->object_store()->set_error_class(error_class);
+  }
+  const Class& instance_class = Class::Handle(isolate, instance.clazz());
+  Error& malformed_type_error = Error::Handle(isolate);
+  if (instance_class.IsSubtypeOf(Object::null_abstract_type_arguments(),
+                                 error_class,
+                                 Object::null_abstract_type_arguments(),
+                                 &malformed_type_error)) {
+    ASSERT(malformed_type_error.IsNull());
+    return error_class.LookupInstanceField(Symbols::_stackTrace());
+  }
+  return Field::null();
+}
+
+
 static void ThrowExceptionHelper(const Instance& incoming_exception,
                                  const Instance& existing_stacktrace) {
   bool use_preallocated_stacktrace = false;
@@ -293,23 +363,47 @@
                                           &handler_fp,
                                           &frame_builder);
   } else {
-    RegularStacktraceBuilder frame_builder;
+    const Field& stacktrace_field =
+        Field::Handle(LookupStacktraceField(incoming_exception));
+    bool full_stacktrace = !stacktrace_field.IsNull();
+    RegularStacktraceBuilder frame_builder(full_stacktrace);
     handler_exists = FindExceptionHandler(&handler_pc,
                                           &handler_sp,
                                           &handler_fp,
                                           &frame_builder);
+    // Create arrays for function, code and pc_offset triplet of each frame.
+    const Array& func_array =
+        Array::Handle(isolate, Array::MakeArray(frame_builder.func_list()));
+    const Array& code_array =
+        Array::Handle(isolate, Array::MakeArray(frame_builder.code_list()));
+    const Array& pc_offset_array =
+        Array::Handle(isolate,
+                      Array::MakeArray(frame_builder.pc_offset_list()));
+    if (!stacktrace_field.IsNull()) {
+      // This is an error object and we need to capture the full stack trace
+      // here implicitly, so we set up the stack trace. The stack trace field
+      // is set only once, it is not overriden.
+      const Array& catch_func_array =
+          Array::Handle(isolate,
+                        Array::MakeArray(frame_builder.catch_func_list()));
+      const Array& catch_code_array =
+          Array::Handle(isolate,
+                        Array::MakeArray(frame_builder.catch_code_list()));
+      const Array& catch_pc_offset_array =
+          Array::Handle(isolate,
+                        Array::MakeArray(frame_builder.catch_pc_offset_list()));
+      stacktrace = Stacktrace::New(func_array, code_array, pc_offset_array);
+      stacktrace.SetCatchStacktrace(catch_func_array,
+                                    catch_code_array,
+                                    catch_pc_offset_array);
+      if (incoming_exception.GetField(stacktrace_field) == Object::null()) {
+        incoming_exception.SetField(stacktrace_field, stacktrace);
+      }
+    }
     // TODO(5411263): At some point we can optimize by figuring out if a
     // stack trace is needed based on whether the catch code specifies a
     // stack trace object or there is a rethrow in the catch clause.
-    if (frame_builder.pc_offset_list().Length() != 0) {
-      // Create arrays for function, code and pc_offset triplet for each frame.
-      const Array& func_array =
-          Array::Handle(isolate, Array::MakeArray(frame_builder.func_list()));
-      const Array& code_array =
-          Array::Handle(isolate, Array::MakeArray(frame_builder.code_list()));
-      const Array& pc_offset_array =
-          Array::Handle(isolate,
-                        Array::MakeArray(frame_builder.pc_offset_list()));
+    if (pc_offset_array.Length() != 0) {
       if (existing_stacktrace.IsNull()) {
         stacktrace = Stacktrace::New(func_array, code_array, pc_offset_array);
       } else {
@@ -402,18 +496,6 @@
 }
 
 
-// Assign the value to the field given by its name in the given instance.
-void Exceptions::SetField(const Instance& instance,
-                          const Class& cls,
-                          const char* field_name,
-                          const Object& value) {
-  const Field& field = Field::Handle(cls.LookupInstanceField(
-      String::Handle(Symbols::New(field_name))));
-  ASSERT(!field.IsNull());
-  instance.SetField(field, value);
-}
-
-
 // Allocate, initialize, and throw a TypeError.
 void Exceptions::CreateAndThrowTypeError(intptr_t location,
                                          const String& src_type_name,
@@ -548,6 +630,8 @@
   const String* constructor_name = &Symbols::Dot();
   switch (type) {
     case kNone:
+    case kStackOverflow:
+    case kOutOfMemory:
       UNREACHABLE();
       break;
     case kRange:
@@ -571,14 +655,6 @@
       library = Library::CoreLibrary();
       class_name = &Symbols::UnsupportedError();
       break;
-    case kStackOverflow:
-      library = Library::CoreLibrary();
-      class_name = &Symbols::StackOverflowError();
-      break;
-    case kOutOfMemory:
-      library = Library::CoreLibrary();
-      class_name = &Symbols::OutOfMemoryError();
-      break;
     case kInternalError:
       library = Library::CoreLibrary();
       class_name = &Symbols::InternalError();
diff --git a/runtime/vm/exceptions.h b/runtime/vm/exceptions.h
index e3b3c12..04623a4 100644
--- a/runtime/vm/exceptions.h
+++ b/runtime/vm/exceptions.h
@@ -33,10 +33,6 @@
   // Helpers to create and throw errors.
   static RawScript* GetCallerScript(DartFrameIterator* iterator);
   static RawInstance* NewInstance(const char* class_name);
-  static void SetField(const Instance& instance,
-                       const Class& cls,
-                       const char* field_name,
-                       const Object& value);
   static void CreateAndThrowTypeError(intptr_t location,
                                       const String& src_type_name,
                                       const String& dst_type_name,
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 8fd9d02..7861810 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -36,11 +36,21 @@
 DEFINE_FLAG(bool, use_cha, true, "Use class hierarchy analysis.");
 DEFINE_FLAG(bool, trace_load_optimization, false,
     "Print live sets for load optimization pass.");
+DEFINE_FLAG(bool, enable_simd_inline, true,
+    "Enable inlining of SIMD related method calls.");
 DECLARE_FLAG(bool, eliminate_type_checks);
 DECLARE_FLAG(bool, enable_type_checks);
 DECLARE_FLAG(bool, trace_type_check_elimination);
 
 
+static bool ShouldInlineSimd() {
+#if defined(TARGET_ARCH_MIPS)
+  return false;
+#endif
+  return FLAG_enable_simd_inline;
+}
+
+
 // Optimize instance calls using ICData.
 void FlowGraphOptimizer::ApplyICData() {
   VisitBlocks();
@@ -440,10 +450,14 @@
       unboxed = kUnboxedDouble;
       break;
     case kFloat32x4Cid:
-      unboxed = kUnboxedFloat32x4;
+      if (ShouldInlineSimd()) {
+        unboxed = kUnboxedFloat32x4;
+      }
       break;
     case kUint32x4Cid:
-      unboxed = kUnboxedUint32x4;
+      if (ShouldInlineSimd()) {
+        unboxed = kUnboxedUint32x4;
+      }
       break;
   }
 
@@ -820,6 +834,9 @@
       break;
     }
     case kTypedDataFloat32x4ArrayCid: {
+      if (!ShouldInlineSimd()) {
+        return false;
+      }
       // Check that value is always a Float32x4.
       value_check = call->ic_data()->AsUnaryClassChecksForArgNr(2);
       if ((value_check.NumberOfChecks() != 1) ||
@@ -952,7 +969,6 @@
     case kGrowableObjectArrayCid:
     case kTypedDataFloat32ArrayCid:
     case kTypedDataFloat64ArrayCid:
-    case kTypedDataFloat32x4ArrayCid:
     case kTypedDataInt8ArrayCid:
     case kTypedDataUint8ArrayCid:
     case kTypedDataUint8ClampedArrayCid:
@@ -961,6 +977,11 @@
     case kTypedDataInt16ArrayCid:
     case kTypedDataUint16ArrayCid:
       break;
+    case kTypedDataFloat32x4ArrayCid:
+      if (!ShouldInlineSimd()) {
+        return false;
+      }
+      break;
     case kTypedDataInt32ArrayCid:
     case kTypedDataUint32ArrayCid: {
         if (!CanUnboxInt32()) return false;
@@ -1035,7 +1056,7 @@
       }
       break;
     case Token::kDIV:
-      if (ShouldSpecializeForDouble(ic_data)) {
+      if (ShouldSpecializeForDouble(ic_data) || HasOnlyTwoSmis(ic_data)) {
         operands_type = kDoubleCid;
       } else if (HasOnlyTwoFloat32x4s(ic_data)) {
         operands_type = kFloat32x4Cid;
@@ -1102,13 +1123,16 @@
   Definition* right = call->ArgumentAt(1);
   if (operands_type == kDoubleCid) {
     // Check that either left or right are not a smi.  Result of a
-    // binary operation with two smis is a smi not a double.
-    InsertBefore(call,
-                 new CheckEitherNonSmiInstr(new Value(left),
-                                            new Value(right),
-                                            call->deopt_id()),
-                 call->env(),
-                 Definition::kEffect);
+    // binary operation with two smis is a smi not a double, except '/' which
+    // returns a double for two smis.
+    if (op_kind != Token::kDIV) {
+      InsertBefore(call,
+                   new CheckEitherNonSmiInstr(new Value(left),
+                                              new Value(right),
+                                              call->deopt_id()),
+                   call->env(),
+                   Definition::kEffect);
+    }
 
     BinaryDoubleOpInstr* double_bin_op =
         new BinaryDoubleOpInstr(op_kind, new Value(left), new Value(right),
@@ -1128,45 +1152,9 @@
       ReplaceCall(call, bin_op);
     }
   } else if (operands_type == kFloat32x4Cid) {
-    // Type check left.
-    AddCheckClass(left,
-                  ICData::ZoneHandle(
-                      call->ic_data()->AsUnaryClassChecksForArgNr(0)),
-                  call->deopt_id(),
-                  call->env(),
-                  call);
-    // Type check right.
-    AddCheckClass(right,
-                  ICData::ZoneHandle(
-                      call->ic_data()->AsUnaryClassChecksForArgNr(1)),
-                  call->deopt_id(),
-                  call->env(),
-                  call);
-    // Replace call.
-    BinaryFloat32x4OpInstr* float32x4_bin_op =
-        new BinaryFloat32x4OpInstr(op_kind, new Value(left), new Value(right),
-                                   call->deopt_id());
-    ReplaceCall(call, float32x4_bin_op);
+    return InlineFloat32x4BinaryOp(call, op_kind);
   } else if (operands_type == kUint32x4Cid) {
-    // Type check left.
-    AddCheckClass(left,
-                  ICData::ZoneHandle(
-                      call->ic_data()->AsUnaryClassChecksForArgNr(0)),
-                  call->deopt_id(),
-                  call->env(),
-                  call);
-    // Type check right.
-    AddCheckClass(right,
-                  ICData::ZoneHandle(
-                      call->ic_data()->AsUnaryClassChecksForArgNr(1)),
-                  call->deopt_id(),
-                  call->env(),
-                  call);
-    // Replace call.
-    BinaryUint32x4OpInstr* uint32x4_bin_op =
-        new BinaryUint32x4OpInstr(op_kind, new Value(left), new Value(right),
-                                  call->deopt_id());
-    ReplaceCall(call, uint32x4_bin_op);
+    return InlineUint32x4BinaryOp(call, op_kind);
   } else if (op_kind == Token::kMOD) {
     // TODO(vegorov): implement fast path code for modulo.
     ASSERT(operands_type == kSmiCid);
@@ -1462,6 +1450,9 @@
 
 bool FlowGraphOptimizer::InlineFloat32x4Getter(InstanceCallInstr* call,
                                                MethodRecognizer::Kind getter) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
   AddCheckClass(call->ArgumentAt(0),
                 ICData::ZoneHandle(
                     call->ic_data()->AsUnaryClassChecksForArgNr(0)),
@@ -1493,6 +1484,9 @@
 
 bool FlowGraphOptimizer::InlineUint32x4Getter(InstanceCallInstr* call,
                                               MethodRecognizer::Kind getter) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
   AddCheckClass(call->ArgumentAt(0),
                 ICData::ZoneHandle(
                     call->ic_data()->AsUnaryClassChecksForArgNr(0)),
@@ -1508,6 +1502,69 @@
 }
 
 
+bool FlowGraphOptimizer::InlineFloat32x4BinaryOp(InstanceCallInstr* call,
+                                                 Token::Kind op_kind) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
+  ASSERT(call->ArgumentCount() == 2);
+  Definition* left = call->ArgumentAt(0);
+  Definition* right = call->ArgumentAt(1);
+  // Type check left.
+  AddCheckClass(left,
+                ICData::ZoneHandle(
+                    call->ic_data()->AsUnaryClassChecksForArgNr(0)),
+                call->deopt_id(),
+                call->env(),
+                call);
+  // Type check right.
+  AddCheckClass(right,
+                ICData::ZoneHandle(
+                    call->ic_data()->AsUnaryClassChecksForArgNr(1)),
+                call->deopt_id(),
+                call->env(),
+                call);
+  // Replace call.
+  BinaryFloat32x4OpInstr* float32x4_bin_op =
+      new BinaryFloat32x4OpInstr(op_kind, new Value(left), new Value(right),
+                                 call->deopt_id());
+  ReplaceCall(call, float32x4_bin_op);
+
+  return true;
+}
+
+
+bool FlowGraphOptimizer::InlineUint32x4BinaryOp(InstanceCallInstr* call,
+                                                Token::Kind op_kind) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
+  ASSERT(call->ArgumentCount() == 2);
+  Definition* left = call->ArgumentAt(0);
+  Definition* right = call->ArgumentAt(1);
+  // Type check left.
+  AddCheckClass(left,
+                ICData::ZoneHandle(
+                    call->ic_data()->AsUnaryClassChecksForArgNr(0)),
+                call->deopt_id(),
+                call->env(),
+                call);
+  // Type check right.
+  AddCheckClass(right,
+                ICData::ZoneHandle(
+                    call->ic_data()->AsUnaryClassChecksForArgNr(1)),
+                call->deopt_id(),
+                call->env(),
+                call);
+  // Replace call.
+  BinaryUint32x4OpInstr* uint32x4_bin_op =
+      new BinaryUint32x4OpInstr(op_kind, new Value(left), new Value(right),
+                                call->deopt_id());
+  ReplaceCall(call, uint32x4_bin_op);
+  return true;
+}
+
+
 // Only unique implicit instance getters can be currently handled.
 bool FlowGraphOptimizer::TryInlineInstanceGetter(InstanceCallInstr* call) {
   ASSERT(call->HasICData());
@@ -1898,9 +1955,62 @@
 }
 
 
+bool FlowGraphOptimizer::TryInlineFloat32x4Constructor(
+    StaticCallInstr* call,
+    MethodRecognizer::Kind recognized_kind) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
+  if (recognized_kind == MethodRecognizer::kFloat32x4Zero) {
+    Float32x4ZeroInstr* zero = new Float32x4ZeroInstr(call->deopt_id());
+    ReplaceCall(call, zero);
+    return true;
+  } else if (recognized_kind == MethodRecognizer::kFloat32x4Splat) {
+    Float32x4SplatInstr* splat =
+        new Float32x4SplatInstr(new Value(call->ArgumentAt(1)),
+                                call->deopt_id());
+    ReplaceCall(call, splat);
+    return true;
+  } else if (recognized_kind == MethodRecognizer::kFloat32x4Constructor) {
+    Float32x4ConstructorInstr* con =
+        new Float32x4ConstructorInstr(new Value(call->ArgumentAt(1)),
+                                      new Value(call->ArgumentAt(2)),
+                                      new Value(call->ArgumentAt(3)),
+                                      new Value(call->ArgumentAt(4)),
+                                      call->deopt_id());
+    ReplaceCall(call, con);
+    return true;
+  }
+  return false;
+}
+
+
+bool FlowGraphOptimizer::TryInlineUint32x4Constructor(
+    StaticCallInstr* call,
+    MethodRecognizer::Kind recognized_kind) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
+  if (recognized_kind == MethodRecognizer::kUint32x4BoolConstructor) {
+    Uint32x4BoolConstructorInstr* con = new Uint32x4BoolConstructorInstr(
+        new Value(call->ArgumentAt(1)),
+        new Value(call->ArgumentAt(2)),
+        new Value(call->ArgumentAt(3)),
+        new Value(call->ArgumentAt(4)),
+        call->deopt_id());
+    ReplaceCall(call, con);
+    return true;
+  }
+  return false;
+}
+
+
 bool FlowGraphOptimizer::TryInlineFloat32x4Method(
     InstanceCallInstr* call,
     MethodRecognizer::Kind recognized_kind) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
   ASSERT(call->HasICData());
   switch (recognized_kind) {
     case MethodRecognizer::kFloat32x4Equal:
@@ -2057,6 +2167,9 @@
 bool FlowGraphOptimizer::TryInlineUint32x4Method(
     InstanceCallInstr* call,
     MethodRecognizer::Kind recognized_kind) {
+  if (!ShouldInlineSimd()) {
+    return false;
+  }
   ASSERT(call->HasICData());
   switch (recognized_kind) {
     case MethodRecognizer::kUint32x4Select: {
@@ -2123,6 +2236,10 @@
     InstanceCallInstr* call,
     intptr_t receiver_cid,
     intptr_t view_cid) {
+  if ((view_cid == kTypedDataFloat32x4ArrayCid) && !ShouldInlineSimd()) {
+    return false;
+  }
+
   Definition* array = call->ArgumentAt(0);
   PrepareByteArrayViewOp(call, receiver_cid, view_cid, &array);
 
@@ -2150,6 +2267,9 @@
     InstanceCallInstr* call,
     intptr_t receiver_cid,
     intptr_t view_cid) {
+  if ((view_cid == kTypedDataFloat32x4ArrayCid) && !ShouldInlineSimd()) {
+    return false;
+  }
   Definition* array = call->ArgumentAt(0);
   PrepareByteArrayViewOp(call, receiver_cid, view_cid, &array);
   ICData& value_check = ICData::ZoneHandle();
@@ -2500,30 +2620,12 @@
     MathSqrtInstr* sqrt =
         new MathSqrtInstr(new Value(call->ArgumentAt(0)), call->deopt_id());
     ReplaceCall(call, sqrt);
-  } else if (recognized_kind == MethodRecognizer::kFloat32x4Zero) {
-    Float32x4ZeroInstr* zero = new Float32x4ZeroInstr(call->deopt_id());
-    ReplaceCall(call, zero);
-  } else if (recognized_kind == MethodRecognizer::kFloat32x4Splat) {
-    Float32x4SplatInstr* splat =
-        new Float32x4SplatInstr(new Value(call->ArgumentAt(1)),
-                                call->deopt_id());
-    ReplaceCall(call, splat);
-  } else if (recognized_kind == MethodRecognizer::kFloat32x4Constructor) {
-    Float32x4ConstructorInstr* con =
-        new Float32x4ConstructorInstr(new Value(call->ArgumentAt(1)),
-                                      new Value(call->ArgumentAt(2)),
-                                      new Value(call->ArgumentAt(3)),
-                                      new Value(call->ArgumentAt(4)),
-                                      call->deopt_id());
-    ReplaceCall(call, con);
+  } else if ((recognized_kind == MethodRecognizer::kFloat32x4Zero) ||
+             (recognized_kind == MethodRecognizer::kFloat32x4Splat) ||
+             (recognized_kind == MethodRecognizer::kFloat32x4Constructor)) {
+    TryInlineFloat32x4Constructor(call, recognized_kind);
   } else if (recognized_kind == MethodRecognizer::kUint32x4BoolConstructor) {
-    Uint32x4BoolConstructorInstr* con = new Uint32x4BoolConstructorInstr(
-        new Value(call->ArgumentAt(1)),
-        new Value(call->ArgumentAt(2)),
-        new Value(call->ArgumentAt(3)),
-        new Value(call->ArgumentAt(4)),
-        call->deopt_id());
-    ReplaceCall(call, con);
+    TryInlineUint32x4Constructor(call, recognized_kind);
   } else if (recognized_kind == MethodRecognizer::kObjectConstructor) {
     // Remove the original push arguments.
     for (intptr_t i = 0; i < call->ArgumentCount(); ++i) {
diff --git a/runtime/vm/flow_graph_optimizer.h b/runtime/vm/flow_graph_optimizer.h
index ce18d6b..1f18c51 100644
--- a/runtime/vm/flow_graph_optimizer.h
+++ b/runtime/vm/flow_graph_optimizer.h
@@ -87,6 +87,10 @@
                                const ICData& unary_ic_data);
 
   bool TryInlineInstanceMethod(InstanceCallInstr* call);
+  bool TryInlineFloat32x4Constructor(StaticCallInstr* call,
+                                     MethodRecognizer::Kind recognized_kind);
+  bool TryInlineUint32x4Constructor(StaticCallInstr* call,
+                                    MethodRecognizer::Kind recognized_kind);
   bool TryInlineFloat32x4Method(InstanceCallInstr* call,
                                 MethodRecognizer::Kind recognized_kind);
   bool TryInlineUint32x4Method(InstanceCallInstr* call,
@@ -148,7 +152,10 @@
                              MethodRecognizer::Kind getter);
   bool InlineUint32x4Getter(InstanceCallInstr* call,
                             MethodRecognizer::Kind getter);
-
+  bool InlineFloat32x4BinaryOp(InstanceCallInstr* call,
+                               Token::Kind op_kind);
+  bool InlineUint32x4BinaryOp(InstanceCallInstr* call,
+                              Token::Kind op_kind);
   void InlineImplicitInstanceGetter(InstanceCallInstr* call);
   void InlineArrayLengthGetter(InstanceCallInstr* call,
                                intptr_t length_offset,
diff --git a/runtime/vm/object_store.cc b/runtime/vm/object_store.cc
index 27fea12..11482f7 100644
--- a/runtime/vm/object_store.cc
+++ b/runtime/vm/object_store.cc
@@ -5,9 +5,11 @@
 #include "vm/object_store.h"
 
 #include "vm/exceptions.h"
+#include "vm/dart_entry.h"
 #include "vm/isolate.h"
 #include "vm/object.h"
 #include "vm/raw_object.h"
+#include "vm/symbols.h"
 #include "vm/visitor.h"
 
 namespace dart {
@@ -41,6 +43,7 @@
     float32x4_class_(Class::null()),
     uint32x4_class_(Class::null()),
     typed_data_classes_(Array::null()),
+    error_class_(Class::null()),
     stacktrace_class_(Class::null()),
     jsregexp_class_(Class::null()),
     weak_property_class_(Class::null()),
@@ -103,15 +106,21 @@
   ASSERT(this->preallocated_stack_trace() == Stacktrace::null());
 
   Object& result = Object::Handle();
+  const Library& library = Library::Handle(Library::CoreLibrary());
 
-  result = Exceptions::Create(Exceptions::kStackOverflow,
-                              Object::empty_array());
+  result = DartLibraryCalls::InstanceCreate(library,
+                                            Symbols::StackOverflowError(),
+                                            Symbols::Dot(),
+                                            Object::empty_array());
   if (result.IsError()) {
     return false;
   }
   set_stack_overflow(Instance::Cast(result));
 
-  result = Exceptions::Create(Exceptions::kOutOfMemory, Object::empty_array());
+  result = DartLibraryCalls::InstanceCreate(library,
+                                            Symbols::OutOfMemoryError(),
+                                            Symbols::Dot(),
+                                            Object::empty_array());
   if (result.IsError()) {
     return false;
   }
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 35c5c9f..76a2bcf 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -213,6 +213,16 @@
     typed_data_classes_ = value.raw();
   }
 
+  RawClass* error_class() const {
+    return error_class_;
+  }
+  void set_error_class(const Class& value) {
+    error_class_ = value.raw();
+  }
+  static intptr_t error_class_offset() {
+    return OFFSET_OF(ObjectStore, error_class_);
+  }
+
   RawClass* stacktrace_class() const {
     return stacktrace_class_;
   }
@@ -451,6 +461,7 @@
   RawClass* float32x4_class_;
   RawClass* uint32x4_class_;
   RawArray* typed_data_classes_;
+  RawClass* error_class_;
   RawClass* stacktrace_class_;
   RawClass* jsregexp_class_;
   RawClass* weak_property_class_;
diff --git a/runtime/vm/symbols.h b/runtime/vm/symbols.h
index 554e419..3345429 100644
--- a/runtime/vm/symbols.h
+++ b/runtime/vm/symbols.h
@@ -99,6 +99,7 @@
   V(ICData, "ICData")                                                          \
   V(MegamorphicCache, "MegamorphicCache")                                      \
   V(SubtypeTestCache, "SubtypeTestCache")                                      \
+  V(Error, "Error")                                                            \
   V(ApiError, "ApiError")                                                      \
   V(LanguageError, "LanguageError")                                            \
   V(UnhandledException, "UnhandledException")                                  \
@@ -266,7 +267,9 @@
   V(_Random, "_Random")                                                        \
   V(_state, "_state")                                                          \
   V(_A, "_A")                                                                  \
+  V(_stackTrace, "_stackTrace")                                                \
   V(_SpecialTypeMirrorImpl, "_SpecialTypeMirrorImpl")                          \
+  V(_LocalMethodMirrorImpl, "_LocalMethodMirrorImpl")                          \
   V(_LocalVariableMirrorImpl, "_LocalVariableMirrorImpl")                      \
   V(_LocalParameterMirrorImpl, "_LocalParameterMirrorImpl")                    \
 
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
index ad1a2fe..b408354 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/emitter.dart
@@ -1143,11 +1143,14 @@
       // possible stubs for this closure.
       FunctionSignature signature = member.computeSignature(compiler);
       Set<Selector> selectors = signature.optionalParametersAreNamed
-          ? computeNamedSelectors(signature, member)
+          ? computeSeenNamedSelectors(member)
           : computeOptionalSelectors(signature, member);
       for (Selector selector in selectors) {
         addParameterStub(member, selector, defineStub, generatedStubNames);
       }
+      if (signature.optionalParametersAreNamed) {
+        addCatchAllParameterStub(member, signature, defineStub);
+      }
     } else {
       Set<Selector> selectors = compiler.codegenWorld.invokedNames[member.name];
       if (selectors == null) return;
@@ -1158,37 +1161,36 @@
     }
   }
 
-  /**
-   * Compute the set of possible selectors in the presence of named
-   * parameters.
-   */
-  Set<Selector> computeNamedSelectors(FunctionSignature signature,
-                                      FunctionElement element) {
-    Set<Selector> selectors = new Set<Selector>();
-    // Add the selector that does not have any optional argument.
-    selectors.add(new Selector(SelectorKind.CALL,
-                               element.name,
-                               element.getLibrary(),
-                               signature.requiredParameterCount,
-                               <SourceString>[]));
+  Set<Selector> computeSeenNamedSelectors(FunctionElement element) {
+    Set<Selector> selectors = compiler.codegenWorld.invokedNames[element.name];
+    if (selectors == null) return null;
+    Set<Selector> result = new Set<Selector>();
+    for (Selector selector in selectors) {
+      if (!selector.applies(element, compiler)) continue;
+      result.add(selector);
+    }
+    return result;
+  }
 
-    // For each optional parameter, we iterator over the set of
-    // already computed selectors and create new selectors with that
-    // parameter now being passed.
-    signature.forEachOptionalParameter((Element element) {
-      Set<Selector> newSet = new Set<Selector>();
-      selectors.forEach((Selector other) {
-        List<SourceString> namedArguments = [element.name];
-        namedArguments.addAll(other.namedArguments);
-        newSet.add(new Selector(other.kind,
-                                other.name,
-                                other.library,
-                                other.argumentCount + 1,
-                                namedArguments));
-      });
-      selectors.addAll(newSet);
-    });
-    return selectors;
+  void addCatchAllParameterStub(FunctionElement member,
+                                FunctionSignature signature,
+                                DefineStubFunction defineStub) {
+    // See Primities.applyFunction in js_helper.dart for details.
+    List<jsAst.Property> properties = <jsAst.Property>[];
+    for (Element element in signature.orderedOptionalParameters) {
+      String jsName = backend.namer.safeName(element.name.slowToString());
+      Constant value = compiler.constantHandler.initialVariableValues[element];
+      jsAst.Expression reference = null;
+      if (value == null) {
+        reference = new jsAst.LiteralNull();
+      } else {
+        reference = constantReference(value);
+      }
+      properties.add(new jsAst.Property(js.string(jsName), reference));
+    }
+    defineStub(
+        backend.namer.callCatchAllName,
+        js.fun([], js.return_(new jsAst.ObjectInitializer(properties))));
   }
 
   /**
@@ -3753,7 +3755,11 @@
                 ..write(',$_')
                 ..write('{$n')
                 ..addBuffer(buffer)
-                ..write('}],$n');
+                ..write('}');
+            if (library == compiler.mainApp) {
+              mainBuffer.write(',${n}1');
+            }
+            mainBuffer.write('],$n');
           }
           buffer = buffers[1];
           if (buffer != null) {
@@ -3963,8 +3969,6 @@
   if (!init.mangledNames) init.mangledNames = {};
   if (!init.mangledGlobalNames) init.mangledGlobalNames = {};
   if (!init.statics) init.statics = {};
-  init.getterPrefix = "${namer.getterPrefix}";
-  init.setterPrefix = "${namer.setterPrefix}";
   var libraries = init.libraries;
   var mangledNames = init.mangledNames;
   var mangledGlobalNames = init.mangledGlobalNames;
@@ -3972,10 +3976,22 @@
   var length = reflectionData.length;
   for (var i = 0; i < length; i++) {
     var data = reflectionData[i];
+'''
+// [data] contains these elements:
+// 0. The library name (not unique).
+// 1. The library URI (unique).
+// 2. A function returning the metadata associated with this library.
+// 3. An object literal listing the members of the library.
+// 4. This element is optional and if present it is true and signals that this
+// library is the root library (see dart:mirrors IsolateMirror.rootLibrary).
+//
+// The entries of [data] are built in [assembleProgram] above.
+'''
     var name = data[0];
     var uri = data[1];
     var metadata = data[2];
     var descriptor = data[3];
+    var isRoot = !!data[4];
     var fields = descriptor && descriptor[""];
     var classes = [];
     var functions = [];
@@ -4017,7 +4033,7 @@
       }
     }
     processStatics(descriptor);
-    libraries.push([name, uri, classes, functions, metadata, fields]);
+    libraries.push([name, uri, classes, functions, metadata, fields, isRoot]);
   }
 })''';
   }
diff --git a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
index a58c9db..9ddf1a5 100644
--- a/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
+++ b/sdk/lib/_internal/compiler/implementation/js_backend/namer.dart
@@ -178,6 +178,7 @@
   final String getterPrefix = r'get$';
   final String setterPrefix = r'set$';
   final String metadataField = '@';
+  final String callCatchAllName = r'call$catchAll';
 
   /**
    * Map from top-level or static elements to their unique identifiers provided
@@ -233,6 +234,19 @@
   SourceString get closureInvocationSelectorName => Compiler.CALL_OPERATOR_NAME;
   bool get shouldMinify => false;
 
+  String getNameForJsGetName(Node node, String name) {
+    switch (name) {
+      case 'GETTER_PREFIX': return getterPrefix;
+      case 'SETTER_PREFIX': return setterPrefix;
+      case 'CALL_CATCH_ALL': return callCatchAllName;
+      default:
+        compiler.reportErrorCode(
+            node, MessageKind.GENERIC,
+            {'text': 'Error: Namer has no name for "$name".'});
+        return 'BROKEN';
+    }
+  }
+
   bool isReserved(String name) => name == isolateName;
 
   String constantName(Constant constant) {
diff --git a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
index 466a9c5..9e84207 100644
--- a/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
+++ b/sdk/lib/_internal/compiler/implementation/ssa/builder.dart
@@ -3023,6 +3023,39 @@
     }
   }
 
+  void handleForeignJsGetName(Send node) {
+    List<Node> arguments = node.arguments.toList();
+    Node argument;
+    switch (arguments.length) {
+    case 0:
+      compiler.reportErrorCode(
+          node, MessageKind.GENERIC,
+          {'text': 'Error: Expected one argument to JS_GET_NAME.'});
+      return;
+    case 1:
+      argument = arguments[0];
+      break;
+    default:
+      for (int i = 1; i < arguments.length; i++) {
+        compiler.reportErrorCode(
+            arguments[i], MessageKind.GENERIC,
+            {'text': 'Error: Extra argument to JS_GET_NAME.'});
+      }
+      return;
+    }
+    LiteralString string = argument.asLiteralString();
+    if (string == null) {
+      compiler.reportErrorCode(
+          argument, MessageKind.GENERIC,
+          {'text': 'Error: Expected a literal string.'});
+    }
+    stack.add(
+        addConstantString(
+            argument,
+            backend.namer.getNameForJsGetName(
+                argument, string.dartString.slowToString())));
+  }
+
   void handleForeignJsSetupObject(Send node) {
     if (!node.arguments.isEmpty) {
       compiler.cancel(
@@ -3198,6 +3231,8 @@
       stack.add(addConstantString(node, backend.namer.operatorIs(element)));
     } else if (name == const SourceString('JS_CURRENT_ISOLATE')) {
       handleForeignJsCurrentIsolate(node);
+    } else if (name == const SourceString('JS_GET_NAME')) {
+      handleForeignJsGetName(node);
     } else {
       throw "Unknown foreign: ${selector}";
     }
diff --git a/sdk/lib/_internal/lib/foreign_helper.dart b/sdk/lib/_internal/lib/foreign_helper.dart
index 55e99a1..7268ead 100644
--- a/sdk/lib/_internal/lib/foreign_helper.dart
+++ b/sdk/lib/_internal/lib/foreign_helper.dart
@@ -72,6 +72,28 @@
  *
  *        this.field = JS('String', '# + "x"', this.field);
  *
+ *  + Never use `#` in function bodies.
+ *
+ *    This is a variation on the previous guideline.  Since `#` is replaced with
+ *    an *expression* and the expression is only valid in the immediate context,
+ *    `#` should never appear in a function body.  Doing so might defer the
+ *    evaluation of the expression, and its side effects, until the function is
+ *    called.
+ *
+ *    For example,
+ *
+ *        var value = foo();
+ *        var f = JS('', 'function(){return #}', value)
+ *
+ *    might result in no immediate call to `foo` and a call to `foo` on every
+ *    call to the JavaScript function bound to `f`.  This is better:
+ *
+ *        var f = JS('',
+ *            '(function(val) { return function(){return val}; })(#)', value);
+ *
+ *    Since `#` occurs in the immediately evaluated expression, the expression
+ *    is immediately evaluated and bound to `val` in the immediate call.
+ *
  *
  * Additional notes.
  *
@@ -208,3 +230,8 @@
  * Returns the global object, usually called encoded as [: $ :].
  */
 JS_GLOBAL_OBJECT() {}
+
+/**
+ * Obtain [name] from Namer.
+ */
+String JS_GET_NAME(String name) {}
diff --git a/sdk/lib/_internal/lib/js_helper.dart b/sdk/lib/_internal/lib/js_helper.dart
index f7b6fd1..5f590b0 100644
--- a/sdk/lib/_internal/lib/js_helper.dart
+++ b/sdk/lib/_internal/lib/js_helper.dart
@@ -12,19 +12,20 @@
                                    JS_CURRENT_ISOLATE_CONTEXT,
                                    JS_DART_OBJECT_CONSTRUCTOR,
                                    JS_FUNCTION_CLASS_NAME,
+                                   JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG,
+                                   JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG,
+                                   JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG,
+                                   JS_FUNCTION_TYPE_RETURN_TYPE_TAG,
+                                   JS_FUNCTION_TYPE_TAG,
+                                   JS_FUNCTION_TYPE_VOID_RETURN_TAG,
+                                   JS_GET_NAME,
+                                   JS_GLOBAL_OBJECT,
+                                   JS_HAS_EQUALS,
                                    JS_IS_INDEXABLE_FIELD_NAME,
                                    JS_OBJECT_CLASS_NAME,
                                    JS_OPERATOR_AS_PREFIX,
                                    JS_OPERATOR_IS_PREFIX,
-                                   JS_GLOBAL_OBJECT,
                                    JS_SIGNATURE_NAME,
-                                   JS_HAS_EQUALS,
-                                   JS_FUNCTION_TYPE_TAG,
-                                   JS_FUNCTION_TYPE_VOID_RETURN_TAG,
-                                   JS_FUNCTION_TYPE_RETURN_TYPE_TAG,
-                                   JS_FUNCTION_TYPE_REQUIRED_PARAMETERS_TAG,
-                                   JS_FUNCTION_TYPE_OPTIONAL_PARAMETERS_TAG,
-                                   JS_FUNCTION_TYPE_NAMED_PARAMETERS_TAG,
                                    RAW_DART_FUNCTION_REF;
 import 'dart:_interceptors';
 import 'dart:_collection-dev' as _symbol_dev;
@@ -571,21 +572,40 @@
       arguments.addAll(positionalArguments);
     }
 
-    // Sort the named arguments to get the right selector name and
-    // arguments order.
-    if (namedArguments != null && !namedArguments.isEmpty) {
-      // Call new List.from to make sure we get a JavaScript array.
+    // TODO(ahe): Use JS_SOMETHING to get name from Namer.
+    if (JS('bool', r'# in #', JS_GET_NAME('CALL_CATCH_ALL'), function)) {
+      // We expect the closure to have a "call$catchAll" (the value of
+      // JS_GET_NAME('CALL_CATCH_ALL')) function that returns all the expected
+      // named parameters as a (new) JavaScript object literal.  The keys in
+      // the object literal correspond to the argument names, and the values
+      // are the default values. The compiler emits the properties sorted by
+      // keys, and this order is preserved in JavaScript, so we don't need to
+      // sort the keys. Since a new object is returned each time we call
+      // call$catchAll, we can simply overwrite default entries with the
+      // provided named arguments. If there are incorrectly named arguments in
+      // [namedArguments], noSuchMethod will be called as expected.
+      var allNamedArguments =
+          JS('var', r'#[#]()', function, JS_GET_NAME('CALL_CATCH_ALL'));
+      if (namedArguments != null && !namedArguments.isEmpty) {
+        namedArguments.forEach((String key, argument) {
+          JS('void', '#[#] = #', allNamedArguments, key, argument);
+        });
+      }
       List<String> listOfNamedArguments =
-          new List<String>.from(namedArguments.keys);
-      argumentCount += namedArguments.length;
-      // We're sorting on strings, and the behavior is the same between
-      // Dart string sort and JS string sort. To avoid needing the Dart
-      // sort implementation, we use the JavaScript one instead.
-      JS('void', '#.sort()', listOfNamedArguments);
+          JS('List', 'Object.getOwnPropertyNames(#)', allNamedArguments);
+      argumentCount += listOfNamedArguments.length;
       listOfNamedArguments.forEach((String name) {
         buffer.write('\$$name');
-        arguments.add(namedArguments[name]);
+        arguments.add(JS('', '#[#]', allNamedArguments, name));
       });
+    } else {
+      if (namedArguments != null && !namedArguments.isEmpty) {
+        namedArguments.forEach((String name, argument) {
+          buffer.write('\$$name');
+          arguments.add(argument);
+          argumentCount++;
+        });
+      }
     }
 
     String selectorName = 'call\$$argumentCount$buffer';
diff --git a/sdk/lib/_internal/lib/js_mirrors.dart b/sdk/lib/_internal/lib/js_mirrors.dart
index a1c40bf..7beac1b 100644
--- a/sdk/lib/_internal/lib/js_mirrors.dart
+++ b/sdk/lib/_internal/lib/js_mirrors.dart
@@ -7,7 +7,11 @@
 import 'dart:async';
 import 'dart:mirrors';
 
-import 'dart:_foreign_helper' show JS, JS_CURRENT_ISOLATE;
+import 'dart:_foreign_helper' show
+    JS,
+    JS_CURRENT_ISOLATE,
+    JS_CURRENT_ISOLATE_CONTEXT,
+    JS_GET_NAME;
 import 'dart:_collection-dev' as _symbol_dev;
 import 'dart:_js_helper' show
     BoundClosure,
@@ -36,6 +40,8 @@
 }
 
 class JsMirrorSystem implements MirrorSystem {
+  final IsolateMirror isolate = new JsIsolateMirror();
+
   TypeMirror get dynamicType => _dynamicType;
   TypeMirror get voidType => _voidType;
 
@@ -72,18 +78,16 @@
       List<String> functions = data[3];
       var metadataFunction = data[4];
       var fields = data[5];
+      bool isRoot = data[6];
       List metadata = (metadataFunction == null)
           ? null : JS('List', '#()', metadataFunction);
       var libraries = result.putIfAbsent(name, () => <LibraryMirror>[]);
       libraries.add(
           new JsLibraryMirror(
-              s(name), uri, classes, functions, metadata, fields));
+              s(name), uri, classes, functions, metadata, fields, isRoot));
     }
     return result;
   }
-
-  // TODO(ahe): Implement this.
-  IsolateMirror get isolate => throw new UnimplementedError();
 }
 
 abstract class JsMirror implements Mirror {
@@ -113,6 +117,26 @@
   }
 }
 
+// This class is somewhat silly in the current implementation.
+class JsIsolateMirror extends JsMirror implements IsolateMirror {
+  final _isolateContext = JS_CURRENT_ISOLATE_CONTEXT();
+
+  String get _prettyName => 'Isolate';
+
+  String get debugName {
+    String id = _isolateContext == null ? 'X' : _isolateContext.id.toString();
+    // Using name similar to what the VM uses.
+    return '${n(rootLibrary.simpleName)}-$id';
+  }
+
+  bool get isCurrent => JS_CURRENT_ISOLATE_CONTEXT() == _isolateContext;
+
+  LibraryMirror get rootLibrary {
+    return currentJsMirrorSystem.libraries.values.firstWhere(
+        (JsLibraryMirror library) => library._isRoot);
+  }
+}
+
 abstract class JsDeclarationMirror extends JsMirror
     implements DeclarationMirror {
   final Symbol simpleName;
@@ -161,6 +185,7 @@
   final List<String> _functions;
   final List _metadata;
   final String _compactFieldSpecification;
+  final bool _isRoot;
   List<JsMethodMirror> _cachedFunctionMirrors;
   List<JsVariableMirror> _cachedFields;
 
@@ -169,7 +194,8 @@
                   this._classes,
                   this._functions,
                   this._metadata,
-                  this._compactFieldSpecification)
+                  this._compactFieldSpecification,
+                  this._isRoot)
       : super(simpleName);
 
   String get _prettyName => 'LibraryMirror';
@@ -831,7 +857,7 @@
     if (isStatic) {
       unmangledName = mangledGlobalNames[accessorName];
     } else {
-      String getterPrefix = JS('String', 'init.getterPrefix');
+      String getterPrefix = JS_GET_NAME('GETTER_PREFIX');
       unmangledName = mangledNames['$getterPrefix$accessorName'];
     }
     if (unmangledName == null) unmangledName = accessorName;
diff --git a/sdk/lib/_internal/lib/js_names.dart b/sdk/lib/_internal/lib/js_names.dart
index 1d56354..4b3e9a9 100644
--- a/sdk/lib/_internal/lib/js_names.dart
+++ b/sdk/lib/_internal/lib/js_names.dart
@@ -4,7 +4,7 @@
 
 library dart._js_names;
 
-import 'dart:_foreign_helper' show JS;
+import 'dart:_foreign_helper' show JS, JS_GET_NAME;
 
 /// No-op method that is called to inform the compiler that unmangled named
 /// must be preserved.
@@ -37,9 +37,9 @@
   preserveNames();
   var keys = extractKeys(jsMangledNames);
   var result = <String, String>{};
-  String getterPrefix = JS('String', 'init.getterPrefix');
+  String getterPrefix = JS_GET_NAME('GETTER_PREFIX');
   int getterPrefixLength = getterPrefix.length;
-  String setterPrefix = JS('String', 'init.setterPrefix');
+  String setterPrefix = JS_GET_NAME('SETTER_PREFIX');
   for (String key in keys) {
     String value = JS('String', '#[#]', jsMangledNames, key);
     result[key] = value;
diff --git a/sdk/lib/_internal/lib/native_helper.dart b/sdk/lib/_internal/lib/native_helper.dart
index 4c66f65..5c311f4 100644
--- a/sdk/lib/_internal/lib/native_helper.dart
+++ b/sdk/lib/_internal/lib/native_helper.dart
@@ -139,36 +139,27 @@
   return JS('var', '{}');
 }
 
-/**
- * Cached value for the function to use to get the type name of an
- * object.
- */
-Function _getTypeNameOf;
-
-/**
- * Returns the type name of [obj].
- */
-String getTypeNameOf(var obj) {
-  if (_getTypeNameOf == null) _getTypeNameOf = getFunctionForTypeNameOf();
-  return _getTypeNameOf(obj);
-}
+Function getTypeNameOf = getFunctionForTypeNameOf();
 
 /**
  * Returns the function to use to get the type name (i.e. dispatch tag) of an
  * object.
  */
 Function getFunctionForTypeNameOf() {
-  var getTagFunction = _getFunctionForTypeNameOf();
+  var getTagFunction = getBaseFunctionForTypeNameOf();
   if (JS('bool', 'typeof dartExperimentalFixupGetTag == "function"')) {
-    return _applyExperimentalFixup(
+    return applyExperimentalFixup(
         JS('', 'dartExperimentalFixupGetTag'), getTagFunction);
   }
   return getTagFunction;
 }
 
-Function _getFunctionForTypeNameOf() {
+/// Don't call directly, use [getFunctionForTypeNameOf] instead.
+Function getBaseFunctionForTypeNameOf() {
   // If we're not in the browser, we're almost certainly running on v8.
-  if (!identical(JS('String', 'typeof(navigator)'), 'object')) return typeNameInChrome;
+  if (!identical(JS('String', 'typeof(navigator)'), 'object')) {
+    return typeNameInChrome;
+  }
 
   String userAgent = JS('String', "navigator.userAgent");
   // TODO(antonm): remove a reference to DumpRenderTree.
@@ -191,15 +182,14 @@
   }
 }
 
-Function _applyExperimentalFixup(fixupJSFunction,
-                                 Function originalGetTagFunction) {
-  // Since DART_CLOSURE_TO_JS works only for top level functions, store the
-  // closed over JavaScript function in a top level variable and use a top level
-  // function.  This is fine since we have only one instance of the 'closure'.
-  _getTagJSFunction = originalGetTagFunction;
+Function applyExperimentalFixup(fixupJSFunction,
+                                Function originalGetTagDartFunction) {
+  var originalGetTagJSFunction =
+      convertDartClosure1ArgToJSNoDataConversions(
+          originalGetTagDartFunction);
+
   var newGetTagJSFunction =
-      JS('', '#(#)',
-         fixupJSFunction, DART_CLOSURE_TO_JS(_callGetTagJSFunction));
+      JS('', '#(#)', fixupJSFunction, originalGetTagJSFunction);
 
   String newGetTagDartFunction(object) =>
       JS('', '#(#)', newGetTagJSFunction, object);
@@ -207,10 +197,15 @@
   return newGetTagDartFunction;
 }
 
-var _getTagJSFunction;
-_callGetTagJSFunction(object) => _getTagJSFunction(object);
+callDartFunctionWith1Arg(fn, arg) => fn(arg);
 
-
+convertDartClosure1ArgToJSNoDataConversions(dartClosure) {
+  return JS('',
+      '(function(invoke, closure){'
+        'return function(arg){ return invoke(closure, arg); };'
+      '})(#, #)',
+      DART_CLOSURE_TO_JS(callDartFunctionWith1Arg), dartClosure);
+}
 
 
 String toStringForNativeObject(var obj) {
diff --git a/sdk/lib/_internal/pub/test/validator/dependency_test.dart b/sdk/lib/_internal/pub/test/validator/dependency_test.dart
index 57b380f..ee38ee8 100644
--- a/sdk/lib/_internal/pub/test/validator/dependency_test.dart
+++ b/sdk/lib/_internal/pub/test/validator/dependency_test.dart
@@ -22,12 +22,12 @@
 
 expectDependencyValidationError(String error) {
   expect(schedulePackageValidation(dependency),
-      completion(pairOf(someElement(contains(error)), isEmpty)));
+      completion(pairOf(anyElement(contains(error)), isEmpty)));
 }
 
 expectDependencyValidationWarning(String warning) {
   expect(schedulePackageValidation(dependency),
-      completion(pairOf(isEmpty, someElement(contains(warning)))));
+      completion(pairOf(isEmpty, anyElement(contains(warning)))));
 }
 
 /// Sets up a test package with dependency [dep] and mocks a server with
diff --git a/sdk/lib/async/future.dart b/sdk/lib/async/future.dart
index 9761481..02e51a0 100644
--- a/sdk/lib/async/future.dart
+++ b/sdk/lib/async/future.dart
@@ -322,27 +322,53 @@
 }
 
 /**
- * A [Completer] is used to produce [Future]s and supply their value when it
- * becomes available.
+ * A [Completer] is used to produce [Future]s and to complete those futures
+ * with a value or error at a later time.
  *
- * A service that provides values to callers, and wants to return [Future]s can
- * use a [Completer] as follows:
+ * A class that wants to return [Future]s can use a [Completer] as follows:
  *
- *     Completer completer = new Completer();
- *     // send future object back to client...
- *     return completer.future;
- *     ...
+ *     Class myAsyncOperation {
+ *       Completer _completer = new Completer();
  *
- *     // later when value is available, call:
- *     completer.complete(value);
+ *       Future<T> myOp() {
+ *         _startOperation();
+ *         // send future object back to client...
+ *         return _completer.future;
+ *       }
  *
- *     // alternatively, if the service cannot produce the value, it
- *     // can provide an error:
- *     completer.completeError(error);
+ *       // Something calls this when the value is ready.
+ *       _finishOperation(T result) {
+ *         _completer.complete(value);
+ *       }
+ *
+ *       // If something goes wrong, call this.
+ *       _errorHappened(error) {
+ *         _completer.completeError(error);
+ *       }
+ *     }
  *
  */
 abstract class Completer<T> {
 
+  /**
+   * Creates a completer whose future is completed asynchronously, sometime
+   * after [complete] is called on it. This allows a call to [complete] to
+   * be in the middle of other code, without running an unknown amount of
+   * future completion and [then] callbacks synchronously at the point that
+   * [complete] is called.
+   *
+   * Example:
+   *
+   *     var completer = new Completer.sync();
+   *     completer.future.then((_) { bar(); });
+   *     // The completion is the result of the asynchronous onDone event.
+   *     // However, there is code executed after the call to complete,
+   *     // but before completer.future runs its completion callback.
+   *     stream.listen(print, onDone: () {
+   *       completer.complete("done");
+   *       foo();  // In this case, foo() runs before bar().
+   *     });
+   */
   factory Completer() => new _AsyncCompleter<T>();
 
   /**
@@ -363,16 +389,14 @@
    * Bad example. Do not use this code. Only for illustrative purposes:
    *
    *     var completer = new Completer.sync();
+   *     completer.future.then((_) { bar(); });
    *     // The completion is the result of the asynchronous onDone event.
    *     // However, there is still code executed after the completion. This
    *     // operation is *not* safe.
    *     stream.listen(print, onDone: () {
    *       completer.complete("done");
-   *       foo();  // This operation follows the completion.
+   *       foo();  // In this case, foo() runs after bar().
    *     });
-   *
-   * *WARNING* This constructor is experimental and could disappear or change
-   * behavior.
    */
   factory Completer.sync() => new _SyncCompleter<T>();
 
diff --git a/sdk/lib/convert/byte_conversion.dart b/sdk/lib/convert/byte_conversion.dart
new file mode 100644
index 0000000..edf297e
--- /dev/null
+++ b/sdk/lib/convert/byte_conversion.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.convert;
+
+/**
+ * The [ByteConversionSink] provides an interface for converters to
+ * efficiently transmit byte data.
+ *
+ * Instead of limiting the interface to one non-chunked list of bytes it
+ * accepts its input in chunks (themselves being lists of bytes).
+ */
+abstract class ByteConversionSink extends ChunkedConversionSink<List<int>> {
+  ByteConversionSink();
+  factory ByteConversionSink.withCallback(void callback(List<int> accumulated))
+      = _ByteCallbackSink;
+  factory ByteConversionSink.from(ChunkedConversionSink<List<int>> sink)
+      = _ByteAdapterSink;
+
+  /**
+   * Adds the next [chunk] to `this`.
+   *
+   * Adds the bytes defined by [start] and [end]-exclusive to `this`.
+   *
+   * If [isLast] is `true` closes `this`.
+   *
+   * Contrary to `add` the given [chunk] must not be held onto. Once the method
+   * returns, it is safe to overwrite the data in it.
+   */
+  void addSlice(List<int> chunk, int start, int end, bool isLast);
+
+  // TODO(floitsch): add more methods:
+  // - iterateBytes.
+}
+
+/**
+ * This class provides a base-class for converters that need to accept byte
+ * inputs.
+ */
+abstract class ByteConversionSinkBase extends ByteConversionSink {
+
+  void add(List<int> chunk);
+  void close();
+
+  void addSlice(List<int> chunk, int start, int end, bool isLast) {
+    add(chunk.sublist(start, end));
+    if (isLast) close();
+  }
+}
+
+/**
+ * This class adapts a simple [ChunkedConversionSink] to a [ByteConversionSink].
+ *
+ * All additional methods of the [ByteConversionSink] (compared to the
+ * ChunkedConversionSink) are redirected to the `add` method.
+ */
+class _ByteAdapterSink extends ByteConversionSinkBase {
+  final ChunkedConversionSink<List<int>> _sink;
+
+  _ByteAdapterSink(this._sink);
+
+  void add(List<int> chunk) => _sink.add(chunk);
+  void close() => _sink.close();
+}
+
+/**
+ * This class accumulates all chunks into one list of bytes
+ * and invokes a callback when the sink is closed.
+ *
+ * This class can be used to terminate a chunked conversion.
+ */
+class _ByteCallbackSink extends ByteConversionSinkBase {
+  static const _INITIAL_BUFFER_SIZE = 1024;
+
+  final _ChunkedConversionCallback<List<int>> _callback;
+  // TODO(11971, floitsch): use Uint8List instead of normal lists.
+  List<int> _buffer = new List<int>(_INITIAL_BUFFER_SIZE);
+  int _bufferIndex = 0;
+
+  _ByteCallbackSink(void callback(List<int> accumulated))
+      : this._callback = callback;
+
+  void add(Iterable<int> chunk) {
+    int freeCount = _buffer.length - _bufferIndex;
+    if (chunk.length > freeCount) {
+      // Grow the buffer.
+      int oldLength = _buffer.length;
+      int newLength = _roundToPowerOf2(chunk.length + oldLength) * 2;
+      // TODO(11971, floitsch): use Uint8List instead of normal lists.
+      List<int> grown = new List<int>(newLength);
+      grown.setRange(0, _buffer.length, _buffer);
+      _buffer = grown;
+    }
+    _buffer.setRange(_bufferIndex, _bufferIndex + chunk.length, chunk);
+    _bufferIndex += chunk.length;
+  }
+
+  static int _roundToPowerOf2(int v) {
+    assert(v > 0);
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+    return v;
+  }
+
+  void close() {
+    _callback(_buffer.sublist(0, _bufferIndex));
+  }
+}
diff --git a/sdk/lib/convert/chunked_conversion.dart b/sdk/lib/convert/chunked_conversion.dart
new file mode 100644
index 0000000..7674421
--- /dev/null
+++ b/sdk/lib/convert/chunked_conversion.dart
@@ -0,0 +1,148 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.convert;
+
+typedef void _ChunkedConversionCallback<T>(T accumulated);
+
+/**
+ * A [ChunkedConversionSink] is used to transmit data more efficiently between
+ * two converters during chunked conversions.
+ */
+abstract class ChunkedConversionSink<T> {
+  ChunkedConversionSink();
+  factory ChunkedConversionSink.withCallback(
+      void callback(List<T> accumulated)) = _SimpleCallbackSink;
+
+  /**
+   * Adds chunked data to this sink.
+   *
+   * This method is also used when converters are used as [StreamTransformer]s.
+   */
+  void add(T chunk);
+
+  /**
+   * Closes the sink.
+   *
+   * This signals the end of the chunked conversion. This method is called
+   * when converters are used as [StreamTransformer]'s.
+   */
+  void close();
+}
+
+/**
+ * This class accumulates all chunks and invokes a callback with a list of
+ * the chunks when the sink is closed.
+ *
+ * This class can be used to terminate a chunked conversion.
+ */
+class _SimpleCallbackSink<T> extends ChunkedConversionSink<T> {
+  final _ChunkedConversionCallback<List<T>> _callback;
+  final List<T> _accumulated = <T>[];
+
+  _SimpleCallbackSink(this._callback);
+
+  void add(T chunk) { _accumulated.add(chunk); }
+  void close() { _callback(_accumulated); }
+}
+
+/**
+ * This class wraps a [Converter] for use as a [StreamTransformer].
+ */
+class _ConverterTransformStream<S, T> extends EventTransformStream<S, T> {
+  final _ConverterStreamEventTransformer<S, T> _eventTransformer;
+
+  _ConverterTransformStream(Stream<S> source, Converter converter)
+      : this._withEventTransformer(
+          source,
+          new _ConverterStreamEventTransformer<S, T>(converter));
+
+  _ConverterTransformStream._withEventTransformer(
+      Stream<S> source,
+      _ConverterStreamEventTransformer<S, T> eventTransformer)
+      : _eventTransformer = eventTransformer,
+        super(source, eventTransformer);
+
+  /**
+   * Starts listening to `this`.
+   *
+   * This starts the chunked conversion.
+   */
+  StreamSubscription<T> listen(void onData(T data),
+                               { void onError(error),
+                                 void onDone(),
+                                 bool cancelOnError }) {
+    _eventTransformer._startChunkedConversion();
+    return super.listen(onData, onError: onError, onDone: onDone,
+                        cancelOnError: cancelOnError);
+  }
+}
+
+/**
+ * This class converts implements the logic for a chunked conversion as a
+ * stream transformer.
+ *
+ * It is used as strategy in the [EventTransformStream].
+ *
+ * It also implements the [ChunkedConversionSink] interface so that it
+ * can be used as output sink in a chunked conversion.
+ */
+class _ConverterStreamEventTransformer<S, T>
+    implements ChunkedConversionSink<T>, StreamEventTransformer<S, T> {
+  final Converter _converter;
+
+  /** At every [handleData] this field is updated with the new event sink. */
+  EventSink<T> _eventSink;
+
+  /**
+   * The input sink for new data. All data that is received with
+   * [handleData] is added into this sink.
+   */
+  ChunkedConversionSink _chunkedSink;
+
+  _ConverterStreamEventTransformer(this._converter);
+
+  /**
+   * Starts the chunked conversion.
+   */
+  void _startChunkedConversion() {
+    _chunkedSink = _converter.startChunkedConversion(this);
+  }
+
+  /**
+   * Not supported.
+   */
+  Stream bind(Stream otherStream) {
+    throw new UnsupportedError("Converter streams must not call bind");
+  }
+
+  void add(T o) => _eventSink.add(o);
+  void close() => _eventSink.close();
+
+  void handleData(S event, EventSink<T> eventSink) {
+    _eventSink = eventSink;
+    try {
+      _chunkedSink.add(event);
+    } catch(e) {
+      eventSink.addError(e);
+    } finally {
+      _eventSink = null;
+    }
+  }
+
+  void handleDone(EventSink<T> eventSink) {
+    _eventSink = eventSink;
+    try {
+      _chunkedSink.close();
+    } catch(e) {
+      eventSink.addError(e);
+    } finally {
+      _eventSink = null;
+    }
+  }
+
+  void handleError(var errorEvent, EventSink<T> eventSink) {
+    eventSink.addError(errorEvent);
+  }
+}
diff --git a/sdk/lib/convert/convert.dart b/sdk/lib/convert/convert.dart
index e6efd45..6c76db0 100644
--- a/sdk/lib/convert/convert.dart
+++ b/sdk/lib/convert/convert.dart
@@ -4,10 +4,14 @@
 
 library dart.convert;
 
+import 'dart:async';
 import 'dart:json' as OLD_JSON_LIB;
 
+part 'byte_conversion.dart';
+part 'chunked_conversion.dart';
 part 'codec.dart';
 part 'converter.dart';
 part 'encoding.dart';
 part 'json.dart';
+part 'string_conversion.dart';
 part 'utf.dart';
diff --git a/sdk/lib/convert/convert_sources.gypi b/sdk/lib/convert/convert_sources.gypi
index 1e5b9ba..295451f 100644
--- a/sdk/lib/convert/convert_sources.gypi
+++ b/sdk/lib/convert/convert_sources.gypi
@@ -7,10 +7,13 @@
   'sources': [
     'convert.dart',
     # The above file needs to be first as it lists the parts below.
+    'byte_conversion.dart',
+    'chunked_conversion.dart',
     'codec.dart',
     'converter.dart',
     'encoding.dart',
     'json.dart',
+    'string_conversion.dart',
     'utf.dart',
   ],
 }
diff --git a/sdk/lib/convert/converter.dart b/sdk/lib/convert/converter.dart
index 8e7a6f1..5563d7c3 100644
--- a/sdk/lib/convert/converter.dart
+++ b/sdk/lib/convert/converter.dart
@@ -10,7 +10,7 @@
  * *Converters are still experimental and are subject to change without notice.*
  *
  */
-abstract class Converter<S, T> {
+abstract class Converter<S, T> implements StreamTransformer {
   /**
    * Converts [input] and returns the result of the conversion.
    */
@@ -25,6 +25,19 @@
   Converter<S, dynamic> fuse(Converter<T, dynamic> other) {
     return new _FusedConverter<S, T, dynamic>(this, other);
   }
+
+  /**
+   * Starts a chunked conversion.
+   */
+  ChunkedConversionSink startChunkedConversion(ChunkedConversionSink sink) {
+    throw new UnsupportedError(
+        "This converter does not support chunked conversions: $this");
+  }
+
+  // Subclasses are encouraged to provide better types.
+  Stream bind(Stream source) {
+    return new _ConverterTransformStream(source, this);
+  }
 }
 
 /**
@@ -39,4 +52,8 @@
   _FusedConverter(this._first, this._second);
 
   T convert(S input) => _second.convert(_first.convert(input));
+
+  ChunkedConversionSink startChunkedConversion(ChunkedConversionSink sink) {
+    return _first.startChunkedConversion(_second.startChunkedConversion(sink));
+  }
 }
diff --git a/sdk/lib/convert/encoding.dart b/sdk/lib/convert/encoding.dart
index d7092bc..19989fb 100644
--- a/sdk/lib/convert/encoding.dart
+++ b/sdk/lib/convert/encoding.dart
@@ -11,6 +11,9 @@
 // consolitate them, we need to remove `Encoding` here.
 abstract class Encoding extends Codec<String, List<int>> {
   const Encoding();
+
+  // TODO(floitsch): should we introduce a StringToByteEncoder and
+  // a ByteToStringDecoder so that we have better typing?
 }
 
 // TODO(floitsch): add other encodings, like ASCII and ISO_8859_1.
diff --git a/sdk/lib/convert/json.dart b/sdk/lib/convert/json.dart
index df036a1..035a89e 100644
--- a/sdk/lib/convert/json.dart
+++ b/sdk/lib/convert/json.dart
@@ -53,8 +53,10 @@
   JsonDecoder get decoder => new JsonDecoder(null);
 }
 
+typedef _Reviver(var key, var value);
+
 class _ReviverJsonCodec extends JsonCodec {
-  final Function _reviver;
+  final _Reviver _reviver;
   _ReviverJsonCodec(this._reviver);
 
   Object decode(String str, {reviver(var key, var value)}) {
@@ -66,7 +68,7 @@
 }
 
 /**
- * A [JsonEncoder] converts JSON objects to strings.
+ * This class converts JSON objects to strings.
  */
 class JsonEncoder extends Converter<Object, String> {
   JsonEncoder();
@@ -99,13 +101,61 @@
    * serialized, the new values may or may not be reflected in the result.
    */
   String convert(Object o) => OLD_JSON_LIB.stringify(o);
+
+  /**
+   * Starts a chunked conversion.
+   *
+   * The converter works more efficiently if the given [sink] is a
+   * [StringConversionSink].
+   *
+   * Returns a chunked-conversion sink that accepts at most one object. It is
+   * an error to invoke `add` more than once on the returned sink.
+   */
+  ChunkedConversionSink<Object> startChunkedConversion(
+      ChunkedConversionSink<String> sink) {
+    if (sink is! StringConversionSink) {
+      sink = new StringConversionSink.from(sink);
+    }
+    return new _JsonEncoderSink(sink);
+  }
+
+  // Override the base-classes bind, to provide a better type.
+  Stream<String> bind(Stream<Object> stream) => super.bind(stream);
 }
 
-typedef _Reviver(var key, var value);
+/**
+ * Implements the chunked conversion from object to its JSON representation.
+ *
+ * The sink only accepts one value, but will produce output in a chunked way.
+ */
+class _JsonEncoderSink extends ChunkedConversionSink<Object> {
+  final StringConversionSink _sink;
+  bool _isDone = false;
 
+  _JsonEncoderSink(this._sink);
+
+  /**
+   * Encodes the given object [o].
+   *
+   * It is an error to invoke this method more than once on any instance. While
+   * this makes the input effectly non-chunked the output will be generated in
+   * a chunked way.
+   */
+  void add(Object o) {
+    if (_isDone) {
+      throw new StateError("Only one call to add allowed");
+    }
+    _isDone = true;
+    ClosableStringSink stringSink = _sink.asStringSink();
+    OLD_JSON_LIB.printOn(o, stringSink);
+    stringSink.close();
+  }
+
+  void close() { /* do nothing */ }
+}
 
 /**
- * A [JsonDecoder] parses JSON strings and builds the corresponding objects.
+ * This class parses JSON strings and builds the corresponding objects.
  */
 class JsonDecoder extends Converter<String, Object> {
   final _Reviver _reviver;
@@ -117,7 +167,7 @@
   JsonDecoder(reviver(var key, var value)) : this._reviver = reviver;
 
   /**
-   * Converts the given Json-string [input] to its corresponding object.
+   * Converts the given JSON-string [input] to its corresponding object.
    *
    * Parsed JSON values are of the types [num], [String], [bool], [Null],
    * [List]s of parsed JSON values or [Map]s from [String] to parsed
@@ -132,4 +182,43 @@
    * Throws [FormatException] if the input is not valid JSON text.
    */
   Object convert(String input) => OLD_JSON_LIB.parse(input, _reviver);
+
+  /**
+   * Starts a conversion from a chunked JSON string to its corresponding
+   * object.
+   *
+   * The output [sink] receives exactly one decoded element through `add`.
+   */
+  StringConversionSink startChunkedConversion(
+      ChunkedConversionSink<Object> sink) {
+    return new _JsonDecoderSink(_reviver, sink);
+  }
+
+  // Override the base-classes bind, to provide a better type.
+  Stream<Object> bind(Stream<String> stream) => super.bind(stream);
+}
+
+/**
+ * Implements the chunked conversion from a JSON string to its corresponding
+ * object.
+ *
+ * The sink only creates one object, but its input can be chunked.
+ */
+// TODO(floitsch): don't accumulate everything before starting to decode.
+class _JsonDecoderSink extends _StringSinkConversionSink {
+  final _Reviver _reviver;
+  final ChunkedConversionSink<Object> _chunkedSink;
+
+  _JsonDecoderSink(this._reviver, this._chunkedSink)
+      : super(new StringBuffer());
+
+  void close() {
+    super.close();
+    StringBuffer buffer = _stringSink;
+    String accumulated = buffer.toString();
+    buffer.clear();
+    Object decoded = OLD_JSON_LIB.parse(accumulated, _reviver);
+    _chunkedSink.add(decoded);
+    _chunkedSink.close();
+  }
 }
diff --git a/sdk/lib/convert/string_conversion.dart b/sdk/lib/convert/string_conversion.dart
new file mode 100644
index 0000000..ee0d135
--- /dev/null
+++ b/sdk/lib/convert/string_conversion.dart
@@ -0,0 +1,341 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+part of dart.convert;
+
+/**
+ * This class provides an interface for converters to
+ * efficiently transmit String data.
+ *
+ * Instead of limiting the interface to one non-chunked String it accepts
+ * partial strings or can be transformed into a byte sink that
+ * accepts UTF-8 code units.
+ */
+abstract class StringConversionSink
+    extends ChunkedConversionSink<String> {
+  StringConversionSink();
+  factory StringConversionSink.withCallback(void callback(String accumulated))
+      = _StringCallbackSink;
+  factory StringConversionSink.from(ChunkedConversionSink<String> sink)
+      = _StringAdapterSink;
+
+  /**
+   * Creates a new instance wrapping the given [sink].
+   *
+   * Every string that is added to the returned instance is forwarded to
+   * the [sink]. The instance is allowed to buffer and is not required to
+   * forward immediately.
+   */
+  factory StringConversionSink.fromStringSink(StringSink sink) =
+      _StringSinkConversionSink;
+
+  /**
+   * Adds the next [chunk] to `this`.
+   *
+   * Adds the substring defined by [start] and [end]-exclusive to `this`.
+   *
+   * If [isLast] is `true` closes `this`.
+   */
+  void addSlice(String chunk, int start, int end, bool isLast);
+
+  /**
+   * Returns `this` as a sink that accepts UTF-8 input.
+   *
+   * If used, this method must be the first and only call to `this`. It
+   * invalidates `this`. All further operations must be performed on the result.
+   */
+  ByteConversionSink asUtf8Sink(bool allowMalformed);
+  // - asRuneSink
+  // - asCodeUnitsSink
+
+  /**
+   * Returns `this` as a [ClosableStringSink].
+   *
+   * If used, this method must be the first and only call to `this`. It
+   * invalidates `this`. All further operations must be performed on the result.
+   */
+  ClosableStringSink asStringSink();
+}
+
+/**
+ * A [ClosableStringSink] extends the [StringSink] interface by adding a
+ * `close` method.
+ */
+abstract class ClosableStringSink extends StringSink {
+  /**
+   * Creates a new instance combining a [StringSink] [sink] and a callback
+   * [onClose] which is invoked when the returned instance is closed.
+   */
+  factory ClosableStringSink.fromStringSink(StringSink sink, void onClose())
+      = _ClosableStringSink;
+
+  /**
+   * Closes `this` and flushes any outstanding data.
+   */
+  void close();
+}
+
+typedef void _StringSinkCloseCallback();
+
+/**
+ * This class wraps an existing [StringSink] and invokes a
+ * closure when [close] is invoked.
+ */
+class _ClosableStringSink implements ClosableStringSink {
+  final _StringSinkCloseCallback _callback;
+  final StringSink _sink;
+
+  _ClosableStringSink(this._sink, this._callback);
+
+  void close() => _callback();
+
+  void writeCharCode(int charCode) => _sink.writeCharCode(charCode);
+  void write(Object o) => _sink.write(o);
+  void writeln([Object o]) => _sink.writeln(o);
+  void writeAll(Iterable objects, [String separator])
+      => _sink.writeAll(objects, separator);
+}
+
+/**
+ * This class wraps an existing [StringConversionSink] and exposes a
+ * [ClosableStringSink] interface. The wrapped sink only needs to implement
+ * `add` and `close`.
+ */
+// TODO(floitsch): make this class public?
+class _StringConversionSinkAsStringSinkAdapter implements ClosableStringSink {
+  static const _MIN_STRING_SIZE = 16;
+
+  StringBuffer _buffer;
+  StringConversionSink _chunkedSink;
+
+  _StringConversionSinkAsStringSinkAdapter(this._chunkedSink)
+      : _buffer = new StringBuffer();
+
+  void close() {
+    if (_buffer.isNotEmpty) _flush();
+    _chunkedSink.close();
+  }
+
+  void writeCharCode(int charCode) {
+    _buffer.writeCharCode(charCode);
+    if (_buffer.length > _MIN_STRING_SIZE) _flush();
+  }
+
+  void write(Object o) {
+    if (_buffer.isNotEmpty) _flush();
+    String str = o.toString();
+    _chunkedSink.add(o.toString());
+  }
+
+  void writeln([Object o]) {
+    _buffer.writeln(o);
+    if (_buffer.length > _MIN_STRING_SIZE) _flush();
+  }
+
+  void writeAll(Iterable objects, [String separator]) {
+    if (_buffer.isNotEmpty) _flush();
+    Iterator iterator = objects.iterator;
+    if (!iterator.moveNext()) return;
+    if (separator.isEmpty) {
+      do {
+        _chunkedSink.add(iterator.current.toString());
+      } while (iterator.moveNext());
+    } else {
+      _chunkedSink.add(iterator.current.toString());
+      while (iterator.moveNext()) {
+        write(separator);
+        _chunkedSink.add(iterator.current.toString());
+      }
+    }
+  }
+
+  void _flush() {
+    String accumulated = _buffer.toString();
+    _buffer.clear();
+    _chunkedSink.add(accumulated);
+  }
+}
+
+/**
+ * This class provides a base-class for converters that need to accept String
+ * inputs.
+ */
+abstract class StringConversionSinkBase extends StringConversionSinkMixin {
+}
+
+/**
+ * This class provides a mixin for converters that need to accept String
+ * inputs.
+ */
+abstract class StringConversionSinkMixin implements StringConversionSink {
+
+  void addSlice(String str, int start, int end, bool isLast);
+  void close();
+
+  void add(String str) => addSlice(str, 0, str.length, false);
+
+  ByteConversionSink asUtf8Sink(bool allowMalformed) {
+    return new _Utf8ConversionSink(this, allowMalformed);
+  }
+
+  ClosableStringSink asStringSink() {
+    return new _StringConversionSinkAsStringSinkAdapter(this);
+  }
+}
+
+/**
+ * This class is a [StringConversionSink] that wraps a [StringSink].
+ */
+class _StringSinkConversionSink extends StringConversionSinkBase {
+  StringSink _stringSink;
+  _StringSinkConversionSink(StringSink this._stringSink);
+
+  void close() {}
+  void addSlice(String str, int start, int end, bool isLast) {
+    if (start != 0 || end != str.length) {
+      for (int i = start; i < end; i++) {
+        _stringSink.writeCharCode(str.codeUnitAt(i));
+      }
+    } else {
+      _stringSink.write(str);
+    }
+    if (isLast) close();
+  }
+
+  void add(String str) => _stringSink.write(str);
+
+  ByteConversionSink asUtf8Sink(bool allowMalformed) {
+    return new _Utf8StringSinkAdapter(this, _stringSink, allowMalformed);
+  }
+
+  ClosableStringSink asStringSink() {
+    return new ClosableStringSink.fromStringSink(_stringSink, this.close);
+  }
+}
+
+/**
+ * This class accumulates all chunks into one string
+ * and invokes a callback when the sink is closed.
+ *
+ * This class can be used to terminate a chunked conversion.
+ */
+class _StringCallbackSink extends _StringSinkConversionSink {
+  final _ChunkedConversionCallback<String> _callback;
+  _StringCallbackSink(this._callback) : super(new StringBuffer());
+
+  void close() {
+    StringBuffer buffer = _stringSink;
+    String accumulated = buffer.toString();
+    buffer.clear();
+    _callback(accumulated);
+  }
+
+  ByteConversionSink asUtf8Sink(bool allowMalformed) {
+    return new _Utf8StringSinkAdapter(
+        this, _stringSink, allowMalformed);
+  }
+}
+
+/**
+ * This class adapts a simple [ChunkedConversionSink] to a
+ * [StringConversionSink].
+ *
+ * All additional methods of the [StringConversionSink] (compared to the
+ * ChunkedConversionSink) are redirected to the `add` method.
+ */
+class _StringAdapterSink extends StringConversionSinkBase {
+  final ChunkedConversionSink<String> _sink;
+
+  _StringAdapterSink(this._sink);
+
+  void add(String str) => _sink.add(str);
+
+  void addSlice(String str, int start, int end, bool isLast) {
+    if (start == 0 && end == str.length) {
+      add(str);
+    } else {
+      add(str.substring(start, end));
+    }
+    if (isLast) close();
+  }
+
+  void close() => _sink.close();
+}
+
+
+/**
+ * Decodes UTF-8 code units and stores them in a [StringSink].
+ */
+class _Utf8StringSinkAdapter extends ByteConversionSink {
+  final _Utf8Decoder _decoder;
+  final ChunkedConversionSink _chunkedSink;
+
+  _Utf8StringSinkAdapter(ChunkedConversionSink chunkedSink,
+                         StringSink sink, bool allowMalformed)
+      : _chunkedSink = chunkedSink,
+        _decoder = new _Utf8Decoder(sink, allowMalformed);
+
+  void close() {
+    _decoder.close();
+    if(_chunkedSink != null) _chunkedSink.close();
+  }
+
+  void add(List<int> chunk) {
+    addSlice(chunk, 0, chunk.length, false);
+  }
+
+  void addSlice(List<int> codeUnits, int startIndex, int endIndex,
+                bool isLast) {
+    _decoder.convert(codeUnits, startIndex, endIndex);
+    if (isLast) close();
+  }
+}
+
+/**
+ * Decodes UTF-8 code units.
+ *
+ * Forwards the decoded strings to the given [StringConversionSink].
+ */
+// TODO(floitsch): make this class public?
+class _Utf8ConversionSink extends ByteConversionSink {
+  static const _MIN_STRING_SIZE = 16;
+
+  final _Utf8Decoder _decoder;
+  final StringConversionSink _chunkedSink;
+  final StringBuffer _buffer;
+  _Utf8ConversionSink(StringConversionSink sink, bool allowMalformed)
+      : this._(sink, new StringBuffer(), allowMalformed);
+
+  _Utf8ConversionSink._(this._chunkedSink, StringBuffer stringBuffer,
+                       bool allowMalformed)
+      : _decoder = new _Utf8Decoder(stringBuffer, allowMalformed),
+        _buffer = stringBuffer;
+
+  void close() {
+    _decoder.close();
+    if (_buffer.isNotEmpty) {
+      String accumulated = _buffer.toString();
+      _buffer.clear();
+      _chunkedSink.addSlice(accumulated, 0, accumulated.length, true);
+    } else  {
+      _chunkedSink.close();
+    }
+  }
+
+  void add(List<int> chunk) {
+    addSlice(chunk, 0, chunk.length, false);
+  }
+
+  void addSlice(List<int> chunk, int startIndex, int endIndex,
+                bool isLast) {
+    _decoder.convert(chunk, startIndex, endIndex);
+    if (_buffer.length > _MIN_STRING_SIZE) {
+      String accumulated = _buffer.toString();
+      _chunkedSink.addSlice(accumulated, 0, accumulated.length, isLast);
+      _buffer.clear();
+      return;
+    }
+    if (isLast) close();
+  }
+}
diff --git a/sdk/lib/convert/utf.dart b/sdk/lib/convert/utf.dart
index 6ce09a9..2f92ef4 100644
--- a/sdk/lib/convert/utf.dart
+++ b/sdk/lib/convert/utf.dart
@@ -62,7 +62,7 @@
 }
 
 /**
- * A [Utf8Encoder] converts strings to their UTF-8 code units (a list of
+ * This class converts strings to their UTF-8 code units (a list of
  * unsigned 8-bit integers).
  */
 class Utf8Encoder extends Converter<String, List<int>> {
@@ -86,6 +86,23 @@
     }
     return encoder._buffer.sublist(0, encoder._bufferIndex);
   }
+
+  /**
+   * Starts a chunked conversion.
+   *
+   * The converter works more efficiently if the given [sink] is a
+   * [ByteConversionSink].
+   */
+  StringConversionSink startChunkedConversion(
+      ChunkedConversionSink<List<int>> sink) {
+    if (sink is! ByteConversionSink) {
+      sink = new ByteConversionSink.from(sink);
+    }
+    return new _Utf8EncoderSink(sink);
+  }
+
+  // Override the base-classes bind, to provide a better type.
+  Stream<List<int>> bind(Stream<String> stream) => super.bind(stream);
 }
 
 /**
@@ -110,8 +127,11 @@
    * writes it to [_buffer].
    *
    * Returns true if the [nextCodeUnit] was combined with the
-   * [leadingSurrogate]. If it wasn't then nextCodeUnit has not been written
-   * yet.
+   * [leadingSurrogate]. If it wasn't then nextCodeUnit was not a trailing
+   * surrogate and has not been written yet.
+   *
+   * It is safe to pass 0 for [nextCodeUnit] in which case only the leading
+   * surrogate is written.
    */
   bool _writeSurrogate(int leadingSurrogate, int nextCodeUnit) {
     if (_isTailSurrogate(nextCodeUnit)) {
@@ -187,6 +207,72 @@
 }
 
 /**
+ * This class encodes chunked strings to UTF-8 code units (unsigned 8-bit
+ * integers).
+ */
+class _Utf8EncoderSink extends _Utf8Encoder with StringConversionSinkMixin {
+
+  final ByteConversionSink _sink;
+
+  _Utf8EncoderSink(this._sink);
+
+  void close() {
+    if (_carry != 0) {
+      // addSlice will call close again, but then the carry must be equal to 0.
+      addSlice("", 0, 0, true);
+      return;
+    }
+    _sink.close();
+  }
+
+  void addSlice(String str, int start, int end, bool isLast) {
+    _bufferIndex = 0;
+
+    if (start == end && !isLast) {
+      return;
+    }
+
+    if (_carry != 0) {
+      int nextCodeUnit = 0;
+      if (start != end) {
+        nextCodeUnit = str.codeUnitAt(start);
+      } else {
+        assert(isLast);
+      }
+      bool wasCombined = _writeSurrogate(_carry, nextCodeUnit);
+      // Either we got a non-empty string, or we must not have been combined.
+      assert(!wasCombined || start != end );
+      if (wasCombined) start++;
+      _carry = 0;
+    }
+    do {
+      start = _fillBuffer(str, start, end);
+      bool isLastSlice = isLast && (start == end);
+      if (start == end - 1 && _isLeadSurrogate(str.codeUnitAt(start))) {
+        if (isLast && _bufferIndex < _buffer.length - 3) {
+          // There is still space for the last incomplete surrogate.
+          // We use a non-surrogate as second argument. This way the
+          // function will just add the surrogate-half to the buffer.
+          bool hasBeenCombined = _writeSurrogate(str.codeUnitAt(start), 0);
+          assert(!hasBeenCombined);
+        } else {
+          // Otherwise store it in the carry. If isLast is true, then
+          // close will flush the last carry.
+          _carry = str.codeUnitAt(start);
+        }
+        start++;
+      }
+      _sink.addSlice(_buffer, 0, _bufferIndex, isLastSlice);
+      _bufferIndex = 0;
+    } while (start < end);
+    if (isLast) close();
+  }
+
+  // TODO(floitsch): implement asUtf8Sink. Sligthly complicated because it
+  // needs to deal with malformed input.
+}
+
+/**
  * This class converts UTF-8 code units (lists of unsigned 8-bit integers)
  * to a string.
  */
@@ -212,18 +298,38 @@
    */
   String convert(List<int> codeUnits) {
     StringBuffer buffer = new StringBuffer();
-    _Utf8Decoder decoder = new _Utf8Decoder(_allowMalformed);
-    decoder.convert(codeUnits, 0, codeUnits.length, buffer);
-    decoder.close(buffer);
+    _Utf8Decoder decoder = new _Utf8Decoder(buffer, _allowMalformed);
+    decoder.convert(codeUnits, 0, codeUnits.length);
+    decoder.close();
     return buffer.toString();
   }
+
+  /**
+   * Starts a chunked conversion.
+   *
+   * The converter works more efficiently if the given [sink] is a
+   * [StringConversionSink].
+   */
+  ByteConversionSink startChunkedConversion(
+      ChunkedConversionSink<String> sink) {
+    StringConversionSink stringSink;
+    if (sink is StringConversionSink) {
+      stringSink = sink;
+    } else {
+      stringSink = new StringConversionSink.from(sink);
+    }
+    return stringSink.asUtf8Sink(_allowMalformed);
+  }
+
+  // Override the base-classes bind, to provide a better type.
+  Stream<String> bind(Stream<List<int>> stream) => super.bind(stream);
 }
 
 // UTF-8 constants.
-const int _ONE_BYTE_LIMIT = 0x7f;   // 7 bytes
-const int _TWO_BYTE_LIMIT = 0x7ff;  // 11 bytes
-const int _THREE_BYTE_LIMIT = 0xffff;  // 16 bytes
-const int _FOUR_BYTE_LIMIT = 0x10ffff;  // 21 bytes, truncated to Unicode max.
+const int _ONE_BYTE_LIMIT = 0x7f;   // 7 bits
+const int _TWO_BYTE_LIMIT = 0x7ff;  // 11 bits
+const int _THREE_BYTE_LIMIT = 0xffff;  // 16 bits
+const int _FOUR_BYTE_LIMIT = 0x10ffff;  // 21 bits, truncated to Unicode max.
 
 // UTF-16 constants.
 const int _SURROGATE_MASK = 0xF800;
@@ -254,12 +360,13 @@
 // TODO(floitsch): make this class public.
 class _Utf8Decoder {
   final bool _allowMalformed;
+  final StringSink _stringSink;
   bool _isFirstCharacter = true;
   int _value = 0;
   int _expectedUnits = 0;
   int _extraUnits = 0;
 
-  _Utf8Decoder(this._allowMalformed);
+  _Utf8Decoder(this._stringSink, this._allowMalformed);
 
   bool get hasPartialInput => _expectedUnits > 0;
 
@@ -270,17 +377,29 @@
       _THREE_BYTE_LIMIT,
       _FOUR_BYTE_LIMIT ];
 
-  void close(StringSink sink) {
+  void close() {
+    flush();
+  }
+
+  /**
+   * Flushes this decoder as if closed.
+   *
+   * This method throws if the input was partial and the decoder was
+   * constructed with `allowMalformed` set to `false`.
+   */
+  void flush() {
     if (hasPartialInput) {
       if (!_allowMalformed) {
         throw new FormatException("Unfinished UTF-8 octet sequence");
       }
-      sink.writeCharCode(_REPLACEMENT_CHARACTER);
+      _stringSink.writeCharCode(_REPLACEMENT_CHARACTER);
+      _value = 0;
+      _expectedUnits = 0;
+      _extraUnits = 0;
     }
   }
 
-  void convert(List<int> codeUnits, int startIndex, int endIndex,
-               StringSink sink) {
+  void convert(List<int> codeUnits, int startIndex, int endIndex) {
     int value = _value;
     int expectedUnits = _expectedUnits;
     int extraUnits = _extraUnits;
@@ -303,7 +422,7 @@
                   "Bad UTF-8 encoding 0x${unit.toRadixString(16)}");
             }
             _isFirstCharacter = false;
-            sink.writeCharCode(_REPLACEMENT_CHARACTER);
+            _stringSink.writeCharCode(_REPLACEMENT_CHARACTER);
             break multibyte;
           } else {
             value = (value << 6) | (unit & 0x3f);
@@ -329,7 +448,7 @@
           value = _REPLACEMENT_CHARACTER;
         }
         if (!_isFirstCharacter || value != _BOM_CHARACTER) {
-          sink.writeCharCode(value);
+          _stringSink.writeCharCode(value);
         }
         _isFirstCharacter = false;
       }
@@ -338,7 +457,7 @@
         int unit = codeUnits[i++];
         if (unit <= _ONE_BYTE_LIMIT) {
           _isFirstCharacter = false;
-          sink.writeCharCode(unit);
+          _stringSink.writeCharCode(unit);
         } else {
           if ((unit & 0xE0) == 0xC0) {
             value = unit & 0x1F;
@@ -363,7 +482,7 @@
           value = _REPLACEMENT_CHARACTER;
           expectedUnits = extraUnits = 0;
           _isFirstCharacter = false;
-          sink.writeCharCode(value);
+          _stringSink.writeCharCode(value);
         }
       }
       break loop;
diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart
index c9f131f..a4f487c 100644
--- a/sdk/lib/html/dart2js/html_dart2js.dart
+++ b/sdk/lib/html/dart2js/html_dart2js.dart
@@ -8893,6 +8893,46 @@
    */
   @Experimental()
   CssRect get marginEdge => new _MarginCssRect(this);
+
+  /** 
+   * Provides the coordinates of the element relative to the top of the 
+   * document. 
+   *
+   * This method is the Dart equivalent to jQuery's 
+   * [offset](http://api.jquery.com/offset/) method.
+   */
+  Point get documentOffset => offsetTo(document.documentElement);
+
+  /** 
+   * Provides the offset of this element's [borderEdge] relative to the
+   * specified [parent].
+   * 
+   * This is the Dart equivalent of jQuery's
+   * [position](http://api.jquery.com/position/) method. Unlike jQuery's
+   * position, however, [parent] can be any parent element of `this`, 
+   * rather than only `this`'s immediate [offsetParent]. If the specified
+   * element is _not_ an offset parent or transitive offset parent to this
+   * element, an [ArgumentError] is thrown.
+   */
+  Point offsetTo(Element parent) {
+    return Element._offsetToHelper(this, parent);
+  }
+
+  static Point _offsetToHelper(Element current, Element parent) {
+    // We're hopping from _offsetParent_ to offsetParent (not just parent), so
+    // offsetParent, "tops out" at BODY. But people could conceivably pass in 
+    // the document.documentElement and I want it to return an absolute offset,
+    // so we have the special case checking for HTML.
+    bool foundAsParent = identical(current, parent) || parent.tagName == 'HTML';
+    if (current == null || identical(current, parent)) {
+      if (foundAsParent) return new Point(0, 0);
+      throw new ArgumentError("Specified element is not a transitive offset "
+          "parent of this element.");
+    } 
+    Element parentOffset = current.offsetParent;
+    Point p = Element._offsetToHelper(parentOffset, parent);
+    return new Point(p.x + current.offsetLeft, p.y + current.offsetTop);
+  }
   // To suppress missing implicit constructor warnings.
   factory Element._() { throw new UnsupportedError("Not supported"); }
 
@@ -16976,6 +17016,7 @@
   TemplateInstance(this.firstNode, this.lastNode, this.model);
 }
 
+
 @DomName('Node')
 class Node extends EventTarget native "Node" {
   List<Node> get nodes {
@@ -17049,14 +17090,28 @@
    */
   String toString() => nodeValue == null ? super.toString() : nodeValue;
 
+
+  /**
+   * Creates a binding to the attribute [name] to the [path] of the [model].
+   *
+   * This can be overridden by custom elements to provide the binding used in
+   * [Node.bind]. This will only create the binding; it will not add it to
+   * [bindings].
+   *
+   * You should not need to call this directly except from [Node.bind].
+   */
+  @Experimental()
+  createBinding(String name, model, String path) =>
+      TemplateElement.mdvPackage(this).createBinding(name, model, path);
+
   /**
    * Binds the attribute [name] to the [path] of the [model].
    * Path is a String of accessors such as `foo.bar.baz`.
+   * Returns the `NodeBinding` instance.
    */
   @Experimental()
-  void bind(String name, model, String path) {
-    TemplateElement.mdvPackage(this).bind(name, model, path);
-  }
+  bind(String name, model, String path) =>
+      TemplateElement.mdvPackage(this).bind(name, model, path);
 
   /** Unbinds the attribute [name]. */
   @Experimental()
@@ -17070,6 +17125,11 @@
     TemplateElement.mdvPackage(this).unbindAll();
   }
 
+  /** Gets the data bindings that are associated with this node. */
+  @Experimental()
+  Map<String, dynamic> get bindings =>
+      TemplateElement.mdvPackage(this).bindings;
+
   /** Gets the template instance that instantiated this node, if any. */
   @Experimental()
   TemplateInstance get templateInstance =>
@@ -23203,6 +23263,11 @@
       JS('void',
          '(self.URL || self.webkitURL).revokeObjectURL(#)', url);
 
+  @JSName('createObjectURL')
+  @DomName('URL.createObjectURL')
+  @DocsEditable()
+  static String _createObjectUrlFromWebKitSource(_WebKitMediaSource source) native;
+
 }
 // Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
 // for details. All rights reserved. Use of this source code is governed by a
diff --git a/sdk/lib/html/dartium/html_dartium.dart b/sdk/lib/html/dartium/html_dartium.dart
index 2981699..516c416 100644
--- a/sdk/lib/html/dartium/html_dartium.dart
+++ b/sdk/lib/html/dartium/html_dartium.dart
@@ -9261,6 +9261,46 @@
    */
   @Experimental()
   CssRect get marginEdge => new _MarginCssRect(this);
+
+  /** 
+   * Provides the coordinates of the element relative to the top of the 
+   * document. 
+   *
+   * This method is the Dart equivalent to jQuery's 
+   * [offset](http://api.jquery.com/offset/) method.
+   */
+  Point get documentOffset => offsetTo(document.documentElement);
+
+  /** 
+   * Provides the offset of this element's [borderEdge] relative to the
+   * specified [parent].
+   * 
+   * This is the Dart equivalent of jQuery's
+   * [position](http://api.jquery.com/position/) method. Unlike jQuery's
+   * position, however, [parent] can be any parent element of `this`, 
+   * rather than only `this`'s immediate [offsetParent]. If the specified
+   * element is _not_ an offset parent or transitive offset parent to this
+   * element, an [ArgumentError] is thrown.
+   */
+  Point offsetTo(Element parent) {
+    return Element._offsetToHelper(this, parent);
+  }
+
+  static Point _offsetToHelper(Element current, Element parent) {
+    // We're hopping from _offsetParent_ to offsetParent (not just parent), so
+    // offsetParent, "tops out" at BODY. But people could conceivably pass in 
+    // the document.documentElement and I want it to return an absolute offset,
+    // so we have the special case checking for HTML.
+    bool foundAsParent = identical(current, parent) || parent.tagName == 'HTML';
+    if (current == null || identical(current, parent)) {
+      if (foundAsParent) return new Point(0, 0);
+      throw new ArgumentError("Specified element is not a transitive offset "
+          "parent of this element.");
+    } 
+    Element parentOffset = current.offsetParent;
+    Point p = Element._offsetToHelper(parentOffset, parent);
+    return new Point(p.x + current.offsetLeft, p.y + current.offsetTop);
+  }
   // To suppress missing implicit constructor warnings.
   factory Element._() { throw new UnsupportedError("Not supported"); }
 
@@ -17982,6 +18022,7 @@
   TemplateInstance(this.firstNode, this.lastNode, this.model);
 }
 
+
 @DomName('Node')
 class Node extends EventTarget {
   List<Node> get nodes {
@@ -18055,14 +18096,28 @@
    */
   String toString() => nodeValue == null ? super.toString() : nodeValue;
 
+
+  /**
+   * Creates a binding to the attribute [name] to the [path] of the [model].
+   *
+   * This can be overridden by custom elements to provide the binding used in
+   * [Node.bind]. This will only create the binding; it will not add it to
+   * [bindings].
+   *
+   * You should not need to call this directly except from [Node.bind].
+   */
+  @Experimental()
+  createBinding(String name, model, String path) =>
+      TemplateElement.mdvPackage(this).createBinding(name, model, path);
+
   /**
    * Binds the attribute [name] to the [path] of the [model].
    * Path is a String of accessors such as `foo.bar.baz`.
+   * Returns the `NodeBinding` instance.
    */
   @Experimental()
-  void bind(String name, model, String path) {
-    TemplateElement.mdvPackage(this).bind(name, model, path);
-  }
+  bind(String name, model, String path) =>
+      TemplateElement.mdvPackage(this).bind(name, model, path);
 
   /** Unbinds the attribute [name]. */
   @Experimental()
@@ -18076,6 +18131,11 @@
     TemplateElement.mdvPackage(this).unbindAll();
   }
 
+  /** Gets the data bindings that are associated with this node. */
+  @Experimental()
+  Map<String, dynamic> get bindings =>
+      TemplateElement.mdvPackage(this).bindings;
+
   /** Gets the template instance that instantiated this node, if any. */
   @Experimental()
   TemplateInstance get templateInstance =>
@@ -24831,9 +24891,34 @@
 @DomName('URL')
 class Url extends NativeFieldWrapperClass1 {
 
-  @DomName('URL.createObjectURL')
+  @DomName('URL._createObjectUrlFromWebKitSource')
   @DocsEditable()
-  static String createObjectUrl(_WebKitMediaSource source) native "URL_createObjectURL_Callback";
+  @Experimental() // untriaged
+  static String _createObjectUrlFromWebKitSource(_WebKitMediaSource source) native "URL__createObjectUrlFromWebKitSource_Callback";
+
+  static String createObjectUrl(blob_OR_source_OR_stream) {
+    if ((blob_OR_source_OR_stream is MediaSource || blob_OR_source_OR_stream == null)) {
+      return _createObjectURL_1(blob_OR_source_OR_stream);
+    }
+    if ((blob_OR_source_OR_stream is _WebKitMediaSource || blob_OR_source_OR_stream == null)) {
+      return _createObjectURL_2(blob_OR_source_OR_stream);
+    }
+    if ((blob_OR_source_OR_stream is MediaStream || blob_OR_source_OR_stream == null)) {
+      return _createObjectURL_3(blob_OR_source_OR_stream);
+    }
+    if ((blob_OR_source_OR_stream is Blob || blob_OR_source_OR_stream == null)) {
+      return _createObjectURL_4(blob_OR_source_OR_stream);
+    }
+    throw new ArgumentError("Incorrect number or type of arguments");
+  }
+
+  static String _createObjectURL_1(blob_OR_source_OR_stream) native "URL__createObjectURL_1_Callback";
+
+  static String _createObjectURL_2(blob_OR_source_OR_stream) native "URL__createObjectURL_2_Callback";
+
+  static String _createObjectURL_3(blob_OR_source_OR_stream) native "URL__createObjectURL_3_Callback";
+
+  static String _createObjectURL_4(blob_OR_source_OR_stream) native "URL__createObjectURL_4_Callback";
 
   @DomName('URL.createObjectUrlFromBlob')
   @DocsEditable()
diff --git a/sdk/lib/io/secure_server_socket.dart b/sdk/lib/io/secure_server_socket.dart
index 8ae021e..581a76c 100644
--- a/sdk/lib/io/secure_server_socket.dart
+++ b/sdk/lib/io/secure_server_socket.dart
@@ -95,9 +95,10 @@
   int get port => _socket.port;
 
   /**
-   * Closes the socket.
+   * Closes the socket. The returned future completes when the socket
+   * is fully closed and is no longer bound.
    */
-  void close() => _socket.close();
+  Future<SecureServerSocket> close() => _socket.close().then((_) => this);
 }
 
 
@@ -204,11 +205,12 @@
   int get port => _socket.port;
 
   /**
-   * Closes the socket.
+   * Closes the socket. The returned future completes when the socket
+   * is fully closed and is no longer bound.
    */
-  void close() {
+  Future<RawSecureServerSocket> close() {
     _closed = true;
-    _socket.close();
+    return _socket.close().then((_) => this);
   }
 
   void _onData(RawSocket connection) {
diff --git a/sdk/lib/io/secure_socket.dart b/sdk/lib/io/secure_socket.dart
index 6da8ec6..1867050 100644
--- a/sdk/lib/io/secure_socket.dart
+++ b/sdk/lib/io/secure_socket.dart
@@ -439,6 +439,7 @@
   bool _socketClosedWrite = false;  // The network socket is closed for writing.
   bool _closedRead = false;  // The secure socket has fired an onClosed event.
   bool _closedWrite = false;  // The secure socket has been closed for writing.
+  Completer _closeCompleter = new Completer();  // The network socket is gone.
   _FilterStatus _filterStatus = new _FilterStatus();
   bool _connectPending = false;
   bool _filterPending = false;
@@ -516,7 +517,7 @@
     _secureFilter.registerHandshakeCompleteCallback(
         _secureHandshakeCompleteHandler);
     if (onBadCertificate != null) {
-      _secureFilter.registerBadCertificateCallback(onBadCertificate);
+      _secureFilter.registerBadCertificateCallback(_onBadCertificateWrapper);
     }
     var futureSocket;
     if (socket == null) {
@@ -613,15 +614,22 @@
     return _secureFilter.buffers[READ_PLAINTEXT].length;
   }
 
-  void close() {
+  Future<RawSecureSocket> close() {
     shutdown(SocketDirection.BOTH);
+    return _closeCompleter.future;
+  }
+
+  void _completeCloseCompleter([dummy]) {
+    if (!_closeCompleter.isCompleted) _closeCompleter.complete(this);
   }
 
   void _close() {
     _closedWrite = true;
     _closedRead = true;
     if (_socket != null) {
-      _socket.close();
+      _socket.close().then(_completeCloseCompleter);
+    } else {
+      _completeCloseCompleter();
     }
     _socketClosedWrite = true;
     _socketClosedRead = true;
@@ -720,6 +728,14 @@
 
   X509Certificate get peerCertificate => _secureFilter.peerCertificate;
 
+  bool _onBadCertificateWrapper(X509Certificate certificate) {
+    if (onBadCertificate == null) return false;
+    var result = onBadCertificate(certificate);
+    if (result is bool) return result;
+    throw new ArgumentError(
+        "onBadCertificate callback returned non-boolean $result");
+  }
+
   bool setOption(SocketOption option, bool enabled) {
     if (_socket == null) return false;
     return _socket.setOption(option, enabled);
@@ -761,9 +777,6 @@
     } else if (_connectPending) {
       // _connectPending is true after the underlying connection has been
       // made, but before the handshake has completed.
-      if (e is! TlsException) {
-        e = new HandshakeException("$e", null);
-      }
       _handshakeComplete.completeError(e);
     } else {
       _controller.addError(e);
diff --git a/sdk/lib/io/socket.dart b/sdk/lib/io/socket.dart
index e82adc7..8c2a21b 100644
--- a/sdk/lib/io/socket.dart
+++ b/sdk/lib/io/socket.dart
@@ -202,9 +202,10 @@
   int get port;
 
   /**
-   * Closes the socket.
+   * Closes the socket. The returned future completes when the socket
+   * is fully closed and is no longer bound.
    */
-  void close();
+  Future<RawServerSocket> close();
 }
 
 
@@ -260,7 +261,7 @@
    * Closes the socket. The returned future completes when the socket
    * is fully closed and is no longer bound.
    */
-  Future close();
+  Future<ServerSocket> close();
 }
 
 /**
@@ -375,11 +376,14 @@
   String get remoteHost;
 
   /**
-   * Closes the socket. Calling [close] will never throw an exception
+   * Closes the socket. Returns a Future that completes with [this] when the
+   * underlying connection is completely destroyed.
+   *
+   * Calling [close] will never throw an exception
    * and calling it several times is supported. Calling [close] can result in
    * a [RawSocketEvent.READ_CLOSED] event.
    */
-  void close();
+  Future<RawSocket> close();
 
   /**
    * Shutdown the socket in the [direction]. Calling [shutdown] will never
diff --git a/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart b/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart
index 9190f36..0c761ff 100644
--- a/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart
+++ b/sdk/lib/typed_data/dart2js/typed_data_dart2js.dart
@@ -9,6 +9,7 @@
 import 'dart:_collection-dev';
 import 'dart:_js_helper' show Creates, JavaScriptIndexingBehavior, JSName, Null, Returns;
 import 'dart:_foreign_helper' show JS;
+import 'dart:math' as Math;
 
 /**
  * Describes endianness to be used when accessing a sequence of bytes.
@@ -647,38 +648,865 @@
 }
 
 
-abstract class Float32x4List implements List<Float32x4>, TypedData {
-  factory Float32x4List(int length) {
-    throw new UnsupportedError("Float32x4List not supported by dart2js.");
+class Float32x4List
+    extends Object with ListMixin<Float32x4>, FixedLengthListMixin<Float32x4>
+    implements List<Float32x4>, TypedData {
+
+  final Float32List _storage;
+
+  ByteBuffer get buffer => _storage.buffer;
+
+  int get lengthInBytes => _storage.lengthInBytes;
+
+  int get offsetInBytes => _storage.offsetInBytes;
+
+  final int elementSizeInBytes = 16;
+
+  void _invalidIndex(int index, int length) {
+    if (index < 0 || index >= length) {
+      throw new RangeError.range(index, 0, length);
+    } else {
+      throw new ArgumentError('Invalid list index $index');
+    }
   }
 
-  factory Float32x4List.view(ByteBuffer buffer,
-                             [int offsetInBytes = 0, int length]) {
-    throw new UnsupportedError("Float32x4List not supported by dart2js.");
+  void _checkIndex(int index, int length) {
+    if (JS('bool', '(# >>> 0 != #)', index, index) || index >= length) {
+      _invalidIndex(index, length);
+    }
   }
 
+  int _checkSublistArguments(int start, int end, int length) {
+    // For `sublist` the [start] and [end] indices are allowed to be equal to
+    // [length]. However, [_checkIndex] only allows incides in the range
+    // 0 .. length - 1. We therefore increment the [length] argument by one
+    // for the [_checkIndex] checks.
+    _checkIndex(start, length + 1);
+    if (end == null) return length;
+    _checkIndex(end, length + 1);
+    if (start > end) throw new RangeError.range(start, 0, end);
+    return end;
+  }
+
+  Float32x4List(int length) : _storage = new Float32List(length*4);
+
+  Float32x4List._externalStorage(Float32List storage) : _storage = storage;
+
+  Float32x4List._slowFromList(List<Float32x4> list)
+      : _storage = new Float32List(list.length * 4) {
+    for (int i = 0; i < list.length; i++) {
+      var e = list[i];
+      _storage[(i*4)+0] = e.x;
+      _storage[(i*4)+1] = e.y;
+      _storage[(i*4)+2] = e.z;
+      _storage[(i*4)+3] = e.w;
+    }
+  }
+
+  factory Float32x4List.fromList(List<Float32x4> list) {
+    if (list is Float32x4List) {
+      Float32x4List nativeList = list as Float32x4List;
+      return new Float32x4List._externalStorage(
+          new Float32List.fromList(nativeList._storage));
+    } else {
+      return new Float32x4List._slowFromList(list);
+    }
+  }
+
+  Float32x4List.view(ByteBuffer buffer,
+                     [int byteOffset = 0, int length])
+      : _storage = new Float32List.view(buffer, byteOffset, length);
+
   static const int BYTES_PER_ELEMENT = 16;
-}
 
+  int get length => _storage.length ~/ 4;
 
-abstract class Float32x4 {
-  factory Float32x4(double x, double y, double z, double w) {
-    throw new UnsupportedError("Float32x4 not supported by dart2js.");
+  Float32x4 operator[](int index) {
+    _checkIndex(index, length);
+    double _x = _storage[(index*4)+0];
+    double _y = _storage[(index*4)+1];
+    double _z = _storage[(index*4)+2];
+    double _w = _storage[(index*4)+3];
+    return new Float32x4(_x, _y, _z, _w);
   }
-  factory Float32x4.splat(double v) {
-    throw new UnsupportedError("Float32x4 not supported by dart2js.");
+
+  void operator[]=(int index, Float32x4 value) {
+    _checkIndex(index, length);
+    _storage[(index*4)+0] = value._storage[0];
+    _storage[(index*4)+1] = value._storage[1];
+    _storage[(index*4)+2] = value._storage[2];
+    _storage[(index*4)+3] = value._storage[3];
   }
-  factory Float32x4.zero() {
-    throw new UnsupportedError("Float32x4 not supported by dart2js.");
+
+  List<Float32x4> sublist(int start, [int end]) {
+    end = _checkSublistArguments(start, end, length);
+    return new Float32x4List._externalStorage(_storage.sublist(start*4, end*4));
   }
 }
 
 
-abstract class Uint32x4 {
-  factory Uint32x4(int x, int y, int z, int w) {
-    throw new UnsupportedError("Uint32x4 not supported by dart2js.");
+class Float32x4 {
+  final _storage = new Float32List(4);
+
+  Float32x4(double x, double y, double z, double w) {
+    _storage[0] = x;
+    _storage[1] = y;
+    _storage[2] = z;
+    _storage[3] = w;
   }
-  factory Uint32x4.bool(bool x, bool y, bool z, bool w) {
-    throw new UnsupportedError("Uint32x4 not supported by dart2js.");
+  Float32x4.splat(double v) {
+    _storage[0] = v;
+    _storage[1] = v;
+    _storage[2] = v;
+    _storage[3] = v;
+  }
+  Float32x4.zero();
+
+   /// Addition operator.
+  Float32x4 operator+(Float32x4 other) {
+    double _x = _storage[0] + other._storage[0];
+    double _y = _storage[1] + other._storage[1];
+    double _z = _storage[2] + other._storage[2];
+    double _w = _storage[3] + other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Negate operator.
+  Float32x4 operator-() {
+    double _x = -_storage[0];
+    double _y = -_storage[1];
+    double _z = -_storage[2];
+    double _w = -_storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Subtraction operator.
+  Float32x4 operator-(Float32x4 other) {
+    double _x = _storage[0] - other._storage[0];
+    double _y = _storage[1] - other._storage[1];
+    double _z = _storage[2] - other._storage[2];
+    double _w = _storage[3] - other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Multiplication operator.
+  Float32x4 operator*(Float32x4 other) {
+    double _x = _storage[0] * other._storage[0];
+    double _y = _storage[1] * other._storage[1];
+    double _z = _storage[2] * other._storage[2];
+    double _w = _storage[3] * other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Division operator.
+  Float32x4 operator/(Float32x4 other) {
+    double _x = _storage[0] / other._storage[0];
+    double _y = _storage[1] / other._storage[1];
+    double _z = _storage[2] / other._storage[2];
+    double _w = _storage[3] / other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Relational less than.
+  Uint32x4 lessThan(Float32x4 other) {
+    bool _cx = _storage[0] < other._storage[0];
+    bool _cy = _storage[1] < other._storage[1];
+    bool _cz = _storage[2] < other._storage[2];
+    bool _cw = _storage[3] < other._storage[3];
+    return new Uint32x4(_cx == true ? 0xFFFFFFFF : 0x0,
+                        _cy == true ? 0xFFFFFFFF : 0x0,
+                        _cz == true ? 0xFFFFFFFF : 0x0,
+                        _cw == true ? 0xFFFFFFFF : 0x0);
+  }
+
+  /// Relational less than or equal.
+  Uint32x4 lessThanOrEqual(Float32x4 other) {
+    bool _cx = _storage[0] <= other._storage[0];
+    bool _cy = _storage[1] <= other._storage[1];
+    bool _cz = _storage[2] <= other._storage[2];
+    bool _cw = _storage[3] <= other._storage[3];
+    return new Uint32x4(_cx == true ? 0xFFFFFFFF : 0x0,
+                        _cy == true ? 0xFFFFFFFF : 0x0,
+                        _cz == true ? 0xFFFFFFFF : 0x0,
+                        _cw == true ? 0xFFFFFFFF : 0x0);
+  }
+
+  /// Relational greater than.
+  Uint32x4 greaterThan(Float32x4 other) {
+    bool _cx = _storage[0] > other._storage[0];
+    bool _cy = _storage[1] > other._storage[1];
+    bool _cz = _storage[2] > other._storage[2];
+    bool _cw = _storage[3] > other._storage[3];
+    return new Uint32x4(_cx == true ? 0xFFFFFFFF : 0x0,
+                        _cy == true ? 0xFFFFFFFF : 0x0,
+                        _cz == true ? 0xFFFFFFFF : 0x0,
+                        _cw == true ? 0xFFFFFFFF : 0x0);
+  }
+
+  /// Relational greater than or equal.
+  Uint32x4 greaterThanOrEqual(Float32x4 other) {
+    bool _cx = _storage[0] >= other._storage[0];
+    bool _cy = _storage[1] >= other._storage[1];
+    bool _cz = _storage[2] >= other._storage[2];
+    bool _cw = _storage[3] >= other._storage[3];
+    return new Uint32x4(_cx == true ? 0xFFFFFFFF : 0x0,
+                        _cy == true ? 0xFFFFFFFF : 0x0,
+                        _cz == true ? 0xFFFFFFFF : 0x0,
+                        _cw == true ? 0xFFFFFFFF : 0x0);
+  }
+
+  /// Relational equal.
+  Uint32x4 equal(Float32x4 other) {
+    bool _cx = _storage[0] == other._storage[0];
+    bool _cy = _storage[1] == other._storage[1];
+    bool _cz = _storage[2] == other._storage[2];
+    bool _cw = _storage[3] == other._storage[3];
+    return new Uint32x4(_cx == true ? 0xFFFFFFFF : 0x0,
+                        _cy == true ? 0xFFFFFFFF : 0x0,
+                        _cz == true ? 0xFFFFFFFF : 0x0,
+                        _cw == true ? 0xFFFFFFFF : 0x0);
+  }
+
+  /// Relational not-equal.
+  Uint32x4 notEqual(Float32x4 other) {
+    bool _cx = _storage[0] != other._storage[0];
+    bool _cy = _storage[1] != other._storage[1];
+    bool _cz = _storage[2] != other._storage[2];
+    bool _cw = _storage[3] != other._storage[3];
+    return new Uint32x4(_cx == true ? 0xFFFFFFFF : 0x0,
+                        _cy == true ? 0xFFFFFFFF : 0x0,
+                        _cz == true ? 0xFFFFFFFF : 0x0,
+                        _cw == true ? 0xFFFFFFFF : 0x0);
+  }
+
+  /// Returns a copy of [this] each lane being scaled by [s].
+  Float32x4 scale(double s) {
+    double _x = s * _storage[0];
+    double _y = s * _storage[1];
+    double _z = s * _storage[2];
+    double _w = s * _storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns the absolute value of this [Float32x4].
+  Float32x4 abs() {
+    double _x = _storage[0].abs();
+    double _y = _storage[1].abs();
+    double _z = _storage[2].abs();
+    double _w = _storage[3].abs();
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Clamps [this] to be in the range [lowerLimit]-[upperLimit].
+  Float32x4 clamp(Float32x4 lowerLimit, Float32x4 upperLimit) {
+    double _lx = lowerLimit._storage[0];
+    double _ly = lowerLimit._storage[1];
+    double _lz = lowerLimit._storage[2];
+    double _lw = lowerLimit._storage[3];
+    double _ux = upperLimit._storage[0];
+    double _uy = upperLimit._storage[1];
+    double _uz = upperLimit._storage[2];
+    double _uw = upperLimit._storage[3];
+    double _x = _storage[0];
+    double _y = _storage[1];
+    double _z = _storage[2];
+    double _w = _storage[3];
+    _x = _x < _lx ? _lx : _x;
+    _x = _x > _ux ? _ux : _x;
+    _y = _y < _ly ? _ly : _y;
+    _y = _y > _uy ? _uy : _y;
+    _z = _z < _lz ? _lz : _z;
+    _z = _z > _uz ? _uz : _z;
+    _w = _w < _lw ? _lw : _w;
+    _w = _w > _uw ? _uw : _w;
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Extracted x value.
+  double get x => _storage[0];
+  /// Extracted y value.
+  double get y => _storage[1];
+  /// Extracted z value.
+  double get z => _storage[2];
+  /// Extracted w value.
+  double get w => _storage[3];
+
+  Float32x4 get xxxx => _shuffle(0x0);
+  Float32x4 get xxxy => _shuffle(0x40);
+  Float32x4 get xxxz => _shuffle(0x80);
+  Float32x4 get xxxw => _shuffle(0xC0);
+  Float32x4 get xxyx => _shuffle(0x10);
+  Float32x4 get xxyy => _shuffle(0x50);
+  Float32x4 get xxyz => _shuffle(0x90);
+  Float32x4 get xxyw => _shuffle(0xD0);
+  Float32x4 get xxzx => _shuffle(0x20);
+  Float32x4 get xxzy => _shuffle(0x60);
+  Float32x4 get xxzz => _shuffle(0xA0);
+  Float32x4 get xxzw => _shuffle(0xE0);
+  Float32x4 get xxwx => _shuffle(0x30);
+  Float32x4 get xxwy => _shuffle(0x70);
+  Float32x4 get xxwz => _shuffle(0xB0);
+  Float32x4 get xxww => _shuffle(0xF0);
+  Float32x4 get xyxx => _shuffle(0x4);
+  Float32x4 get xyxy => _shuffle(0x44);
+  Float32x4 get xyxz => _shuffle(0x84);
+  Float32x4 get xyxw => _shuffle(0xC4);
+  Float32x4 get xyyx => _shuffle(0x14);
+  Float32x4 get xyyy => _shuffle(0x54);
+  Float32x4 get xyyz => _shuffle(0x94);
+  Float32x4 get xyyw => _shuffle(0xD4);
+  Float32x4 get xyzx => _shuffle(0x24);
+  Float32x4 get xyzy => _shuffle(0x64);
+  Float32x4 get xyzz => _shuffle(0xA4);
+  Float32x4 get xyzw => _shuffle(0xE4);
+  Float32x4 get xywx => _shuffle(0x34);
+  Float32x4 get xywy => _shuffle(0x74);
+  Float32x4 get xywz => _shuffle(0xB4);
+  Float32x4 get xyww => _shuffle(0xF4);
+  Float32x4 get xzxx => _shuffle(0x8);
+  Float32x4 get xzxy => _shuffle(0x48);
+  Float32x4 get xzxz => _shuffle(0x88);
+  Float32x4 get xzxw => _shuffle(0xC8);
+  Float32x4 get xzyx => _shuffle(0x18);
+  Float32x4 get xzyy => _shuffle(0x58);
+  Float32x4 get xzyz => _shuffle(0x98);
+  Float32x4 get xzyw => _shuffle(0xD8);
+  Float32x4 get xzzx => _shuffle(0x28);
+  Float32x4 get xzzy => _shuffle(0x68);
+  Float32x4 get xzzz => _shuffle(0xA8);
+  Float32x4 get xzzw => _shuffle(0xE8);
+  Float32x4 get xzwx => _shuffle(0x38);
+  Float32x4 get xzwy => _shuffle(0x78);
+  Float32x4 get xzwz => _shuffle(0xB8);
+  Float32x4 get xzww => _shuffle(0xF8);
+  Float32x4 get xwxx => _shuffle(0xC);
+  Float32x4 get xwxy => _shuffle(0x4C);
+  Float32x4 get xwxz => _shuffle(0x8C);
+  Float32x4 get xwxw => _shuffle(0xCC);
+  Float32x4 get xwyx => _shuffle(0x1C);
+  Float32x4 get xwyy => _shuffle(0x5C);
+  Float32x4 get xwyz => _shuffle(0x9C);
+  Float32x4 get xwyw => _shuffle(0xDC);
+  Float32x4 get xwzx => _shuffle(0x2C);
+  Float32x4 get xwzy => _shuffle(0x6C);
+  Float32x4 get xwzz => _shuffle(0xAC);
+  Float32x4 get xwzw => _shuffle(0xEC);
+  Float32x4 get xwwx => _shuffle(0x3C);
+  Float32x4 get xwwy => _shuffle(0x7C);
+  Float32x4 get xwwz => _shuffle(0xBC);
+  Float32x4 get xwww => _shuffle(0xFC);
+  Float32x4 get yxxx => _shuffle(0x1);
+  Float32x4 get yxxy => _shuffle(0x41);
+  Float32x4 get yxxz => _shuffle(0x81);
+  Float32x4 get yxxw => _shuffle(0xC1);
+  Float32x4 get yxyx => _shuffle(0x11);
+  Float32x4 get yxyy => _shuffle(0x51);
+  Float32x4 get yxyz => _shuffle(0x91);
+  Float32x4 get yxyw => _shuffle(0xD1);
+  Float32x4 get yxzx => _shuffle(0x21);
+  Float32x4 get yxzy => _shuffle(0x61);
+  Float32x4 get yxzz => _shuffle(0xA1);
+  Float32x4 get yxzw => _shuffle(0xE1);
+  Float32x4 get yxwx => _shuffle(0x31);
+  Float32x4 get yxwy => _shuffle(0x71);
+  Float32x4 get yxwz => _shuffle(0xB1);
+  Float32x4 get yxww => _shuffle(0xF1);
+  Float32x4 get yyxx => _shuffle(0x5);
+  Float32x4 get yyxy => _shuffle(0x45);
+  Float32x4 get yyxz => _shuffle(0x85);
+  Float32x4 get yyxw => _shuffle(0xC5);
+  Float32x4 get yyyx => _shuffle(0x15);
+  Float32x4 get yyyy => _shuffle(0x55);
+  Float32x4 get yyyz => _shuffle(0x95);
+  Float32x4 get yyyw => _shuffle(0xD5);
+  Float32x4 get yyzx => _shuffle(0x25);
+  Float32x4 get yyzy => _shuffle(0x65);
+  Float32x4 get yyzz => _shuffle(0xA5);
+  Float32x4 get yyzw => _shuffle(0xE5);
+  Float32x4 get yywx => _shuffle(0x35);
+  Float32x4 get yywy => _shuffle(0x75);
+  Float32x4 get yywz => _shuffle(0xB5);
+  Float32x4 get yyww => _shuffle(0xF5);
+  Float32x4 get yzxx => _shuffle(0x9);
+  Float32x4 get yzxy => _shuffle(0x49);
+  Float32x4 get yzxz => _shuffle(0x89);
+  Float32x4 get yzxw => _shuffle(0xC9);
+  Float32x4 get yzyx => _shuffle(0x19);
+  Float32x4 get yzyy => _shuffle(0x59);
+  Float32x4 get yzyz => _shuffle(0x99);
+  Float32x4 get yzyw => _shuffle(0xD9);
+  Float32x4 get yzzx => _shuffle(0x29);
+  Float32x4 get yzzy => _shuffle(0x69);
+  Float32x4 get yzzz => _shuffle(0xA9);
+  Float32x4 get yzzw => _shuffle(0xE9);
+  Float32x4 get yzwx => _shuffle(0x39);
+  Float32x4 get yzwy => _shuffle(0x79);
+  Float32x4 get yzwz => _shuffle(0xB9);
+  Float32x4 get yzww => _shuffle(0xF9);
+  Float32x4 get ywxx => _shuffle(0xD);
+  Float32x4 get ywxy => _shuffle(0x4D);
+  Float32x4 get ywxz => _shuffle(0x8D);
+  Float32x4 get ywxw => _shuffle(0xCD);
+  Float32x4 get ywyx => _shuffle(0x1D);
+  Float32x4 get ywyy => _shuffle(0x5D);
+  Float32x4 get ywyz => _shuffle(0x9D);
+  Float32x4 get ywyw => _shuffle(0xDD);
+  Float32x4 get ywzx => _shuffle(0x2D);
+  Float32x4 get ywzy => _shuffle(0x6D);
+  Float32x4 get ywzz => _shuffle(0xAD);
+  Float32x4 get ywzw => _shuffle(0xED);
+  Float32x4 get ywwx => _shuffle(0x3D);
+  Float32x4 get ywwy => _shuffle(0x7D);
+  Float32x4 get ywwz => _shuffle(0xBD);
+  Float32x4 get ywww => _shuffle(0xFD);
+  Float32x4 get zxxx => _shuffle(0x2);
+  Float32x4 get zxxy => _shuffle(0x42);
+  Float32x4 get zxxz => _shuffle(0x82);
+  Float32x4 get zxxw => _shuffle(0xC2);
+  Float32x4 get zxyx => _shuffle(0x12);
+  Float32x4 get zxyy => _shuffle(0x52);
+  Float32x4 get zxyz => _shuffle(0x92);
+  Float32x4 get zxyw => _shuffle(0xD2);
+  Float32x4 get zxzx => _shuffle(0x22);
+  Float32x4 get zxzy => _shuffle(0x62);
+  Float32x4 get zxzz => _shuffle(0xA2);
+  Float32x4 get zxzw => _shuffle(0xE2);
+  Float32x4 get zxwx => _shuffle(0x32);
+  Float32x4 get zxwy => _shuffle(0x72);
+  Float32x4 get zxwz => _shuffle(0xB2);
+  Float32x4 get zxww => _shuffle(0xF2);
+  Float32x4 get zyxx => _shuffle(0x6);
+  Float32x4 get zyxy => _shuffle(0x46);
+  Float32x4 get zyxz => _shuffle(0x86);
+  Float32x4 get zyxw => _shuffle(0xC6);
+  Float32x4 get zyyx => _shuffle(0x16);
+  Float32x4 get zyyy => _shuffle(0x56);
+  Float32x4 get zyyz => _shuffle(0x96);
+  Float32x4 get zyyw => _shuffle(0xD6);
+  Float32x4 get zyzx => _shuffle(0x26);
+  Float32x4 get zyzy => _shuffle(0x66);
+  Float32x4 get zyzz => _shuffle(0xA6);
+  Float32x4 get zyzw => _shuffle(0xE6);
+  Float32x4 get zywx => _shuffle(0x36);
+  Float32x4 get zywy => _shuffle(0x76);
+  Float32x4 get zywz => _shuffle(0xB6);
+  Float32x4 get zyww => _shuffle(0xF6);
+  Float32x4 get zzxx => _shuffle(0xA);
+  Float32x4 get zzxy => _shuffle(0x4A);
+  Float32x4 get zzxz => _shuffle(0x8A);
+  Float32x4 get zzxw => _shuffle(0xCA);
+  Float32x4 get zzyx => _shuffle(0x1A);
+  Float32x4 get zzyy => _shuffle(0x5A);
+  Float32x4 get zzyz => _shuffle(0x9A);
+  Float32x4 get zzyw => _shuffle(0xDA);
+  Float32x4 get zzzx => _shuffle(0x2A);
+  Float32x4 get zzzy => _shuffle(0x6A);
+  Float32x4 get zzzz => _shuffle(0xAA);
+  Float32x4 get zzzw => _shuffle(0xEA);
+  Float32x4 get zzwx => _shuffle(0x3A);
+  Float32x4 get zzwy => _shuffle(0x7A);
+  Float32x4 get zzwz => _shuffle(0xBA);
+  Float32x4 get zzww => _shuffle(0xFA);
+  Float32x4 get zwxx => _shuffle(0xE);
+  Float32x4 get zwxy => _shuffle(0x4E);
+  Float32x4 get zwxz => _shuffle(0x8E);
+  Float32x4 get zwxw => _shuffle(0xCE);
+  Float32x4 get zwyx => _shuffle(0x1E);
+  Float32x4 get zwyy => _shuffle(0x5E);
+  Float32x4 get zwyz => _shuffle(0x9E);
+  Float32x4 get zwyw => _shuffle(0xDE);
+  Float32x4 get zwzx => _shuffle(0x2E);
+  Float32x4 get zwzy => _shuffle(0x6E);
+  Float32x4 get zwzz => _shuffle(0xAE);
+  Float32x4 get zwzw => _shuffle(0xEE);
+  Float32x4 get zwwx => _shuffle(0x3E);
+  Float32x4 get zwwy => _shuffle(0x7E);
+  Float32x4 get zwwz => _shuffle(0xBE);
+  Float32x4 get zwww => _shuffle(0xFE);
+  Float32x4 get wxxx => _shuffle(0x3);
+  Float32x4 get wxxy => _shuffle(0x43);
+  Float32x4 get wxxz => _shuffle(0x83);
+  Float32x4 get wxxw => _shuffle(0xC3);
+  Float32x4 get wxyx => _shuffle(0x13);
+  Float32x4 get wxyy => _shuffle(0x53);
+  Float32x4 get wxyz => _shuffle(0x93);
+  Float32x4 get wxyw => _shuffle(0xD3);
+  Float32x4 get wxzx => _shuffle(0x23);
+  Float32x4 get wxzy => _shuffle(0x63);
+  Float32x4 get wxzz => _shuffle(0xA3);
+  Float32x4 get wxzw => _shuffle(0xE3);
+  Float32x4 get wxwx => _shuffle(0x33);
+  Float32x4 get wxwy => _shuffle(0x73);
+  Float32x4 get wxwz => _shuffle(0xB3);
+  Float32x4 get wxww => _shuffle(0xF3);
+  Float32x4 get wyxx => _shuffle(0x7);
+  Float32x4 get wyxy => _shuffle(0x47);
+  Float32x4 get wyxz => _shuffle(0x87);
+  Float32x4 get wyxw => _shuffle(0xC7);
+  Float32x4 get wyyx => _shuffle(0x17);
+  Float32x4 get wyyy => _shuffle(0x57);
+  Float32x4 get wyyz => _shuffle(0x97);
+  Float32x4 get wyyw => _shuffle(0xD7);
+  Float32x4 get wyzx => _shuffle(0x27);
+  Float32x4 get wyzy => _shuffle(0x67);
+  Float32x4 get wyzz => _shuffle(0xA7);
+  Float32x4 get wyzw => _shuffle(0xE7);
+  Float32x4 get wywx => _shuffle(0x37);
+  Float32x4 get wywy => _shuffle(0x77);
+  Float32x4 get wywz => _shuffle(0xB7);
+  Float32x4 get wyww => _shuffle(0xF7);
+  Float32x4 get wzxx => _shuffle(0xB);
+  Float32x4 get wzxy => _shuffle(0x4B);
+  Float32x4 get wzxz => _shuffle(0x8B);
+  Float32x4 get wzxw => _shuffle(0xCB);
+  Float32x4 get wzyx => _shuffle(0x1B);
+  Float32x4 get wzyy => _shuffle(0x5B);
+  Float32x4 get wzyz => _shuffle(0x9B);
+  Float32x4 get wzyw => _shuffle(0xDB);
+  Float32x4 get wzzx => _shuffle(0x2B);
+  Float32x4 get wzzy => _shuffle(0x6B);
+  Float32x4 get wzzz => _shuffle(0xAB);
+  Float32x4 get wzzw => _shuffle(0xEB);
+  Float32x4 get wzwx => _shuffle(0x3B);
+  Float32x4 get wzwy => _shuffle(0x7B);
+  Float32x4 get wzwz => _shuffle(0xBB);
+  Float32x4 get wzww => _shuffle(0xFB);
+  Float32x4 get wwxx => _shuffle(0xF);
+  Float32x4 get wwxy => _shuffle(0x4F);
+  Float32x4 get wwxz => _shuffle(0x8F);
+  Float32x4 get wwxw => _shuffle(0xCF);
+  Float32x4 get wwyx => _shuffle(0x1F);
+  Float32x4 get wwyy => _shuffle(0x5F);
+  Float32x4 get wwyz => _shuffle(0x9F);
+  Float32x4 get wwyw => _shuffle(0xDF);
+  Float32x4 get wwzx => _shuffle(0x2F);
+  Float32x4 get wwzy => _shuffle(0x6F);
+  Float32x4 get wwzz => _shuffle(0xAF);
+  Float32x4 get wwzw => _shuffle(0xEF);
+  Float32x4 get wwwx => _shuffle(0x3F);
+  Float32x4 get wwwy => _shuffle(0x7F);
+  Float32x4 get wwwz => _shuffle(0xBF);
+  Float32x4 get wwww => _shuffle(0xFF);
+
+  Float32x4 _shuffle(int m) {
+    double _x = _storage[m & 0x3];
+    double _y = _storage[(m >> 2) & 0x3];
+    double _z = _storage[(m >> 4) & 0x3];
+    double _w = _storage[(m >> 6) & 0x3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Float32x4] with values in the X and Y lanes
+  /// replaced with the values in the Z and W lanes of [other].
+  Float32x4 withZWInXY(Float32x4 other) {
+    double _x = other._storage[2];
+    double _y = other._storage[3];
+    double _z = _storage[2];
+    double _w = _storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Float32x4] with the X and Y lane values
+  /// from [this] and [other] interleaved.
+  Float32x4 interleaveXY(Float32x4 other) {
+    double _x = _storage[0];
+    double _y = other._storage[0];
+    double _z = _storage[1];
+    double _w = other._storage[1];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Float32x4] with the Z and W lane values
+  /// from [this] and [other] interleaved.
+  Float32x4 interleaveZW(Float32x4 other) {
+    double _x = _storage[2];
+    double _y = other._storage[2];
+    double _z = _storage[3];
+    double _w = other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Float32x4] with the X and Y lane value pairs
+  /// from [this] and [other] interleaved.
+  Float32x4 interleaveXYPairs(Float32x4 other) {
+    double _x = _storage[0];
+    double _y = _storage[1];
+    double _z = other._storage[0];
+    double _w = other._storage[1];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Float32x4] with the Z and W lane value pairs
+  /// from [this] and [other] interleaved.
+  Float32x4 interleaveZWPairs(Float32x4 other) {
+    double _x = _storage[2];
+    double _y = _storage[3];
+    double _z = other._storage[2];
+    double _w = other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  Float32x4 withX(double x) {
+    double _x = x;
+    double _y = _storage[1];
+    double _z = _storage[2];
+    double _w = _storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  Float32x4 withY(double y) {
+    double _x = _storage[0];
+    double _y = y;
+    double _z = _storage[2];
+    double _w = _storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  Float32x4 withZ(double z) {
+    double _x = _storage[0];
+    double _y = _storage[1];
+    double _z = z;
+    double _w = _storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  Float32x4 withW(double w) {
+    double _x = _storage[0];
+    double _y = _storage[1];
+    double _z = _storage[2];
+    double _w = w;
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns the lane-wise minimum value in [this] or [other].
+  Float32x4 min(Float32x4 other) {
+    double _x = _storage[0] < other._storage[0] ?
+        _storage[0] : other._storage[0];
+    double _y = _storage[1] < other._storage[1] ?
+        _storage[1] : other._storage[1];
+    double _z = _storage[2] < other._storage[2] ?
+        _storage[2] : other._storage[2];
+    double _w = _storage[3] < other._storage[3] ?
+        _storage[3] : other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns the lane-wise maximum value in [this] or [other].
+  Float32x4 max(Float32x4 other) {
+    double _x = _storage[0] > other._storage[0] ?
+        _storage[0] : other._storage[0];
+    double _y = _storage[1] > other._storage[1] ?
+        _storage[1] : other._storage[1];
+    double _z = _storage[2] > other._storage[2] ?
+        _storage[2] : other._storage[2];
+    double _w = _storage[3] > other._storage[3] ?
+        _storage[3] : other._storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns the square root of [this].
+  Float32x4 sqrt() {
+    double _x = Math.sqrt(_storage[0]);
+    double _y = Math.sqrt(_storage[1]);
+    double _z = Math.sqrt(_storage[2]);
+    double _w = Math.sqrt(_storage[3]);
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns the reciprocal of [this].
+  Float32x4 reciprocal() {
+    double _x = 1.0 / _storage[0];
+    double _y = 1.0 / _storage[1];
+    double _z = 1.0 / _storage[2];
+    double _w = 1.0 / _storage[3];
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns the square root of the reciprocal of [this].
+  Float32x4 reciprocalSqrt() {
+    double _x = Math.sqrt(1.0 / _storage[0]);
+    double _y = Math.sqrt(1.0 / _storage[1]);
+    double _z = Math.sqrt(1.0 / _storage[2]);
+    double _w = Math.sqrt(1.0 / _storage[3]);
+    return new Float32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a bit-wise copy of [this] as a [Uint32x4].
+  Uint32x4 toUint32x4() {
+    var view = new Uint32List.view(_storage.buffer);
+    return new Uint32x4(view[0], view[1], view[2], view[3]);
+  }
+}
+
+
+class Uint32x4 {
+  final _storage = new Uint32List(4);
+
+  Uint32x4(int x, int y, int z, int w) {
+    _storage[0] = x;
+    _storage[1] = y;
+    _storage[2] = z;
+    _storage[3] = w;
+  }
+
+  Uint32x4.bool(bool x, bool y, bool z, bool w) {
+    _storage[0] = x == true ? 0xFFFFFFFF : 0x0;
+    _storage[1] = y == true ? 0xFFFFFFFF : 0x0;
+    _storage[2] = z == true ? 0xFFFFFFFF : 0x0;
+    _storage[3] = w == true ? 0xFFFFFFFF : 0x0;
+  }
+
+  /// The bit-wise or operator.
+  Uint32x4 operator|(Uint32x4 other) {
+    int _x = _storage[0] | other._storage[0];
+    int _y = _storage[1] | other._storage[1];
+    int _z = _storage[2] | other._storage[2];
+    int _w = _storage[3] | other._storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// The bit-wise and operator.
+  Uint32x4 operator&(Uint32x4 other) {
+    int _x = _storage[0] & other._storage[0];
+    int _y = _storage[1] & other._storage[1];
+    int _z = _storage[2] & other._storage[2];
+    int _w = _storage[3] & other._storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// The bit-wise xor operator.
+  Uint32x4 operator^(Uint32x4 other) {
+    int _x = _storage[0] ^ other._storage[0];
+    int _y = _storage[1] ^ other._storage[1];
+    int _z = _storage[2] ^ other._storage[2];
+    int _w = _storage[3] ^ other._storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Extract 32-bit mask from x lane.
+  int get x => _storage[0];
+  /// Extract 32-bit mask from y lane.
+  int get y => _storage[1];
+  /// Extract 32-bit mask from z lane.
+  int get z => _storage[2];
+  /// Extract 32-bit mask from w lane.
+  int get w => _storage[3];
+
+  /// Returns a new [Uint32x4] copied from [this] with a new x value.
+  Uint32x4 withX(int x) {
+    int _x = x;
+    int _y = _storage[1];
+    int _z = _storage[2];
+    int _w = _storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Uint32x4] copied from [this] with a new y value.
+  Uint32x4 withY(int y) {
+    int _x = _storage[0];
+    int _y = y;
+    int _z = _storage[2];
+    int _w = _storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Uint32x4] copied from [this] with a new z value.
+  Uint32x4 withZ(int z) {
+    int _x = _storage[0];
+    int _y = _storage[1];
+    int _z = z;
+    int _w = _storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Uint32x4] copied from [this] with a new w value.
+  Uint32x4 withW(int w) {
+    int _x = _storage[0];
+    int _y = _storage[1];
+    int _z = _storage[2];
+    int _w = w;
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Extracted x value. Returns false for 0, true for any other value.
+  bool get flagX => _storage[0] != 0x0;
+  /// Extracted y value. Returns false for 0, true for any other value.
+  bool get flagY => _storage[1] != 0x0;
+  /// Extracted z value. Returns false for 0, true for any other value.
+  bool get flagZ => _storage[2] != 0x0;
+  /// Extracted w value. Returns false for 0, true for any other value.
+  bool get flagW => _storage[3] != 0x0;
+
+  /// Returns a new [Uint32x4] copied from [this] with a new x value.
+  Uint32x4 withFlagX(bool x) {
+    int _x = x == true ? 0xFFFFFFFF : 0x0;
+    int _y = _storage[1];
+    int _z = _storage[2];
+    int _w = _storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Uint32x4] copied from [this] with a new y value.
+  Uint32x4 withFlagY(bool y) {
+    int _x = _storage[0];
+    int _y = y == true ? 0xFFFFFFFF : 0x0;
+    int _z = _storage[2];
+    int _w = _storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Uint32x4] copied from [this] with a new z value.
+  Uint32x4 withFlagZ(bool z) {
+    int _x = _storage[0];
+    int _y = _storage[1];
+    int _z = z == true ? 0xFFFFFFFF : 0x0;
+    int _w = _storage[3];
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Returns a new [Uint32x4] copied from [this] with a new w value.
+  Uint32x4 withFlagW(bool w) {
+    int _x = _storage[0];
+    int _y = _storage[1];
+    int _z = _storage[2];
+    int _w = w == true ? 0xFFFFFFFF : 0x0;
+    return new Uint32x4(_x, _y, _z, _w);
+  }
+
+  /// Merge [trueValue] and [falseValue] based on [this]' bit mask:
+  /// Select bit from [trueValue] when bit in [this] is on.
+  /// Select bit from [falseValue] when bit in [this] is off.
+  Float32x4 select(Float32x4 trueValue, Float32x4 falseValue) {
+    var trueView = new Uint32List.view(trueValue._storage.buffer);
+    var falseView = new Uint32List.view(falseValue._storage.buffer);
+    int cmx = _storage[0];
+    int cmy = _storage[1];
+    int cmz = _storage[2];
+    int cmw = _storage[3];
+    int stx = trueView[0];
+    int sty = trueView[1];
+    int stz = trueView[2];
+    int stw = trueView[3];
+    int sfx = falseView[0];
+    int sfy = falseView[1];
+    int sfz = falseView[2];
+    int sfw = falseView[3];
+    int _x = (cmx & stx) | (~cmx & sfx);
+    int _y = (cmy & sty) | (~cmy & sfy);
+    int _z = (cmz & stz) | (~cmz & sfz);
+    int _w = (cmw & stw) | (~cmw & sfw);
+    var r = new Float32x4(0.0, 0.0, 0.0, 0.0);
+    var rView = new Uint32List.view(r._storage.buffer);
+    rView[0] = _x;
+    rView[1] = _y;
+    rView[2] = _z;
+    rView[3] = _w;
+    return r;
+  }
+
+  /// Returns a bit-wise copy of [this] as a [Float32x4].
+  Float32x4 toFloat32x4() {
+    var view = new Float32List.view(_storage.buffer);
+    return new Float32x4(view[0], view[1], view[2], view[3]);
   }
 }
diff --git a/tests/compiler/dart2js_native/fixup_get_tag_test.dart b/tests/compiler/dart2js_native/fixup_get_tag_test.dart
index fbf9e3a..957fd25 100644
--- a/tests/compiler/dart2js_native/fixup_get_tag_test.dart
+++ b/tests/compiler/dart2js_native/fixup_get_tag_test.dart
@@ -6,7 +6,7 @@
 
 // Test for dartExperimentalFixupGetTag.
 
-class Foo native "A" {  // There is one native class with dispatch tag 'A'.
+class Foo native "A" { // There is one native class with dispatch tag 'A'.
   token() native;
 }
 
@@ -47,4 +47,7 @@
   // This call succeeds because the fixed-up 'getTag' method returns Foo's
   // dispatch tag, and B is a faithful polyfil for Foo/A.
   Expect.equals('isB', b.token());
+
+  Expect.isTrue(a is Foo);
+  Expect.isTrue(b is Foo);
 }
diff --git a/tests/html/element_test.dart b/tests/html/element_test.dart
index 3537335..c360c8f 100644
--- a/tests/html/element_test.dart
+++ b/tests/html/element_test.dart
@@ -59,7 +59,7 @@
       final container = new Element.tag("div");
       container.style.position = 'absolute';
       container.style.top = '8px';
-      container.style.left = '8px';
+      container.style.left = '9px';
       final element = new Element.tag("div");
       element.style.width = '200px';
       element.style.height = '200px';
@@ -72,8 +72,11 @@
       expect(element.offset.height, greaterThan(100));
       expect(element.scrollWidth, greaterThan(100));
       expect(element.scrollHeight, greaterThan(100));
-      expect(element.getBoundingClientRect().left, 8);
+      expect(element.getBoundingClientRect().left, 9);
       expect(element.getBoundingClientRect().top, 8);
+
+      expect(element.documentOffset.x, 9);
+      expect(element.documentOffset.y, 8);
       container.remove();
     });
   });
@@ -725,4 +728,132 @@
       expect(range[1], isInputElement);
     });
   });
+  void initPage() {
+    var level1 = new UListElement()
+      ..classes.add('level-1')
+      ..children.add(new LIElement()..innerHtml = 'I');
+    var itemii = new LIElement()
+      ..classes.add('item-ii')
+      ..style.position = 'relative'
+      ..style.top = '4px'
+      ..innerHtml = 'II';
+    level1.children.add(itemii);
+    var level2 = new UListElement();
+    itemii.children.add(level2);
+    var itema = new LIElement()
+      ..classes.add('item-a')
+      ..innerHtml = 'A';
+    var item1 = new LIElement()
+      ..classes.add('item-1')
+      ..innerHtml = '1';
+    var item2 = new LIElement()
+      ..classes.add('item-2')
+      ..innerHtml = '2';
+    var level3 = new UListElement()
+      ..children.addAll([item1, item2]);
+    var itemb = new LIElement()
+      ..classes.add('item-b')
+      ..style.position = 'relative'
+      ..style.top = '20px'
+      ..style.left = '150px'
+      ..innerHtml = 'B'
+      ..children.add(level3);
+    level2.children.addAll([itema, itemb, new LIElement()..innerHtml = 'C']);
+    document.body.append(level1);
+
+    var bar = new DivElement()..classes.add('bar');
+    var style = bar.style;
+    style..position = 'absolute'
+         ..top = '8px'
+         ..left = '90px';
+    var baz = new DivElement()..classes.add('baz');
+    style = baz.style;
+    style..position = 'absolute'
+         ..top = '600px'
+         ..left = '7000px';
+    bar.children.add(baz);
+
+    var quux = new DivElement()..classes.add('quux');
+    var qux = new DivElement()
+      ..classes.add('qux')
+      ..children.add(quux);
+
+    document.body.append(bar);
+    document.body.append(qux);
+  }
+
+  group('offset', () {
+    setUp(initPage);
+
+    test('offsetTo', () {
+      var itema = query('.item-a');
+      var itemb = query('.item-b');
+      var item1 = query('.item-1');
+      var itemii = query('.item-ii');
+      var level1 = query('.level-1');
+      var baz = query('.baz');
+      var bar = query('.bar');
+      var qux = query('.qux');
+      var quux = query('.quux');
+
+      var point = itema.offsetTo(itemii);
+      // TODO(efortuna): Make cross-machine reproducible.
+      /*expect(point.x, 40);
+      expect(point.y, inInclusiveRange(17, 20));
+
+      expect(baz.offsetTo(bar).x, 7000);
+      expect(baz.offsetTo(bar).y, inInclusiveRange(599, 604));
+
+      qux.style.position = 'fixed';
+      expect(quux.offsetTo(qux).x, 0);
+      expect(quux.offsetTo(qux).y, 0);
+
+      point = item1.offsetTo(itemb);
+      expect(point.x, 40);
+      expect(point.y, inInclusiveRange(17, 20));
+      point = itemb.offsetTo(itemii);
+      expect(point.x, 190);
+      expect(point.y, inInclusiveRange(54, 60));
+      point = item1.offsetTo(itemii);
+      expect(point.x, 230);
+      expect(point.y, inInclusiveRange(74, 80));*/
+    });
+
+    test('documentOffset', () {
+      var bar = query('.bar');
+      var baz = query('.baz');
+      var qux = query('.qux');
+      var quux = query('.quux');
+      var itema = query('.item-a');
+      var itemb = query('.item-b');
+      var item1 = query('.item-1');
+      var itemii = query('.item-ii');
+
+      // TODO(efortuna): Make cross-machine reproducible.
+      /*expect(itema.documentOffset.x, 88);
+      expect(itema.documentOffset.y, inInclusiveRange(119, 160));
+
+      expect(itemii.documentOffset.x, 48);
+      expect(itemii.documentOffset.y, inInclusiveRange(101, 145));
+
+      expect(itemb.documentOffset.x, 238);
+      expect(itemb.documentOffset.y, inInclusiveRange(157, 205));
+
+      expect(item1.documentOffset.x, 278);
+      expect(item1.documentOffset.y, inInclusiveRange(175, 222));
+
+      expect(bar.documentOffset.x, 90);
+      expect(bar.documentOffset.y, 8);
+
+      expect(baz.documentOffset.x, 7090);
+      expect(baz.documentOffset.y, 608);
+
+      expect(qux.documentOffset.x, 8);
+      expect(qux.documentOffset.y, inInclusiveRange(221, 232));
+
+      expect(quux.documentOffset.x, 8);
+      expect(quux.documentOffset.y, inInclusiveRange(221, 232));*/
+    });
+  });
+
 }
diff --git a/tests/language/error_stacktrace_test.dart b/tests/language/error_stacktrace_test.dart
new file mode 100644
index 0000000..806f00a
--- /dev/null
+++ b/tests/language/error_stacktrace_test.dart
@@ -0,0 +1,112 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// Dart test program for testing throw statement
+
+import "package:expect/expect.dart";
+
+class MyException {
+  const MyException(String message) : message_ = message;
+  final String message_;
+}
+
+class Helper1 {
+  static int func1() {
+    return func2();
+  }
+  static int func2() {
+    return func3();
+  }
+  static int func3() {
+    return func4();
+  }
+  static int func4() {
+    var i = 0;
+    try {
+      i = 10;
+      func5();
+    } on ArgumentError catch (e) {
+      i = 100;
+      Expect.isNotNull(e.stackTrace, "Errors need a stackTrace on throw");
+    }
+    return i;
+  }
+  static void func5() {
+    // Throw an Error.
+    throw new ArgumentError("ArgumentError in func5");
+  }
+}
+
+class Helper2 {
+  static int func1() {
+    return func2();
+  }
+  static int func2() {
+    return func3();
+  }
+  static int func3() {
+    return func4();
+  }
+  static int func4() {
+    var i = 0;
+    try {
+      i = 10;
+      func5();
+    } on ArgumentError catch (e, s) {
+      i = 200;
+      Expect.isNotNull(e.stackTrace, "Errors need a stackTrace on throw");
+      Expect.isFalse(identical(e.stackTrace, s));
+      Expect.equals(e.stackTrace.toString(), s.toString());
+    }
+    return i;
+  }
+  static List func5() {
+    // Throw an Error.
+    throw new ArgumentError("ArgumentError in func5");
+  }
+}
+
+class Helper3 {
+  static int func1() {
+    return func2();
+  }
+  static int func2() {
+    return func3();
+  }
+  static int func3() {
+    return func4();
+  }
+  static int func4() {
+    var i = 0;
+    try {
+      i = 10;
+      func5();
+    } on MyException catch (e) {
+      i = 300;
+      try {
+        // There should be no stackTrace in this normal excpetion object.
+        // We should get a NoSuchMethodError.
+        var trace = e.stackTrace;
+      } on NoSuchMethodError catch (e) {
+        Expect.isNotNull(e.stackTrace, "Error needs a stackTrace on throw");
+      }
+    }
+    return i;
+  }
+  static List func5() {
+    // Throw an Exception (any random object).
+    throw new MyException("MyException in func5");
+  }
+}
+
+class ErrorStackTraceTest {
+  static testMain() {
+    Expect.equals(100, Helper1.func1());
+    Expect.equals(200, Helper2.func1());
+    Expect.equals(300, Helper3.func1());
+  }
+}
+
+main() {
+  ErrorStackTraceTest.testMain();
+}
diff --git a/tests/language/language_dart2js.status b/tests/language/language_dart2js.status
index f89b97e..99249b5 100644
--- a/tests/language/language_dart2js.status
+++ b/tests/language/language_dart2js.status
@@ -118,6 +118,7 @@
 isnot_malformed_type_test/01: Fail # http://dartbug.com/5519
 not_enough_positional_arguments_test/01: Fail # http://dartbug.com/5519
 no_such_method_dispatcher_test: Fail # http://dartbug.com/11937
+error_stacktrace_test: Fail # (Issue 11681).
 
 constructor_negative_test: Pass # Wrong reason: the expression 'C()' is valid with class literals.
 
diff --git a/tests/language/many_named_arguments_test.dart b/tests/language/many_named_arguments_test.dart
new file mode 100644
index 0000000..d397573
--- /dev/null
+++ b/tests/language/many_named_arguments_test.dart
@@ -0,0 +1,128 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'package:expect/expect.dart';
+
+class Fisk {
+  method({a: 'a',
+          b: 'b',
+          c: 'c',
+          d: 'd',
+          e: 'e',
+          f: 'f',
+          g: 'g',
+          h: 'h',
+          i: 'i',
+          j: 'j',
+          k: 'k',
+          l: 'l',
+          m: 'm',
+          n: 'n',
+          o: 'o',
+          p: 'p',
+          q: 'q',
+          r: 'r',
+          s: 's',
+          t: 't',
+          u: 'u',
+          v: 'v',
+          w: 'w',
+          x: 'x',
+          y: 'y',
+          z: 'z'}) {
+
+    return
+        'a: $a, '
+        'b: $b, '
+        'c: $c, '
+        'd: $d, '
+        'e: $e, '
+        'f: $f, '
+        'g: $g, '
+        'h: $h, '
+        'i: $i, '
+        'j: $j, '
+        'k: $k, '
+        'l: $l, '
+        'm: $m, '
+        'n: $n, '
+        'o: $o, '
+        'p: $p, '
+        'q: $q, '
+        'r: $r, '
+        's: $s, '
+        't: $t, '
+        'u: $u, '
+        'v: $v, '
+        'w: $w, '
+        'x: $x, '
+        'y: $y, '
+        'z: $z';
+  }
+}
+
+main() {
+  var method = new Fisk().method;
+  var namedArguments = new Map();
+  namedArguments[const Symbol('a')] = 'a';
+  Expect.stringEquals(
+      EXPECTED_RESULT, Function.apply(method, [], namedArguments));
+  Expect.stringEquals(
+      EXPECTED_RESULT,
+      new Fisk().method(
+          a: 'a',
+          b: 'b',
+          c: 'c',
+          d: 'd',
+          e: 'e',
+          f: 'f',
+          g: 'g',
+          h: 'h',
+          i: 'i',
+          j: 'j',
+          k: 'k',
+          l: 'l',
+          m: 'm',
+          n: 'n',
+          o: 'o',
+          p: 'p',
+          q: 'q',
+          r: 'r',
+          s: 's',
+          t: 't',
+          u: 'u',
+          v: 'v',
+          w: 'w',
+          x: 'x',
+          y: 'y',
+          z: 'z'));
+}
+
+const String EXPECTED_RESULT =
+    'a: a, '
+    'b: b, '
+    'c: c, '
+    'd: d, '
+    'e: e, '
+    'f: f, '
+    'g: g, '
+    'h: h, '
+    'i: i, '
+    'j: j, '
+    'k: k, '
+    'l: l, '
+    'm: m, '
+    'n: n, '
+    'o: o, '
+    'p: p, '
+    'q: q, '
+    'r: r, '
+    's: s, '
+    't: t, '
+    'u: u, '
+    'v: v, '
+    'w: w, '
+    'x: x, '
+    'y: y, '
+    'z: z';
diff --git a/tests/lib/convert/chunked_conversion1_test.dart b/tests/lib/convert/chunked_conversion1_test.dart
new file mode 100644
index 0000000..70d82f2
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion1_test.dart
@@ -0,0 +1,254 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:expect/expect.dart';
+
+// This test implements a new special interface that can be used to
+// send data more efficiently between two converters.
+
+abstract class MyChunkedIntSink extends ChunkedConversionSink<int> {
+  MyChunkedIntSink();
+  factory MyChunkedIntSink.from(sink) = IntAdapterSink;
+  factory MyChunkedIntSink.withCallback(callback) {
+    var sink = new ChunkedConversionSink.withCallback(callback);
+    return new MyChunkedIntSink.from(sink);
+  }
+
+  add(int i);
+  close();
+
+  // The special method.
+  specialI(i);
+}
+
+class IntAdapterSink extends MyChunkedIntSink {
+  final _sink;
+  IntAdapterSink(this._sink);
+  add(o) => _sink.add(o);
+  close() => _sink.close();
+  specialI(o) => add(o);
+}
+
+class MyChunkedBoolSink extends ChunkedConversionSink<bool> {
+  MyChunkedBoolSink();
+  factory MyChunkedBoolSink.from(sink) = BoolAdapterSink;
+  factory MyChunkedBoolSink.withCallback(callback) {
+    var sink = new ChunkedConversionSink.withCallback(callback);
+    return new MyChunkedBoolSink.from(sink);
+  }
+
+  add(bool b);
+  close();
+
+  specialB(bool b);
+}
+
+class BoolAdapterSink extends MyChunkedBoolSink {
+  final _sink;
+  BoolAdapterSink(this._sink);
+  add(o) => _sink.add(o);
+  close() => _sink.close();
+  specialB(o) => add(o);
+}
+
+class IntBoolConverter1 extends Converter<List<int>, List<bool>> {
+  List<bool> convert(List<int> input) => input.map((x) => x > 0).toList();
+
+  startChunkedConversion(sink) {
+    if (sink is! MyChunkedBoolSink) sink = new MyChunkedBoolSink.from(sink);
+    return new IntBoolConverter1Sink(sink);
+  }
+}
+
+class BoolIntConverter1 extends Converter<List<bool>, List<int>> {
+  List<int> convert(List<bool> input) => input.map((x) => x ? 1 : 0).toList();
+
+  startChunkedConversion(sink) {
+    if (sink is! MyChunkedIntSink) sink = new MyChunkedIntSink.from(sink);
+    return new BoolIntConverter1Sink(sink);
+  }
+}
+
+int specialICounter = 0;
+int specialBCounter = 0;
+
+class IntBoolConverter1Sink extends MyChunkedIntSink {
+  var outSink;
+  IntBoolConverter1Sink(this.outSink);
+
+  add(int i) {
+    outSink.specialB(i > 0);
+  }
+  specialI(int i) {
+    specialICounter++;
+    add(i);
+  }
+  close() => outSink.close();
+}
+
+class BoolIntConverter1Sink extends MyChunkedBoolSink {
+  var outSink;
+  BoolIntConverter1Sink(this.outSink);
+
+  add(bool b) {
+    outSink.specialI(b ? 1 : 0);
+  }
+
+  specialB(bool b) {
+    specialBCounter++;
+    add(b);
+  }
+  close() => outSink.close();
+}
+
+class IdentityConverter extends Converter {
+  convert(x) => x;
+
+  startChunkedConversion(sink) {
+    return new IdentitySink(sink);
+  }
+}
+
+class IdentitySink extends ChunkedConversionSink {
+  final _sink;
+  IdentitySink(this._sink);
+  add(o) => _sink.add(o);
+  close() => _sink.close();
+}
+
+main() {
+  var converter1, converter2, intSink, intSink2, hasExecuted, boolSink, fused;
+  var converter3, fused2, sink, sink2;
+
+  // Test int->bool converter individually.
+  converter1 = new IntBoolConverter1();
+  Expect.listEquals([true, false, true], converter1.convert([2, -2, 2]));
+  hasExecuted = false;
+  boolSink = new MyChunkedBoolSink.withCallback((value) {
+    hasExecuted = true;
+    Expect.listEquals([true, false, true], value);
+  });
+  intSink = converter1.startChunkedConversion(boolSink);
+  intSink.add(3);
+  intSink.specialI(-3);
+  intSink.add(3);
+  intSink.close();
+  Expect.isTrue(hasExecuted);
+  Expect.equals(1, specialICounter);
+  specialICounter = 0;
+  hasExecuted = false;
+
+  // Test bool->int converter individually.
+  converter2 = new BoolIntConverter1();
+  Expect.listEquals([1, 0, 1], converter2.convert([true, false, true]));
+  hasExecuted = false;
+  intSink = new MyChunkedIntSink.withCallback((value) {
+    hasExecuted = true;
+    Expect.listEquals([1, 0, 1], value);
+  });
+  boolSink = converter2.startChunkedConversion(intSink);
+  boolSink.specialB(true);
+  boolSink.add(false);
+  boolSink.add(true);
+  boolSink.close();
+  Expect.isTrue(hasExecuted);
+  Expect.equals(1, specialBCounter);
+  specialBCounter = 0;
+  hasExecuted = false;
+
+  // Test identity converter indidivually.
+  converter3 = new IdentityConverter();
+  hasExecuted = false;
+  sink = new ChunkedConversionSink.withCallback((value) {
+    hasExecuted = true;
+    Expect.listEquals([1, 2, 3], value);
+  });
+  sink2 = converter3.startChunkedConversion(sink);
+  [1, 2, 3].forEach(sink2.add);
+  sink2.close();
+  Expect.isTrue(hasExecuted);
+  hasExecuted = false;
+
+  // Test fused converters.
+  fused = converter1.fuse(converter2);
+  Expect.listEquals([1, 0, 1], fused.convert([2, -2, 2]));
+  hasExecuted = false;
+  intSink2 = new MyChunkedIntSink.withCallback((value) {
+    hasExecuted = true;
+    Expect.listEquals([1, 0, 1], value);
+  });
+  intSink = fused.startChunkedConversion(intSink2);
+  intSink.specialI(3);
+  intSink.add(-3);
+  intSink.add(3);
+  intSink.close();
+  Expect.isTrue(hasExecuted);
+  Expect.equals(3, specialBCounter);
+  specialBCounter = 0;
+  Expect.equals(1, specialICounter);
+  specialICounter = 0;
+  hasExecuted = false;
+
+  // With identity in front.
+  fused2 = converter3.fuse(fused);
+  hasExecuted = false;
+  intSink2 = new MyChunkedIntSink.withCallback((value) {
+    hasExecuted = true;
+    Expect.listEquals([1, 0, 1], value);
+  });
+  sink = fused2.startChunkedConversion(intSink2);
+  Expect.isFalse(sink is MyChunkedIntSink);
+  sink.add(3);
+  sink.add(-3);
+  sink.add(3);
+  sink.close();
+  Expect.isTrue(hasExecuted);
+  Expect.equals(3, specialBCounter);
+  specialBCounter = 0;
+  Expect.equals(0, specialICounter);
+  specialICounter = 0;
+  hasExecuted = false;
+
+  // With identity at the end.
+  fused2 = fused.fuse(converter3);
+  hasExecuted = false;
+  sink = new ChunkedConversionSink.withCallback((value) {
+    hasExecuted = true;
+    Expect.listEquals([1, 0, 1], value);
+  });
+  intSink = fused2.startChunkedConversion(sink);
+  Expect.isTrue(intSink is MyChunkedIntSink);
+  intSink.specialI(3);
+  intSink.add(-3);
+  intSink.specialI(3);
+  intSink.close();
+  Expect.isTrue(hasExecuted);
+  Expect.equals(3, specialBCounter);
+  specialBCounter = 0;
+  Expect.equals(2, specialICounter);
+  specialICounter = 0;
+  hasExecuted = false;
+
+  // With identity between the two converters.
+  fused = converter1.fuse(converter3).fuse(converter2);
+  Expect.listEquals([1, 0, 1], fused.convert([2, -2, 2]));
+  hasExecuted = false;
+  intSink2 = new MyChunkedIntSink.withCallback((value) {
+    hasExecuted = true;
+    Expect.listEquals([1, 0, 1], value);
+  });
+  intSink = fused.startChunkedConversion(intSink2);
+  intSink.specialI(3);
+  intSink.add(-3);
+  intSink.add(3);
+  intSink.close();
+  Expect.isTrue(hasExecuted);
+  Expect.equals(0, specialBCounter);
+  specialBCounter = 0;
+  Expect.equals(1, specialICounter);
+  specialICounter = 0;
+  hasExecuted = false;
+}
diff --git a/tests/lib/convert/chunked_conversion2_test.dart b/tests/lib/convert/chunked_conversion2_test.dart
new file mode 100644
index 0000000..d1e57ab
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion2_test.dart
@@ -0,0 +1,52 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import 'dart:convert';
+
+import 'package:expect/expect.dart';
+
+// Test that the String and ByteConversionSinks make a copy when they need to
+// adapt.
+
+class MyByteSink extends ByteConversionSinkBase {
+  var accumulator = [];
+  add(List<int> bytes) {
+    accumulator.add(bytes);
+  }
+  close() {}
+}
+
+void testBase() {
+  var byteSink = new MyByteSink();
+  var bytes = [1];
+  byteSink.addSlice(bytes, 0, 1, false);
+  bytes[0] = 2;
+  byteSink.addSlice(bytes, 0, 1, true);
+  Expect.equals(1, byteSink.accumulator[0][0]);
+  Expect.equals(2, byteSink.accumulator[1][0]);
+}
+
+class MyChunkedSink extends ChunkedConversionSink {
+  var accumulator = [];
+  add(List<int> bytes) {
+    accumulator.add(bytes);
+  }
+  close() {}
+}
+
+void testAdapter() {
+  var chunkedSink = new MyChunkedSink();
+  var byteSink = new ByteConversionSink.from(chunkedSink);
+  var bytes = [1];
+  byteSink.addSlice(bytes, 0, 1, false);
+  bytes[0] = 2;
+  byteSink.addSlice(bytes, 0, 1, true);
+  Expect.equals(1, chunkedSink.accumulator[0][0]);
+  Expect.equals(2, chunkedSink.accumulator[1][0]);
+}
+
+void main() {
+  testBase();
+  testAdapter();
+}
diff --git a/tests/lib/convert/chunked_conversion_json_decode1_test.dart b/tests/lib/convert/chunked_conversion_json_decode1_test.dart
new file mode 100644
index 0000000..932eae4
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_json_decode1_test.dart
@@ -0,0 +1,161 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+
+final TESTS = [
+    [ 5, '5' ],
+    [ -42, '-42' ],
+    [ 3.14, '3.14' ],
+    [ true, 'true' ],
+    [ false, 'false' ],
+    [ null, 'null' ],
+    [ 'quote"or\'', '"quote\\"or\'"' ],
+    [ '', '""' ],
+    [ [], "[]" ],
+    [ [3, -4.5, true, "hi", false], '[3,-4.5,true,"hi",false]' ],
+    [ [null], "[null]" ],
+    [ [[null]], "[[null]]" ],
+    [ [[3]], "[[3]]" ],
+    [ {}, "{}" ],
+    [ { "x": 3, "y": 4.5, "z": "hi", "u": true, "v": false },
+      '{"x":3,"y":4.5,"z":"hi","u":true,"v":false}' ],
+    [ { "x": null }, '{"x":null}' ],
+    [ { "x": {} }, '{"x":{}}' ],
+    // Note that -0.0 won't be treated the same in JS. The Json spec seems to
+    // allow it, though.
+    [ { "hi there": 499, "'":-0.0 }, '{"hi there":499,"\'":-0.0}' ],
+    [ r'\foo', r'"\\foo"' ],
+  ];
+
+bool isJsonEqual(o1, o2) {
+  if (o1 == o2) return true;
+  if (o1 is List && o2 is List) {
+    if (o1.length != o2.length) return false;
+    for (int i = 0; i < o1.length; i++) {
+      if (!isJsonEqual(o1[i], o2[i])) return false;
+    }
+    return true;
+  }
+  if (o1 is Map && o2 is Map) {
+    if (o1.length != o2.length) return false;
+    for (var key in o1.keys) {
+      Expect.isTrue(key is String);
+      if (!o2.containsKey(key)) return false;
+      if (!isJsonEqual(o1[key], o2[key])) return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+Object decode(String str) {
+  Object result;
+  var decoder = new JsonDecoder(null);
+  ChunkedConversionSink objectSink =
+      new ChunkedConversionSink.withCallback((x) => result = x.single);
+  var stringConversionSink = decoder.startChunkedConversion(objectSink);
+  stringConversionSink.add(str);
+  stringConversionSink.close();
+  return result;
+}
+
+Object decode2(String str) {
+  Object result;
+  var decoder = new JsonDecoder(null);
+  ChunkedConversionSink objectSink =
+      new ChunkedConversionSink.withCallback((x) => result = x.single);
+  var stringConversionSink = decoder.startChunkedConversion(objectSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  stringSink.write(str);
+  stringSink.close();
+  return result;
+}
+
+Object decode3(String str) {
+  Object result;
+  var decoder = new JsonDecoder(null);
+  ChunkedConversionSink objectSink =
+      new ChunkedConversionSink.withCallback((x) => result = x.single);
+  var stringConversionSink = decoder.startChunkedConversion(objectSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  str.codeUnits.forEach(stringSink.writeCharCode);
+  stringSink.close();
+  return result;
+}
+
+Object decode4(String str) {
+  Object result;
+  var decoder = new JsonDecoder(null);
+  ChunkedConversionSink objectSink =
+      new ChunkedConversionSink.withCallback((x) => result = x.single);
+  var stringConversionSink = decoder.startChunkedConversion(objectSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  str.runes.forEach(stringSink.writeCharCode);
+  stringSink.close();
+  return result;
+}
+
+Object decode5(String str) {
+  Object result;
+  var decoder = new JsonDecoder(null);
+  ChunkedConversionSink objectSink =
+      new ChunkedConversionSink.withCallback((x) => result = x.single);
+  var stringConversionSink = decoder.startChunkedConversion(objectSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  Object tmpBytes = UTF8.encode(str);
+  inputByteSink.add(tmpBytes);
+  inputByteSink.close();
+  return result;
+}
+
+Object decode6(String str) {
+  Object result;
+  var decoder = new JsonDecoder(null);
+  ChunkedConversionSink objectSink =
+      new ChunkedConversionSink.withCallback((x) => result = x.single);
+  var stringConversionSink = decoder.startChunkedConversion(objectSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  Object tmpBytes = UTF8.encode(str);
+  tmpBytes.forEach((b) => inputByteSink.addSlice([0, b, 1], 1, 2, false));
+  inputByteSink.close();
+  return result;
+}
+
+Object decode7(String str) {
+  Object result;
+  var decoder = new JsonDecoder(null);
+  ChunkedConversionSink objectSink =
+      new ChunkedConversionSink.withCallback((x) => result = x.single);
+  var stringConversionSink = decoder.startChunkedConversion(objectSink);
+  stringConversionSink.addSlice("1" + str + "2", 1, str.length + 1, false);
+  stringConversionSink.close();
+  return result;
+}
+
+
+main() {
+  var tests = TESTS.expand((test) {
+    var object = test[0];
+    var string = test[1];
+    var longString = "                                                        "
+                     "                                                        "
+                     "$string"
+                     "                                                        "
+                     "                                                        ";
+    return [test, [object, longString]];
+  });
+  for (var test in TESTS) {
+    var o = test[0];
+    var string = test[1];
+    Expect.isTrue(isJsonEqual(o, decode(string)));
+    Expect.isTrue(isJsonEqual(o, decode2(string)));
+    Expect.isTrue(isJsonEqual(o, decode3(string)));
+    Expect.isTrue(isJsonEqual(o, decode4(string)));
+    Expect.isTrue(isJsonEqual(o, decode5(string)));
+    Expect.isTrue(isJsonEqual(o, decode6(string)));
+    Expect.isTrue(isJsonEqual(o, decode7(string)));
+  }
+}
diff --git a/tests/lib/convert/chunked_conversion_json_encode1_test.dart b/tests/lib/convert/chunked_conversion_json_encode1_test.dart
new file mode 100644
index 0000000..c521aa5
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_json_encode1_test.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+
+final TESTS = [
+    [ 5, '5' ],
+    [ -42, '-42' ],
+    [ 3.14, '3.14' ],
+    [ true, 'true' ],
+    [ false, 'false' ],
+    [ null, 'null' ],
+    [ 'quote"or\'', '"quote\\"or\'"' ],
+    [ '', '""' ],
+    [ [], "[]" ],
+    [ [3, -4.5, true, "hi", false], '[3,-4.5,true,"hi",false]' ],
+    [ [null], "[null]" ],
+    [ [[null]], "[[null]]" ],
+    [ [[3]], "[[3]]" ],
+    [ {}, "{}" ],
+    [ { "x": 3, "y": 4.5, "z": "hi", "u": true, "v": false },
+      '{"x":3,"y":4.5,"z":"hi","u":true,"v":false}' ],
+    [ { "x": null }, '{"x":null}' ],
+    [ { "x": {} }, '{"x":{}}' ],
+    // Note that -0.0 won't be treated the same in JS. The Json spec seems to
+    // allow it, though.
+    [ { "hi there": 499, "'":-0.0 }, '{"hi there":499,"\'":-0.0}' ],
+    [ r'\foo', r'"\\foo"' ],
+  ];
+
+class MyStringConversionSink extends StringConversionSinkBase {
+  var buffer = new StringBuffer();
+  var callback;
+
+  MyStringConversionSink(this.callback);
+
+  addSlice(str, start, end, bool isLast) {
+    buffer.write(str.substring(start, end));
+    if (isLast) close();
+  }
+
+  close() {
+    callback(buffer.toString());
+  }
+}
+
+String encode(Object o) {
+  var result;
+  var encoder = new JsonEncoder();
+  ChunkedConversionSink stringSink =
+      new MyStringConversionSink((x) => result = x);
+  var objectSink = new JsonEncoder().startChunkedConversion(stringSink);
+  objectSink.add(o);
+  objectSink.close();
+  return result;
+}
+
+String encode2(Object o) {
+  var result;
+  var encoder = new JsonEncoder();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.withCallback((x) => result = x);
+  var objectSink = encoder.startChunkedConversion(stringSink);
+  objectSink.add(o);
+  objectSink.close();
+  return result;
+}
+
+main() {
+  for (var test in TESTS) {
+    var o = test[0];
+    var expected = test[1];
+    Expect.equals(expected, encode(o));
+    Expect.equals(expected, encode2(o));
+  }
+}
diff --git a/tests/lib/convert/chunked_conversion_utf82_test.dart b/tests/lib/convert/chunked_conversion_utf82_test.dart
new file mode 100644
index 0000000..6e1c6e7
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf82_test.dart
@@ -0,0 +1,155 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library utf8_test;
+import "package:expect/expect.dart";
+import 'dart:convert';
+
+String decode(List<int> bytes, int chunkSize) {
+  StringBuffer buffer = new StringBuffer();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.fromStringSink(buffer);
+  var byteSink = new Utf8Decoder().startChunkedConversion(stringSink);
+  int i = 0;
+  while (i < bytes.length) {
+    List nextChunk = [];
+    for (int j = 0; j < chunkSize; j++) {
+      if (i < bytes.length) {
+        nextChunk.add(bytes[i]);
+        i++;
+      }
+    }
+    byteSink.add(nextChunk);
+  }
+  byteSink.close();
+  return buffer.toString();
+}
+
+String decodeAllowMalformed(List<int> bytes, int chunkSize) {
+  StringBuffer buffer = new StringBuffer();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.fromStringSink(buffer);
+  var decoder = new Utf8Decoder(allowMalformed: true);
+  var byteSink = decoder.startChunkedConversion(stringSink);
+  int i = 0;
+  while (i < bytes.length) {
+    List nextChunk = [];
+    for (int j = 0; j < chunkSize; j++) {
+      if (i < bytes.length) {
+        nextChunk.add(bytes[i]);
+        i++;
+      }
+    }
+    byteSink.add(nextChunk);
+  }
+  byteSink.close();
+  return buffer.toString();
+}
+
+final TESTS = [
+  // Unfinished UTF-8 sequences.
+  [ 0xc3 ],
+  [ 0xE2, 0x82 ],
+  [ 0xF0, 0xA4, 0xAD ],
+  // Overlong encoding of euro-sign.
+  [ 0xF0, 0x82, 0x82, 0xAC ],
+  // Other overlong/unfinished sequences.
+  [ 0xC0 ],
+  [ 0xC1 ],
+  [ 0xF5 ],
+  [ 0xF6 ],
+  [ 0xF7 ],
+  [ 0xF8 ],
+  [ 0xF9 ],
+  [ 0xFA ],
+  [ 0xFB ],
+  [ 0xFC ],
+  [ 0xFD ],
+  [ 0xFE ],
+  [ 0xFF ],
+  [ 0xC0, 0x80 ],
+  [ 0xC1, 0x80 ],
+  // Outside valid range.
+  [ 0xF4, 0xBF, 0xBF, 0xBF ]];
+
+final TESTS2 = [
+  // Test that 0xC0|1, 0x80 does not eat the next character.
+  [[ 0xC0, 0x80, 0x61 ], "Xa" ],
+  [[ 0xC1, 0x80, 0x61 ], "Xa" ],
+  // 0xF5 .. 0xFF never appear in valid UTF-8 sequences.
+  [[ 0xF5, 0x80 ], "XX" ],
+  [[ 0xF6, 0x80 ], "XX" ],
+  [[ 0xF7, 0x80 ], "XX" ],
+  [[ 0xF8, 0x80 ], "XX" ],
+  [[ 0xF9, 0x80 ], "XX" ],
+  [[ 0xFA, 0x80 ], "XX" ],
+  [[ 0xFB, 0x80 ], "XX" ],
+  [[ 0xFC, 0x80 ], "XX" ],
+  [[ 0xFD, 0x80 ], "XX" ],
+  [[ 0xFE, 0x80 ], "XX" ],
+  [[ 0xFF, 0x80 ], "XX" ],
+  [[ 0xF5, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF6, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF7, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF8, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF9, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFA, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFB, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFC, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFD, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFE, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFF, 0x80, 0x61 ], "XXa" ],
+  // Characters outside the valid range.
+  [[ 0xF5, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF6, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF7, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF8, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF9, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFA, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFB, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFC, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFD, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFE, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFF, 0x80, 0x80, 0x61 ], "XXXa" ]];
+
+main() {
+  var allTests = TESTS.expand((test) {
+    // Pairs of test and expected string output when malformed strings are
+    // allowed. Replacement character: U+FFFD
+    return [[ test, "\u{FFFD}" ],
+            [ new List.from([0x61])..addAll(test), "a\u{FFFD}" ],
+            [ new List.from([0x61])..addAll(test)..add(0x61), "a\u{FFFD}a" ],
+            [ new List.from(test)..add(0x61), "\u{FFFD}a" ],
+            [ new List.from(test)..addAll(test), "\u{FFFD}\u{FFFD}" ],
+            [ new List.from(test)..add(0x61)..addAll(test),
+              "\u{FFFD}a\u{FFFD}" ],
+            [ new List.from([0xc3, 0xa5])..addAll(test), "å\u{FFFD}" ],
+            [ new List.from([0xc3, 0xa5])..addAll(test)..addAll([0xc3, 0xa5]),
+              "å\u{FFFD}å" ],
+            [ new List.from(test)..addAll([0xc3, 0xa5]), "\u{FFFD}å" ],
+            [ new List.from(test)..addAll([0xc3, 0xa5])..addAll(test),
+              "\u{FFFD}å\u{FFFD}" ]];
+  });
+
+  var allTests2 = TESTS2.map((test) {
+    // Pairs of test and expected string output when malformed strings are
+    // allowed. Replacement character: U+FFFD
+    String expected = test[1].replaceAll("X", "\u{FFFD}");
+    return [test[0], expected];
+  });
+
+  for (var test in []..addAll(allTests)..addAll(allTests2)) {
+    List<int> bytes = test[0];
+    Expect.throws(() => decode(bytes, 1), (e) => e is FormatException);
+    Expect.throws(() => decode(bytes, 2), (e) => e is FormatException);
+    Expect.throws(() => decode(bytes, 3), (e) => e is FormatException);
+    Expect.throws(() => decode(bytes, 4), (e) => e is FormatException);
+
+    String expected = test[1];
+    Expect.equals(expected, decodeAllowMalformed(bytes, 1));
+    Expect.equals(expected, decodeAllowMalformed(bytes, 2));
+    Expect.equals(expected, decodeAllowMalformed(bytes, 3));
+    Expect.equals(expected, decodeAllowMalformed(bytes, 4));
+  }
+}
diff --git a/tests/lib/convert/chunked_conversion_utf83_test.dart b/tests/lib/convert/chunked_conversion_utf83_test.dart
new file mode 100644
index 0000000..bf7cea6
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf83_test.dart
@@ -0,0 +1,79 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+
+String decode(List<int> bytes, int chunkSize) {
+  StringBuffer buffer = new StringBuffer();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.fromStringSink(buffer);
+  var byteSink = new Utf8Decoder().startChunkedConversion(stringSink);
+  int i = 0;
+  while (i < bytes.length) {
+    List nextChunk = [];
+    for (int j = 0; j < chunkSize; j++) {
+      if (i < bytes.length) {
+        nextChunk.add(bytes[i]);
+        i++;
+      }
+    }
+    byteSink.add(nextChunk);
+  }
+  byteSink.close();
+  return buffer.toString();
+}
+
+String decodeAllowMalformed(List<int> bytes, int chunkSize) {
+  StringBuffer buffer = new StringBuffer();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.fromStringSink(buffer);
+  var decoder = new Utf8Decoder(allowMalformed: true);
+  var byteSink = decoder.startChunkedConversion(stringSink);
+  int i = 0;
+  while (i < bytes.length) {
+    List nextChunk = [];
+    for (int j = 0; j < chunkSize; j++) {
+      if (i < bytes.length) {
+        nextChunk.add(bytes[i]);
+        i++;
+      }
+    }
+    byteSink.add(nextChunk);
+  }
+  byteSink.close();
+  return buffer.toString();
+}
+
+main() {
+  // Test that chunked UTF8-decoder removes leading BOM.
+  Expect.equals("a", decode([0xEF, 0xBB, 0xBF, 0x61], 1));
+  Expect.equals("a", decode([0xEF, 0xBB, 0xBF, 0x61], 2));
+  Expect.equals("a", decode([0xEF, 0xBB, 0xBF, 0x61], 3));
+  Expect.equals("a", decode([0xEF, 0xBB, 0xBF, 0x61], 4));
+  Expect.equals("a", decodeAllowMalformed([0xEF, 0xBB, 0xBF, 0x61], 1));
+  Expect.equals("a", decodeAllowMalformed([0xEF, 0xBB, 0xBF, 0x61], 2));
+  Expect.equals("a", decodeAllowMalformed([0xEF, 0xBB, 0xBF, 0x61], 3));
+  Expect.equals("a", decodeAllowMalformed([0xEF, 0xBB, 0xBF, 0x61], 4));
+  Expect.equals("", decode([0xEF, 0xBB, 0xBF], 1));
+  Expect.equals("", decode([0xEF, 0xBB, 0xBF], 2));
+  Expect.equals("", decode([0xEF, 0xBB, 0xBF], 3));
+  Expect.equals("", decode([0xEF, 0xBB, 0xBF], 4));
+  Expect.equals("", decodeAllowMalformed([0xEF, 0xBB, 0xBF], 1));
+  Expect.equals("", decodeAllowMalformed([0xEF, 0xBB, 0xBF], 2));
+  Expect.equals("", decodeAllowMalformed([0xEF, 0xBB, 0xBF], 3));
+  Expect.equals("", decodeAllowMalformed([0xEF, 0xBB, 0xBF], 4));
+  Expect.equals("a\u{FEFF}", decode([0x61, 0xEF, 0xBB, 0xBF], 1));
+  Expect.equals("a\u{FEFF}", decode([0x61, 0xEF, 0xBB, 0xBF], 2));
+  Expect.equals("a\u{FEFF}", decode([0x61, 0xEF, 0xBB, 0xBF], 3));
+  Expect.equals("a\u{FEFF}", decode([0x61, 0xEF, 0xBB, 0xBF], 4));
+  Expect.equals("a\u{FEFF}", decodeAllowMalformed([0x61, 0xEF, 0xBB, 0xBF], 1));
+  Expect.equals("a\u{FEFF}", decodeAllowMalformed([0x61, 0xEF, 0xBB, 0xBF], 2));
+  Expect.equals("a\u{FEFF}", decodeAllowMalformed([0x61, 0xEF, 0xBB, 0xBF], 3));
+  Expect.equals("a\u{FEFF}", decodeAllowMalformed([0x61, 0xEF, 0xBB, 0xBF], 4));
+}
diff --git a/tests/lib/convert/chunked_conversion_utf84_test.dart b/tests/lib/convert/chunked_conversion_utf84_test.dart
new file mode 100644
index 0000000..d4710a5
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf84_test.dart
@@ -0,0 +1,40 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+import 'unicode_tests.dart';
+
+String decode(List<int> bytes, int chunkSize) {
+  StringBuffer buffer = new StringBuffer();
+  // Use a non-chunked interface.
+  String result;
+  ChunkedConversionSink chunkedSink =
+      new StringConversionSink.withCallback((decoded) => result = decoded);
+  var byteSink = new Utf8Decoder().startChunkedConversion(chunkedSink);
+  int i = 0;
+  while (i < bytes.length) {
+    List nextChunk = [];
+    for (int j = 0; j < chunkSize; j++) {
+      if (i < bytes.length) {
+        nextChunk.add(bytes[i]);
+        i++;
+      }
+    }
+    byteSink.add(nextChunk);
+  }
+  byteSink.close();
+  return result;
+}
+
+main() {
+  for (var test in UNICODE_TESTS) {
+    var bytes = test[0];
+    var expected = test[1];
+    Expect.stringEquals(expected, decode(bytes, 1));
+    Expect.stringEquals(expected, decode(bytes, 2));
+    Expect.stringEquals(expected, decode(bytes, 3));
+    Expect.stringEquals(expected, decode(bytes, 4));
+  }
+}
\ No newline at end of file
diff --git a/tests/lib/convert/chunked_conversion_utf85_test.dart b/tests/lib/convert/chunked_conversion_utf85_test.dart
new file mode 100644
index 0000000..c27feae
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf85_test.dart
@@ -0,0 +1,99 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+import 'unicode_tests.dart';
+
+List<int> encode(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  stringConversionSink.add(str);
+  stringConversionSink.close();
+  return bytes;
+}
+
+List<int> encode2(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  stringSink.write(str);
+  stringSink.close();
+  return bytes;
+}
+
+List<int> encode3(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  str.codeUnits.forEach(stringSink.writeCharCode);
+  stringSink.close();
+  return bytes;
+}
+
+List<int> encode4(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  str.runes.forEach(stringSink.writeCharCode);
+  stringSink.close();
+  return bytes;
+}
+
+List<int> encode5(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  List<int> tmpBytes = UTF8.encode(str);
+  inputByteSink.add(tmpBytes);
+  inputByteSink.close();
+  return bytes;
+}
+
+List<int> encode6(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  List<int> tmpBytes = UTF8.encode(str);
+  tmpBytes.forEach((b) => inputByteSink.addSlice([0, b, 1], 1, 2, false));
+  inputByteSink.close();
+  return bytes;
+}
+
+List<int> encode7(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  stringConversionSink.addSlice("1" + str + "2", 1, str.length + 1, false);
+  stringConversionSink.close();
+  return bytes;
+}
+
+
+main() {
+  for (var test in UNICODE_TESTS) {
+    List<int> bytes = test[0];
+    String string = test[1];
+    Expect.listEquals(bytes, encode(string));
+    Expect.listEquals(bytes, encode2(string));
+    Expect.listEquals(bytes, encode3(string));
+    Expect.listEquals(bytes, encode4(string));
+    Expect.listEquals(bytes, encode5(string));
+    Expect.listEquals(bytes, encode6(string));
+    Expect.listEquals(bytes, encode7(string));
+  }
+}
diff --git a/tests/lib/convert/chunked_conversion_utf86_test.dart b/tests/lib/convert/chunked_conversion_utf86_test.dart
new file mode 100644
index 0000000..916e7ab
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf86_test.dart
@@ -0,0 +1,129 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+
+String decode(List<int> bytes) {
+  StringBuffer buffer = new StringBuffer();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.fromStringSink(buffer);
+  var byteSink = new Utf8Decoder().startChunkedConversion(stringSink);
+  bytes.forEach((byte) { byteSink.add([byte]); });
+  byteSink.close();
+  return buffer.toString();
+}
+
+String decodeAllowMalformed(List<int> bytes) {
+  StringBuffer buffer = new StringBuffer();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.fromStringSink(buffer);
+  var decoder = new Utf8Decoder(allowMalformed: true);
+  var byteSink = decoder.startChunkedConversion(stringSink);
+  bytes.forEach((byte) { byteSink.add([byte]); });
+  byteSink.close();
+  return buffer.toString();
+}
+
+
+final TESTS = [
+  // Unfinished UTF-8 sequences.
+  [ 0xc3 ],
+  [ 0xE2, 0x82 ],
+  [ 0xF0, 0xA4, 0xAD ],
+  // Overlong encoding of euro-sign.
+  [ 0xF0, 0x82, 0x82, 0xAC ],
+  // Other overlong/unfinished sequences.
+  [ 0xC0 ],
+  [ 0xC1 ],
+  [ 0xF5 ],
+  [ 0xF6 ],
+  [ 0xF7 ],
+  [ 0xF8 ],
+  [ 0xF9 ],
+  [ 0xFA ],
+  [ 0xFB ],
+  [ 0xFC ],
+  [ 0xFD ],
+  [ 0xFE ],
+  [ 0xFF ],
+  [ 0xC0, 0x80 ],
+  [ 0xC1, 0x80 ],
+  // Outside valid range.
+  [ 0xF4, 0xBF, 0xBF, 0xBF ]];
+
+final TESTS2 = [
+  // Test that 0xC0|1, 0x80 does not eat the next character.
+  [[ 0xC0, 0x80, 0x61 ], "Xa" ],
+  [[ 0xC1, 0x80, 0x61 ], "Xa" ],
+  // 0xF5 .. 0xFF never appear in valid UTF-8 sequences.
+  [[ 0xF5, 0x80 ], "XX" ],
+  [[ 0xF6, 0x80 ], "XX" ],
+  [[ 0xF7, 0x80 ], "XX" ],
+  [[ 0xF8, 0x80 ], "XX" ],
+  [[ 0xF9, 0x80 ], "XX" ],
+  [[ 0xFA, 0x80 ], "XX" ],
+  [[ 0xFB, 0x80 ], "XX" ],
+  [[ 0xFC, 0x80 ], "XX" ],
+  [[ 0xFD, 0x80 ], "XX" ],
+  [[ 0xFE, 0x80 ], "XX" ],
+  [[ 0xFF, 0x80 ], "XX" ],
+  [[ 0xF5, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF6, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF7, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF8, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF9, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFA, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFB, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFC, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFD, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFE, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFF, 0x80, 0x61 ], "XXa" ],
+  // Characters outside the valid range.
+  [[ 0xF5, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF6, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF7, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF8, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF9, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFA, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFB, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFC, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFD, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFE, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFF, 0x80, 0x80, 0x61 ], "XXXa" ]];
+
+main() {
+  var allTests = TESTS.expand((test) {
+    // Pairs of test and expected string output when malformed strings are
+    // allowed. Replacement character: U+FFFD
+    return [[ test, "\u{FFFD}" ],
+            [ new List.from([0x61])..addAll(test), "a\u{FFFD}" ],
+            [ new List.from([0x61])..addAll(test)..add(0x61), "a\u{FFFD}a" ],
+            [ new List.from(test)..add(0x61), "\u{FFFD}a" ],
+            [ new List.from(test)..addAll(test), "\u{FFFD}\u{FFFD}" ],
+            [ new List.from(test)..add(0x61)..addAll(test),
+              "\u{FFFD}a\u{FFFD}" ],
+            [ new List.from([0xc3, 0xa5])..addAll(test), "å\u{FFFD}" ],
+            [ new List.from([0xc3, 0xa5])..addAll(test)..addAll([0xc3, 0xa5]),
+              "å\u{FFFD}å" ],
+            [ new List.from(test)..addAll([0xc3, 0xa5]), "\u{FFFD}å" ],
+            [ new List.from(test)..addAll([0xc3, 0xa5])..addAll(test),
+              "\u{FFFD}å\u{FFFD}" ]];
+  });
+
+  var allTests2 = TESTS2.map((test) {
+    // Pairs of test and expected string output when malformed strings are
+    // allowed. Replacement character: U+FFFD
+    String expected = test[1].replaceAll("X", "\u{FFFD}");
+    return [test[0], expected];
+  });
+
+  for (var test in []..addAll(allTests)..addAll(allTests2)) {
+    List<int> bytes = test[0];
+    Expect.throws(() => decode(bytes), (e) => e is FormatException);
+
+    String expected = test[1];
+    Expect.equals(expected, decodeAllowMalformed(bytes));
+  }
+}
diff --git a/tests/lib/convert/chunked_conversion_utf87_test.dart b/tests/lib/convert/chunked_conversion_utf87_test.dart
new file mode 100644
index 0000000..60caf58
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf87_test.dart
@@ -0,0 +1,154 @@
+// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+
+String decode(List<int> inputBytes) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  inputByteSink.add(inputBytes);
+  inputByteSink.close();
+  return UTF8.decode(bytes);
+}
+
+String decode2(List<int> inputBytes) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  inputBytes.forEach((b) => inputByteSink.addSlice([0, b, 1], 1, 2, false));
+  inputByteSink.close();
+  return UTF8.decode(bytes);
+}
+
+String decodeAllowMalformed(List<int> inputBytes) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(true);
+  inputByteSink.add(inputBytes);
+  inputByteSink.close();
+  return UTF8.decode(bytes);
+}
+
+String decodeAllowMalformed2(List<int> inputBytes) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(true);
+  inputBytes.forEach((b) => inputByteSink.addSlice([0, b, 1], 1, 2, false));
+  inputByteSink.close();
+  return UTF8.decode(bytes);
+}
+
+
+final TESTS = [
+  // Unfinished UTF-8 sequences.
+  [ 0xc3 ],
+  [ 0xE2, 0x82 ],
+  [ 0xF0, 0xA4, 0xAD ],
+  // Overlong encoding of euro-sign.
+  [ 0xF0, 0x82, 0x82, 0xAC ],
+  // Other overlong/unfinished sequences.
+  [ 0xC0 ],
+  [ 0xC1 ],
+  [ 0xF5 ],
+  [ 0xF6 ],
+  [ 0xF7 ],
+  [ 0xF8 ],
+  [ 0xF9 ],
+  [ 0xFA ],
+  [ 0xFB ],
+  [ 0xFC ],
+  [ 0xFD ],
+  [ 0xFE ],
+  [ 0xFF ],
+  [ 0xC0, 0x80 ],
+  [ 0xC1, 0x80 ],
+  // Outside valid range.
+  [ 0xF4, 0xBF, 0xBF, 0xBF ]];
+
+final TESTS2 = [
+  // Test that 0xC0|1, 0x80 does not eat the next character.
+  [[ 0xC0, 0x80, 0x61 ], "Xa" ],
+  [[ 0xC1, 0x80, 0x61 ], "Xa" ],
+  // 0xF5 .. 0xFF never appear in valid UTF-8 sequences.
+  [[ 0xF5, 0x80 ], "XX" ],
+  [[ 0xF6, 0x80 ], "XX" ],
+  [[ 0xF7, 0x80 ], "XX" ],
+  [[ 0xF8, 0x80 ], "XX" ],
+  [[ 0xF9, 0x80 ], "XX" ],
+  [[ 0xFA, 0x80 ], "XX" ],
+  [[ 0xFB, 0x80 ], "XX" ],
+  [[ 0xFC, 0x80 ], "XX" ],
+  [[ 0xFD, 0x80 ], "XX" ],
+  [[ 0xFE, 0x80 ], "XX" ],
+  [[ 0xFF, 0x80 ], "XX" ],
+  [[ 0xF5, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF6, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF7, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF8, 0x80, 0x61 ], "XXa" ],
+  [[ 0xF9, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFA, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFB, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFC, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFD, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFE, 0x80, 0x61 ], "XXa" ],
+  [[ 0xFF, 0x80, 0x61 ], "XXa" ],
+  // Characters outside the valid range.
+  [[ 0xF5, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF6, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF7, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF8, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xF9, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFA, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFB, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFC, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFD, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFE, 0x80, 0x80, 0x61 ], "XXXa" ],
+  [[ 0xFF, 0x80, 0x80, 0x61 ], "XXXa" ]];
+
+main() {
+  var allTests = TESTS.expand((test) {
+    // Pairs of test and expected string output when malformed strings are
+    // allowed. Replacement character: U+FFFD
+    return [[ test, "\u{FFFD}" ],
+            [ new List.from([0x61])..addAll(test), "a\u{FFFD}" ],
+            [ new List.from([0x61])..addAll(test)..add(0x61), "a\u{FFFD}a" ],
+            [ new List.from(test)..add(0x61), "\u{FFFD}a" ],
+            [ new List.from(test)..addAll(test), "\u{FFFD}\u{FFFD}" ],
+            [ new List.from(test)..add(0x61)..addAll(test),
+              "\u{FFFD}a\u{FFFD}" ],
+            [ new List.from([0xc3, 0xa5])..addAll(test), "å\u{FFFD}" ],
+            [ new List.from([0xc3, 0xa5])..addAll(test)..addAll([0xc3, 0xa5]),
+              "å\u{FFFD}å" ],
+            [ new List.from(test)..addAll([0xc3, 0xa5]), "\u{FFFD}å" ],
+            [ new List.from(test)..addAll([0xc3, 0xa5])..addAll(test),
+              "\u{FFFD}å\u{FFFD}" ]];
+  });
+
+  var allTests2 = TESTS2.map((test) {
+    // Pairs of test and expected string output when malformed strings are
+    // allowed. Replacement character: U+FFFD
+    String expected = test[1].replaceAll("X", "\u{FFFD}");
+    return [test[0], expected];
+  });
+
+  for (var test in []..addAll(allTests)..addAll(allTests2)) {
+    List<int> bytes = test[0];
+    Expect.throws(() => decode(bytes), (e) => e is FormatException);
+    Expect.throws(() => decode2(bytes), (e) => e is FormatException);
+
+    String expected = test[1];
+    Expect.equals(expected, decodeAllowMalformed(bytes));
+    Expect.equals(expected, decodeAllowMalformed2(bytes));
+  }
+}
diff --git a/tests/lib/convert/chunked_conversion_utf88_test.dart b/tests/lib/convert/chunked_conversion_utf88_test.dart
new file mode 100644
index 0000000..c9a968c
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf88_test.dart
@@ -0,0 +1,249 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library utf8_test;
+import "package:expect/expect.dart";
+import 'dart:convert';
+
+List<int> encode(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  stringConversionSink.add(str);
+  stringConversionSink.close();
+  return bytes;
+}
+
+List<int> encode2(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  stringSink.write(str);
+  stringSink.close();
+  return bytes;
+}
+
+List<int> encode3(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  str.codeUnits.forEach(stringSink.writeCharCode);
+  stringSink.close();
+  return bytes;
+}
+
+List<int> encode4(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ClosableStringSink stringSink = stringConversionSink.asStringSink();
+  str.runes.forEach(stringSink.writeCharCode);
+  stringSink.close();
+  return bytes;
+}
+
+List<int> encode5(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  List<int> tmpBytes = UTF8.encode(str);
+  inputByteSink.add(tmpBytes);
+  inputByteSink.close();
+  return bytes;
+}
+
+List<int> encode6(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  ByteConversionSink inputByteSink = stringConversionSink.asUtf8Sink(false);
+  List<int> tmpBytes = UTF8.encode(str);
+  tmpBytes.forEach((b) => inputByteSink.addSlice([0, b, 1], 1, 2, false));
+  inputByteSink.close();
+  return bytes;
+}
+
+List<int> encode7(String str) {
+  List<int> bytes;
+  ChunkedConversionSink byteSink =
+      new ByteConversionSink.withCallback((result) => bytes = result);
+  var stringConversionSink = new Utf8Encoder().startChunkedConversion(byteSink);
+  stringConversionSink.addSlice("1" + str + "2", 1, str.length + 1, false);
+  stringConversionSink.close();
+  return bytes;
+}
+
+
+int _nextPowerOf2(v) {
+  assert(v > 0);
+  v--;
+  v |= v >> 1;
+  v |= v >> 2;
+  v |= v >> 4;
+  v |= v >> 8;
+  v |= v >> 16;
+  v++;
+  return v;
+}
+
+runTest(test) {
+  List<int> bytes = test[0];
+  String string = test[1];
+  Expect.listEquals(bytes, encode(string));
+  Expect.listEquals(bytes, encode2(string));
+  Expect.listEquals(bytes, encode3(string));
+  Expect.listEquals(bytes, encode4(string));
+  Expect.listEquals(bytes, encode5(string));
+  Expect.listEquals(bytes, encode6(string));
+  Expect.listEquals(bytes, encode7(string));
+}
+
+main() {
+  const LEADING_SURROGATE = 0xd801;
+  const TRAILING_SURROGATE = 0xdc12;
+  const UTF8_ENCODING = const [0xf0, 0x90, 0x90, 0x92];
+  const UTF8_LEADING = const [0xed, 0xa0, 0x81];
+  const UTF8_TRAILING = const [0xed, 0xb0, 0x92];
+  const CHAR_A = 0x61;
+
+  // Test surrogates at all kinds of locations.
+  var tests = [];
+  List codeUnits = <int>[];
+  for (int i = 0; i < 2049; i++) {
+    // Invariant: codeUnits[0..i - 1] is filled with CHAR_A (character 'a').
+    codeUnits.length = i + 1;
+    codeUnits[i] = CHAR_A;
+
+    // Only test for problem zones, close to powers of two.
+    if (i > 20 && _nextPowerOf2(i - 2) - i > 10) continue;
+
+    codeUnits[i] = LEADING_SURROGATE;
+    var str = new String.fromCharCodes(codeUnits);
+    var bytes = new List.filled(i + 3, CHAR_A);
+    bytes[i] = UTF8_LEADING[0];
+    bytes[i + 1] = UTF8_LEADING[1];
+    bytes[i + 2] = UTF8_LEADING[2];
+    runTest([bytes, str]);
+
+    codeUnits[i] = TRAILING_SURROGATE;
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 3, CHAR_A);
+    bytes[i] = UTF8_TRAILING[0];
+    bytes[i + 1] = UTF8_TRAILING[1];
+    bytes[i + 2] = UTF8_TRAILING[2];
+    runTest([bytes, str]);
+
+    codeUnits.length = i + 2;
+    codeUnits[i] = LEADING_SURROGATE;
+    codeUnits[i + 1] = TRAILING_SURROGATE;
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 4, CHAR_A);
+    bytes[i] = UTF8_ENCODING[0];
+    bytes[i + 1] = UTF8_ENCODING[1];
+    bytes[i + 2] = UTF8_ENCODING[2];
+    bytes[i + 3] = UTF8_ENCODING[3];
+    runTest([bytes, str]);
+
+    codeUnits[i] = TRAILING_SURROGATE;
+    codeUnits[i + 1] = TRAILING_SURROGATE;
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 6, CHAR_A);
+    bytes[i] = UTF8_TRAILING[0];
+    bytes[i + 1] = UTF8_TRAILING[1];
+    bytes[i + 2] = UTF8_TRAILING[2];
+    bytes[i + 3] = UTF8_TRAILING[0];
+    bytes[i + 4] = UTF8_TRAILING[1];
+    bytes[i + 5] = UTF8_TRAILING[2];
+    runTest([bytes, str]);
+
+    codeUnits[i] = LEADING_SURROGATE;
+    codeUnits[i + 1] = LEADING_SURROGATE;
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 6, CHAR_A);
+    bytes[i] = UTF8_LEADING[0];
+    bytes[i + 1] = UTF8_LEADING[1];
+    bytes[i + 2] = UTF8_LEADING[2];
+    bytes[i + 3] = UTF8_LEADING[0];
+    bytes[i + 4] = UTF8_LEADING[1];
+    bytes[i + 5] = UTF8_LEADING[2];
+    runTest([bytes, str]);
+
+    codeUnits[i] = TRAILING_SURROGATE;
+    codeUnits[i + 1] = LEADING_SURROGATE;
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 6, CHAR_A);
+    bytes[i] = UTF8_TRAILING[0];
+    bytes[i + 1] = UTF8_TRAILING[1];
+    bytes[i + 2] = UTF8_TRAILING[2];
+    bytes[i + 3] = UTF8_LEADING[0];
+    bytes[i + 4] = UTF8_LEADING[1];
+    bytes[i + 5] = UTF8_LEADING[2];
+    runTest([bytes, str]);
+
+    codeUnits.length = i + 3;
+    codeUnits[i] = LEADING_SURROGATE;
+    codeUnits[i + 1] = TRAILING_SURROGATE;
+    codeUnits[i + 2] = CHAR_A;  // Add trailing 'a'.
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 5, CHAR_A);
+    bytes[i] = UTF8_ENCODING[0];
+    bytes[i + 1] = UTF8_ENCODING[1];
+    bytes[i + 2] = UTF8_ENCODING[2];
+    bytes[i + 3] = UTF8_ENCODING[3];
+    // No need to assign the 'a' character. The whole list is already filled
+    // with it.
+    runTest([bytes, str]);
+
+    codeUnits[i] = TRAILING_SURROGATE;
+    codeUnits[i + 1] = TRAILING_SURROGATE;
+    codeUnits[i + 2] = CHAR_A;  // Add trailing 'a'.
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 7, CHAR_A);
+    bytes[i] = UTF8_TRAILING[0];
+    bytes[i + 1] = UTF8_TRAILING[1];
+    bytes[i + 2] = UTF8_TRAILING[2];
+    bytes[i + 3] = UTF8_TRAILING[0];
+    bytes[i + 4] = UTF8_TRAILING[1];
+    bytes[i + 5] = UTF8_TRAILING[2];
+    runTest([bytes, str]);
+
+    codeUnits[i] = LEADING_SURROGATE;
+    codeUnits[i + 1] = LEADING_SURROGATE;
+    codeUnits[i + 2] = CHAR_A;  // Add trailing 'a'.
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 7, CHAR_A);
+    bytes[i] = UTF8_LEADING[0];
+    bytes[i + 1] = UTF8_LEADING[1];
+    bytes[i + 2] = UTF8_LEADING[2];
+    bytes[i + 3] = UTF8_LEADING[0];
+    bytes[i + 4] = UTF8_LEADING[1];
+    bytes[i + 5] = UTF8_LEADING[2];
+    runTest([bytes, str]);
+
+    codeUnits[i] = TRAILING_SURROGATE;
+    codeUnits[i + 1] = LEADING_SURROGATE;
+    codeUnits[i + 2] = CHAR_A;  // Add trailing 'a'.
+    str = new String.fromCharCodes(codeUnits);
+    bytes = new List.filled(i + 7, CHAR_A);
+    bytes[i] = UTF8_TRAILING[0];
+    bytes[i + 1] = UTF8_TRAILING[1];
+    bytes[i + 2] = UTF8_TRAILING[2];
+    bytes[i + 3] = UTF8_LEADING[0];
+    bytes[i + 4] = UTF8_LEADING[1];
+    bytes[i + 5] = UTF8_LEADING[2];
+    runTest([bytes, str]);
+
+    // Make sure the invariant is correct.
+    codeUnits[i] = CHAR_A;
+  }
+}
diff --git a/tests/lib/convert/chunked_conversion_utf8_test.dart b/tests/lib/convert/chunked_conversion_utf8_test.dart
new file mode 100644
index 0000000..750bac2
--- /dev/null
+++ b/tests/lib/convert/chunked_conversion_utf8_test.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:convert';
+import 'unicode_tests.dart';
+
+String decode(List<int> bytes, int chunkSize) {
+  StringBuffer buffer = new StringBuffer();
+  ChunkedConversionSink stringSink =
+      new StringConversionSink.fromStringSink(buffer);
+  var byteSink = new Utf8Decoder().startChunkedConversion(stringSink);
+  int i = 0;
+  while (i < bytes.length) {
+    List nextChunk = [];
+    for (int j = 0; j < chunkSize; j++) {
+      if (i < bytes.length) {
+        nextChunk.add(bytes[i]);
+        i++;
+      }
+    }
+    byteSink.add(nextChunk);
+  }
+  byteSink.close();
+  return buffer.toString();
+}
+
+
+main() {
+  for (var test in UNICODE_TESTS) {
+    var bytes = test[0];
+    var expected = test[1];
+    Expect.stringEquals(expected, decode(bytes, 1));
+    Expect.stringEquals(expected, decode(bytes, 2));
+    Expect.stringEquals(expected, decode(bytes, 3));
+    Expect.stringEquals(expected, decode(bytes, 4));
+  }
+}
diff --git a/tests/lib/convert/json_unicode_tests.dart b/tests/lib/convert/json_unicode_tests.dart
new file mode 100644
index 0000000..8537bc1
--- /dev/null
+++ b/tests/lib/convert/json_unicode_tests.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library json_unicode_tests;
+import 'unicode_tests.dart';
+
+const _QUOTE = 0x22;  // "
+const _COLON = 0x3a;  // :
+const _COMMA = 0x2c;  // ,
+const _BRACE_OPEN = 0x7b;  // {
+const _BRACE_CLOSE = 0x7d;  // }
+const _BRACKET_OPEN = 0x5b;  // [
+const _BRACKET_CLOSE = 0x5d;  // ]
+
+_expandUnicodeTests() {
+  return UNICODE_TESTS.expand((test) {
+    // The unicode test will be a string (possibly) containing unicode
+    // characters. It also contains the empty string.
+    // It must not contain a double-quote '"'.
+    assert(!test.contains('"'));
+
+    var bytes = test[0];
+    var string = test[1];
+
+    // expanded will hold all tests that are generated from the unicode test.
+    var expanded = [];
+
+    // Put the string into quotes.
+    // For example: 'abcd' -> '"abcd"'.
+    var inQuotesBytes = [];
+    inQuotesBytes.add(_QUOTE);
+    inQuotesBytes.addAll(bytes);
+    inQuotesBytes.add(_QUOTE);
+    expanded.add([inQuotesBytes, string]);
+
+    // Put the quoted string into a triple nested list.
+    // For example: 'abcd' -> '[[["abcd"]]]'.
+    var listExpected = [[[string]]];
+    var inListBytes = [];
+    inListBytes.addAll([_BRACKET_OPEN, _BRACKET_OPEN, _BRACKET_OPEN]);
+    inListBytes.addAll(inQuotesBytes);
+    inListBytes.addAll([_BRACKET_CLOSE, _BRACKET_CLOSE, _BRACKET_CLOSE]);
+    expanded.add([inListBytes, listExpected]);
+
+    // Put the quoted string into a triple nested list and duplicate that
+    // list three times.
+    // For example: 'abcd' -> '[[[["abcd"]]],[[["abcd"]]],[[["abcd"]]]]'.
+    var listLongerExpected = [listExpected, listExpected, listExpected];
+    var listLongerBytes = [];
+    listLongerBytes.add(_BRACKET_OPEN);
+    listLongerBytes.addAll(inListBytes);
+    listLongerBytes.add(_COMMA);
+    listLongerBytes.addAll(inListBytes);
+    listLongerBytes.add(_COMMA);
+    listLongerBytes.addAll(inListBytes);
+    listLongerBytes.add(_BRACKET_CLOSE);
+    expanded.add([listLongerBytes, listLongerExpected]);
+
+    // Put the previous strings/lists into a map.
+    // For example:
+    //    'abcd' -> '{"abcd":[[[["abcd"]]],[[["abcd"]]],[[["abcd"]]]]}'.
+    var mapExpected = new Map();
+    mapExpected[string] = listLongerExpected;
+    var mapBytes = [];
+    mapBytes.add(_BRACE_OPEN);
+    mapBytes.addAll(inQuotesBytes);
+    mapBytes.add(_COLON);
+    mapBytes.addAll(listLongerBytes);
+    mapBytes.add(_BRACE_CLOSE);
+    expanded.add([mapBytes, mapExpected]);
+
+    return expanded;
+  }).toList();
+}
+
+final JSON_UNICODE_TESTS = _expandUnicodeTests();
diff --git a/tests/lib/convert/streamed_conversion_json_decode1_test.dart b/tests/lib/convert/streamed_conversion_json_decode1_test.dart
new file mode 100644
index 0000000..67e8383
--- /dev/null
+++ b/tests/lib/convert/streamed_conversion_json_decode1_test.dart
@@ -0,0 +1,99 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:convert';
+import '../../async_helper.dart';
+
+final TESTS = [
+    [ 5, '5' ],
+    [ -42, '-42' ],
+    [ 3.14, '3.14' ],
+    [ true, 'true' ],
+    [ false, 'false' ],
+    [ null, 'null' ],
+    [ 'quote"or\'', '"quote\\"or\'"' ],
+    [ '', '""' ],
+    [ [], "[]" ],
+    [ [3, -4.5, true, "hi", false], '[3,-4.5,true,"hi",false]' ],
+    [ [null], "[null]" ],
+    [ [[null]], "[[null]]" ],
+    [ [[3]], "[[3]]" ],
+    [ {}, "{}" ],
+    [ { "x": 3, "y": 4.5, "z": "hi", "u": true, "v": false },
+      '{"x":3,"y":4.5,"z":"hi","u":true,"v":false}' ],
+    [ { "x": null }, '{"x":null}' ],
+    [ { "x": {} }, '{"x":{}}' ],
+    // Note that -0.0 won't be treated the same in JS. The Json spec seems to
+    // allow it, though.
+    [ { "hi there": 499, "'":-0.0 }, '{"hi there":499,"\'":-0.0}' ],
+    [ r'\foo', r'"\\foo"' ],
+  ];
+
+bool isJsonEqual(o1, o2) {
+  if (o1 == o2) return true;
+  if (o1 is List && o2 is List) {
+    if (o1.length != o2.length) return false;
+    for (int i = 0; i < o1.length; i++) {
+      if (!isJsonEqual(o1[i], o2[i])) return false;
+    }
+    return true;
+  }
+  if (o1 is Map && o2 is Map) {
+    if (o1.length != o2.length) return false;
+    for (var key in o1.keys) {
+      Expect.isTrue(key is String);
+      if (!o2.containsKey(key)) return false;
+      if (!isJsonEqual(o1[key], o2[key])) return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+Stream<Object> createStream(List<String> chunks) {
+  var decoder = new JsonDecoder(null);
+  var controller;
+  controller = new StreamController(onListen: () {
+    chunks.forEach(controller.add);
+    controller.close();
+  });
+  return controller.stream.transform(decoder);
+}
+
+Stream<Object> decode(String str) {
+  return createStream([str]);
+}
+
+Stream<Object> decode2(String str) {
+  return createStream(str.split(""));
+}
+
+void checkIsJsonEqual(expected, stream) {
+  asyncStart();
+  stream.single.then((o) {
+    Expect.isTrue(isJsonEqual(expected, o));
+    asyncEnd();
+  });
+}
+
+void main() {
+  var tests = TESTS.expand((test) {
+    var object = test[0];
+    var string = test[1];
+    var longString = "                                                        "
+                     "                                                        "
+                     "$string"
+                     "                                                        "
+                     "                                                        ";
+    return [test, [object, longString]];
+  });
+  for (var test in TESTS) {
+    var o = test[0];
+    var string = test[1];
+    checkIsJsonEqual(o, decode(string));
+    checkIsJsonEqual(o, decode2(string));
+  }
+}
diff --git a/tests/lib/convert/streamed_conversion_json_encode1_test.dart b/tests/lib/convert/streamed_conversion_json_encode1_test.dart
new file mode 100644
index 0000000..1a31ea6
--- /dev/null
+++ b/tests/lib/convert/streamed_conversion_json_encode1_test.dart
@@ -0,0 +1,77 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:convert';
+import '../../async_helper.dart';
+
+final TESTS = [
+    [ 5, '5' ],
+    [ -42, '-42' ],
+    [ 3.14, '3.14' ],
+    [ true, 'true' ],
+    [ false, 'false' ],
+    [ null, 'null' ],
+    [ 'quote"or\'', '"quote\\"or\'"' ],
+    [ '', '""' ],
+    [ [], "[]" ],
+    [ [3, -4.5, true, "hi", false], '[3,-4.5,true,"hi",false]' ],
+    [ [null], "[null]" ],
+    [ [[null]], "[[null]]" ],
+    [ [[3]], "[[3]]" ],
+    [ {}, "{}" ],
+    [ { "x": 3, "y": 4.5, "z": "hi", "u": true, "v": false },
+      '{"x":3,"y":4.5,"z":"hi","u":true,"v":false}' ],
+    [ { "x": null }, '{"x":null}' ],
+    [ { "x": {} }, '{"x":{}}' ],
+    // Note that -0.0 won't be treated the same in JS. The Json spec seems to
+    // allow it, though.
+    [ { "hi there": 499, "'":-0.0 }, '{"hi there":499,"\'":-0.0}' ],
+    [ r'\foo', r'"\\foo"' ],
+  ];
+
+Stream<String> encode(Object o) {
+  var encoder = new JsonEncoder();
+  StreamController controller;
+  controller = new StreamController(onListen: () {
+    controller.add(o);
+    controller.close();
+  });
+  return controller.stream.transform(encoder);
+}
+
+void testNoPause(String expected, Object o) {
+  asyncStart();
+  Stream stream = encode(o);
+  stream.toList().then((list) {
+    StringBuffer buffer = new StringBuffer();
+    buffer.writeAll(list);
+    Expect.stringEquals(expected, buffer.toString());
+    asyncEnd();
+  });
+}
+
+void testWithPause(String expected, Object o) {
+  asyncStart();
+  Stream stream = encode(o);
+  StringBuffer buffer = new StringBuffer();
+  var sub;
+  sub = stream.listen((x) {
+    buffer.write(x);
+    sub.pause(new Future.delayed(Duration.ZERO));
+  }, onDone: () {
+    Expect.stringEquals(expected, buffer.toString());
+    asyncEnd();
+  });
+}
+
+void main() {
+  for (var test in TESTS) {
+    var o = test[0];
+    var expected = test[1];
+    testNoPause(expected, o);
+    testWithPause(expected, o);
+  }
+}
diff --git a/tests/lib/convert/streamed_conversion_json_utf8_decode_test.dart b/tests/lib/convert/streamed_conversion_json_utf8_decode_test.dart
new file mode 100644
index 0000000..d40a204
--- /dev/null
+++ b/tests/lib/convert/streamed_conversion_json_utf8_decode_test.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:convert';
+import 'json_unicode_tests.dart';
+import '../../async_helper.dart';
+
+final JSON_UTF8 = JSON.fuse(UTF8);
+
+bool isJsonEqual(o1, o2) {
+  if (o1 == o2) return true;
+  if (o1 is List && o2 is List) {
+    if (o1.length != o2.length) return false;
+    for (int i = 0; i < o1.length; i++) {
+      if (!isJsonEqual(o1[i], o2[i])) return false;
+    }
+    return true;
+  }
+  if (o1 is Map && o2 is Map) {
+    if (o1.length != o2.length) return false;
+    for (var key in o1.keys) {
+      Expect.isTrue(key is String);
+      if (!o2.containsKey(key)) return false;
+      if (!isJsonEqual(o1[key], o2[key])) return false;
+    }
+    return true;
+  }
+  return false;
+}
+
+Stream<Object> createStream(List<List<int>> chunks) {
+  var controller;
+  controller = new StreamController(onListen: () {
+    chunks.forEach(controller.add);
+    controller.close();
+  });
+  return controller.stream.transform(JSON_UTF8.decoder);
+}
+
+Stream<Object> decode(List<int> bytes) {
+  return createStream([bytes]);
+}
+
+Stream<Object> decodeChunked(List<int> bytes, int chunkSize) {
+  List<List<int>> chunked = <List<int>>[];
+  int i = 0;
+  while (i < bytes.length) {
+    if (i + chunkSize <= bytes.length) {
+      chunked.add(bytes.sublist(i, i + chunkSize));
+    } else {
+      chunked.add(bytes.sublist(i));
+    }
+    i += chunkSize;
+  }
+  return createStream(chunked);
+}
+
+void checkIsJsonEqual(expected, stream) {
+  asyncStart();
+  stream.single.then((o) {
+    Expect.isTrue(isJsonEqual(expected, o));
+    asyncEnd();
+  });
+}
+
+main() {
+  for (var test in JSON_UNICODE_TESTS) {
+    var bytes = test[0];
+    var o = test[1];
+    checkIsJsonEqual(o, decode(bytes));
+    checkIsJsonEqual(o, decodeChunked(bytes, 1));
+    checkIsJsonEqual(o, decodeChunked(bytes, 2));
+    checkIsJsonEqual(o, decodeChunked(bytes, 3));
+    checkIsJsonEqual(o, decodeChunked(bytes, 4));
+    checkIsJsonEqual(o, decodeChunked(bytes, 5));
+  }
+}
diff --git a/tests/lib/convert/streamed_conversion_json_utf8_encode_test.dart b/tests/lib/convert/streamed_conversion_json_utf8_encode_test.dart
new file mode 100644
index 0000000..ca3f2dd
--- /dev/null
+++ b/tests/lib/convert/streamed_conversion_json_utf8_encode_test.dart
@@ -0,0 +1,53 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:convert';
+import 'json_unicode_tests.dart';
+import '../../async_helper.dart';
+
+final JSON_UTF8 = JSON.fuse(UTF8);
+
+
+Stream<List<int>> encode(Object o) {
+  var controller;
+  controller = new StreamController(onListen: () {
+    controller.add(o);
+    controller.close();
+  });
+  return controller.stream.transform(JSON_UTF8.encoder);
+}
+
+void testUnpaused(List<int> expected, Stream stream) {
+  asyncStart();
+  stream.toList().then((list) {
+    var combined = [];
+    list.forEach(combined.addAll);
+    Expect.listEquals(expected, combined);
+    asyncEnd();
+  });
+}
+
+void testWithPauses(List<int> expected, Stream stream) {
+  asyncStart();
+  var accumulated = [];
+  var sub;
+  sub = stream.listen((x) {
+    accumulated.addAll(x);
+    sub.pause(new Future.delayed(Duration.ZERO));
+  }, onDone: () {
+    Expect.listEquals(expected, accumulated);
+    asyncEnd();
+  });
+}
+
+void main() {
+  for (var test in JSON_UNICODE_TESTS) {
+    var expected = test[0];
+    var object = test[1];
+    testUnpaused(expected, encode(object));
+    testWithPauses(expected, encode(object));
+  }
+}
diff --git a/tests/lib/convert/streamed_conversion_utf8_decode_test.dart b/tests/lib/convert/streamed_conversion_utf8_decode_test.dart
new file mode 100644
index 0000000..e29d384
--- /dev/null
+++ b/tests/lib/convert/streamed_conversion_utf8_decode_test.dart
@@ -0,0 +1,66 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:convert';
+import 'unicode_tests.dart';
+import '../../async_helper.dart';
+
+Stream<String> decode(List<int> bytes, int chunkSize) {
+  var controller;
+  controller = new StreamController(onListen: () {
+    int i = 0;
+    while (i < bytes.length) {
+      List nextChunk = [];
+      for (int j = 0; j < chunkSize; j++) {
+        if (i < bytes.length) {
+          nextChunk.add(bytes[i]);
+          i++;
+        }
+      }
+      controller.add(nextChunk);
+    }
+    controller.close();
+  });
+  return controller.stream.transform(UTF8.decoder);
+}
+
+testUnpaused(String expected, Stream stream) {
+  asyncStart();
+  stream.toList().then((list) {
+    StringBuffer buffer = new StringBuffer();
+    buffer.writeAll(list);
+    Expect.stringEquals(expected, buffer.toString());
+    asyncEnd();
+  });
+}
+
+testWithPauses(String expected, Stream stream) {
+  asyncStart();
+  StringBuffer buffer = new StringBuffer();
+  var sub;
+  sub = stream.listen((x) {
+    buffer.write(x);
+    sub.pause(new Future.delayed(Duration.ZERO));
+  }, onDone: () {
+    Expect.stringEquals(expected, buffer.toString());
+    asyncEnd();
+  });
+}
+
+main() {
+  for (var test in UNICODE_TESTS) {
+    var bytes = test[0];
+    var expected = test[1];
+    testUnpaused(expected, decode(bytes, 1));
+    testWithPauses(expected, decode(bytes, 1));
+    testUnpaused(expected, decode(bytes, 2));
+    testWithPauses(expected, decode(bytes, 2));
+    testUnpaused(expected, decode(bytes, 3));
+    testWithPauses(expected, decode(bytes, 3));
+    testUnpaused(expected, decode(bytes, 4));
+    testWithPauses(expected, decode(bytes, 4));
+  }
+}
diff --git a/tests/lib/convert/streamed_conversion_utf8_encode_test.dart b/tests/lib/convert/streamed_conversion_utf8_encode_test.dart
new file mode 100644
index 0000000..a5be9bc
--- /dev/null
+++ b/tests/lib/convert/streamed_conversion_utf8_encode_test.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import "package:expect/expect.dart";
+import 'dart:async';
+import 'dart:convert';
+import 'unicode_tests.dart';
+import '../../async_helper.dart';
+
+Stream<List<int>> encode(String string, int chunkSize) {
+  var controller;
+  controller = new StreamController(onListen: () {
+    int i = 0;
+    while (i < string.length) {
+      if (i + chunkSize <= string.length) {
+        controller.add(string.substring(i, i + chunkSize));
+      } else {
+        controller.add(string.substring(i));
+      }
+      i += chunkSize;
+    }
+    controller.close();
+  });
+  return controller.stream.transform(UTF8.encoder);
+}
+
+void testUnpaused(List<int> expected, Stream stream) {
+  asyncStart();
+  stream.toList().then((list) {
+    var combined = [];
+    // Flatten the list.
+    list.forEach(combined.addAll);
+    Expect.listEquals(expected, combined);
+    asyncEnd();
+  });
+}
+
+void testWithPauses(List<int> expected, Stream stream) {
+  asyncStart();
+  var combined = [];
+  var sub;
+  sub = stream.listen((x) {
+    combined.addAll(x);
+    sub.pause(new Future.delayed(Duration.ZERO));
+  }, onDone: () {
+    Expect.listEquals(expected, combined);
+    asyncEnd();
+  });
+}
+
+main() {
+  for (var test in UNICODE_TESTS) {
+    var expected = test[0];
+    var string = test[1];
+    testUnpaused(expected, encode(string, 1));
+    testWithPauses(expected, encode(string, 1));
+    testUnpaused(expected, encode(string, 2));
+    testWithPauses(expected, encode(string, 2));
+  }
+}
diff --git a/tests/lib/convert/unicode_tests.dart b/tests/lib/convert/unicode_tests.dart
new file mode 100644
index 0000000..1f90b7b
--- /dev/null
+++ b/tests/lib/convert/unicode_tests.dart
@@ -0,0 +1,76 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library unicode_tests;
+
+// Google favorite: "Îñţérñåţîöñåļîžåţîờñ".
+const INTER_BYTES = const [0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9, 0x72,
+                           0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3, 0xae, 0xc3,
+                           0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4, 0xbc, 0xc3, 0xae,
+                           0xc5, 0xbe, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3, 0xae, 0xe1,
+                           0xbb, 0x9d, 0xc3, 0xb1];
+const INTER_STRING = "Îñţérñåţîöñåļîžåţîờñ";
+
+// Blueberry porridge in Danish: "blåbærgrød".
+const BLUEBERRY_BYTES = const [0x62, 0x6c, 0xc3, 0xa5, 0x62, 0xc3, 0xa6, 0x72,
+                               0x67, 0x72, 0xc3, 0xb8, 0x64];
+const BLUEBERRY_STRING = "blåbærgrød";
+
+// "சிவா அணாமாைல", that is "Siva Annamalai" in Tamil.
+const SIVA_BYTES1 = const [0xe0, 0xae, 0x9a, 0xe0, 0xae, 0xbf, 0xe0, 0xae, 0xb5,
+                           0xe0, 0xae, 0xbe, 0x20, 0xe0, 0xae, 0x85, 0xe0, 0xae,
+                           0xa3, 0xe0, 0xae, 0xbe, 0xe0, 0xae, 0xae, 0xe0, 0xae,
+                           0xbe, 0xe0, 0xaf, 0x88, 0xe0, 0xae, 0xb2];
+const SIVA_STRING1 = "சிவா அணாமாைல";
+
+// "िसवा अणामालै", that is "Siva Annamalai" in Devanagari.
+const SIVA_BYTES2 = const [0xe0, 0xa4, 0xbf, 0xe0, 0xa4, 0xb8, 0xe0, 0xa4, 0xb5,
+                           0xe0, 0xa4, 0xbe, 0x20, 0xe0, 0xa4, 0x85, 0xe0, 0xa4,
+                           0xa3, 0xe0, 0xa4, 0xbe, 0xe0, 0xa4, 0xae, 0xe0, 0xa4,
+                           0xbe, 0xe0, 0xa4, 0xb2, 0xe0, 0xa5, 0x88];
+const SIVA_STRING2 = "िसवा अणामालै";
+
+// DESERET CAPITAL LETTER BEE, unicode 0x10412(0xD801+0xDC12)
+// UTF-8: F0 90 90 92
+const BEE_BYTES = const [0xf0, 0x90, 0x90, 0x92];
+const BEE_STRING = "𐐒";
+
+const DIGIT_BYTES = const [ 0x35 ];
+const DIGIT_STRING = "5";
+
+const ASCII_BYTES = const [ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+                            0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
+                            0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+                            0x79, 0x7A ];
+const ASCII_STRING = "abcdefghijklmnopqrstuvwxyz";
+
+const _TEST_PAIRS = const [
+    const [ const [], "" ],
+    const [ INTER_BYTES, INTER_STRING ],
+    const [ BLUEBERRY_BYTES, BLUEBERRY_STRING ],
+    const [ SIVA_BYTES1, SIVA_STRING1 ],
+    const [ SIVA_BYTES2, SIVA_STRING2 ],
+    const [ BEE_BYTES, BEE_STRING ],
+    const [ DIGIT_BYTES, DIGIT_STRING ],
+    const [ ASCII_BYTES, ASCII_STRING ]];
+
+List<List> _expandTestPairs() {
+  assert(2 == BEE_STRING.length);
+  var tests = [];
+  tests.addAll(_TEST_PAIRS);
+  tests.addAll(_TEST_PAIRS.expand((test) {
+    var bytes = test[0];
+    var string = test[1];
+    var longBytes = [];
+    var longString = "";
+    for (int i = 0; i < 100; i++) {
+      longBytes.addAll(bytes);
+      longString += string;
+    }
+    return [test, [longBytes, longString]];
+  }));
+  return tests;
+}
+
+final List UNICODE_TESTS = _expandTestPairs();
diff --git a/tests/lib/convert/utf8_encode_test.dart b/tests/lib/convert/utf8_encode_test.dart
index 3aa208c..4e15646 100644
--- a/tests/lib/convert/utf8_encode_test.dart
+++ b/tests/lib/convert/utf8_encode_test.dart
@@ -2,78 +2,15 @@
 // 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 utf8_test;
 import "package:expect/expect.dart";
 import 'dart:convert';
-
-// Google favorite: "Îñţérñåţîöñåļîžåţîờñ".
-const INTER_BYTES = const [0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9, 0x72,
-                           0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3, 0xae, 0xc3,
-                           0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4, 0xbc, 0xc3, 0xae,
-                           0xc5, 0xbe, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3, 0xae, 0xe1,
-                           0xbb, 0x9d, 0xc3, 0xb1];
-const INTER_STRING = "Îñţérñåţîöñåļîžåţîờñ";
-
-// Blueberry porridge in Danish: "blåbærgrød".
-const BLUEBERRY_BYTES = const [0x62, 0x6c, 0xc3, 0xa5, 0x62, 0xc3, 0xa6, 0x72,
-                               0x67, 0x72, 0xc3, 0xb8, 0x64];
-const BLUEBERRY_STRING = "blåbærgrød";
-
-// "சிவா அணாமாைல", that is "Siva Annamalai" in Tamil.
-const SIVA_BYTES1 = const [0xe0, 0xae, 0x9a, 0xe0, 0xae, 0xbf, 0xe0, 0xae, 0xb5,
-                           0xe0, 0xae, 0xbe, 0x20, 0xe0, 0xae, 0x85, 0xe0, 0xae,
-                           0xa3, 0xe0, 0xae, 0xbe, 0xe0, 0xae, 0xae, 0xe0, 0xae,
-                           0xbe, 0xe0, 0xaf, 0x88, 0xe0, 0xae, 0xb2];
-const SIVA_STRING1 = "சிவா அணாமாைல";
-
-// "िसवा अणामालै", that is "Siva Annamalai" in Devanagari.
-const SIVA_BYTES2 = const [0xe0, 0xa4, 0xbf, 0xe0, 0xa4, 0xb8, 0xe0, 0xa4, 0xb5,
-                           0xe0, 0xa4, 0xbe, 0x20, 0xe0, 0xa4, 0x85, 0xe0, 0xa4,
-                           0xa3, 0xe0, 0xa4, 0xbe, 0xe0, 0xa4, 0xae, 0xe0, 0xa4,
-                           0xbe, 0xe0, 0xa4, 0xb2, 0xe0, 0xa5, 0x88];
-const SIVA_STRING2 = "िसवा अणामालै";
-
-// DESERET CAPITAL LETTER BEE, unicode 0x10412(0xD801+0xDC12)
-// UTF-8: F0 90 90 92
-const BEE_BYTES = const [0xf0, 0x90, 0x90, 0x92];
-const BEE_STRING = "𐐒";
-
-const DIGIT_BYTES = const [ 0x35 ];
-const DIGIT_STRING = "5";
-
-const ASCII_BYTES = const [ 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
-                            0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70,
-                            0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
-                            0x79, 0x7A ];
-const ASCII_STRING = "abcdefghijklmnopqrstuvwxyz";
-
-const TESTS = const [
-    const [ const [], "" ],
-    const [ INTER_BYTES, INTER_STRING ],
-    const [ BLUEBERRY_BYTES, BLUEBERRY_STRING ],
-    const [ SIVA_BYTES1, SIVA_STRING1 ],
-    const [ SIVA_BYTES2, SIVA_STRING2 ],
-    const [ BEE_BYTES, BEE_STRING ],
-    const [ DIGIT_BYTES, DIGIT_STRING ],
-    const [ ASCII_BYTES, ASCII_STRING ]];
+import 'unicode_tests.dart';
 
 List<int> encode(String str) => new Utf8Encoder().convert(str);
 List<int> encode2(String str) => UTF8.encode(str);
 
 main() {
-  Expect.equals(2, BEE_STRING.length);
-  var tests = TESTS.expand((test) {
-    var bytes = test[0];
-    var string = test[1];
-    var longBytes = [];
-    var longString = "";
-    for (int i = 0; i < 100; i++) {
-      longBytes.addAll(bytes);
-      longString += string;
-    }
-    return [test, [longBytes, longString]];
-  });
-  for (var test in tests) {
+  for (var test in UNICODE_TESTS) {
     List<int> bytes = test[0];
     String string = test[1];
     Expect.listEquals(bytes, encode(string));
diff --git a/tests/lib/convert/utf8_test.dart b/tests/lib/convert/utf8_test.dart
index 4fa0297..ba5bc89 100644
--- a/tests/lib/convert/utf8_test.dart
+++ b/tests/lib/convert/utf8_test.dart
@@ -2,46 +2,16 @@
 // 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 utf8_test;
 import "package:expect/expect.dart";
 import 'dart:convert';
+import 'unicode_tests.dart';
 
 String decode(List<int> bytes) => new Utf8Decoder().convert(bytes);
 
 main() {
-  // Google favorite: "Îñţérñåţîöñåļîžåţîờñ".
-  String string = decode([0xc3, 0x8e, 0xc3, 0xb1, 0xc5, 0xa3, 0xc3, 0xa9, 0x72,
-                          0xc3, 0xb1, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3, 0xae, 0xc3,
-                          0xb6, 0xc3, 0xb1, 0xc3, 0xa5, 0xc4, 0xbc, 0xc3, 0xae,
-                          0xc5, 0xbe, 0xc3, 0xa5, 0xc5, 0xa3, 0xc3, 0xae, 0xe1,
-                          0xbb, 0x9d, 0xc3, 0xb1]);
-  Expect.stringEquals("Îñţérñåţîöñåļîžåţîờñ", string);
-
-  // Blueberry porridge in Danish: "blåbærgrød".
-  string = decode([0x62, 0x6c, 0xc3, 0xa5, 0x62, 0xc3, 0xa6, 0x72, 0x67, 0x72,
-                   0xc3, 0xb8, 0x64]);
-  Expect.stringEquals("blåbærgrød", string);
-
-  // "சிவா அணாமாைல", that is "Siva Annamalai" in Tamil.
-  string = decode([0xe0, 0xae, 0x9a, 0xe0, 0xae, 0xbf, 0xe0, 0xae, 0xb5, 0xe0,
-                   0xae, 0xbe, 0x20, 0xe0, 0xae, 0x85, 0xe0, 0xae, 0xa3, 0xe0,
-                   0xae, 0xbe, 0xe0, 0xae, 0xae, 0xe0, 0xae, 0xbe, 0xe0, 0xaf,
-                   0x88, 0xe0, 0xae, 0xb2]);
-  Expect.stringEquals("சிவா அணாமாைல", string);
-
-  // "िसवा अणामालै", that is "Siva Annamalai" in Devanagari.
-  string = decode([0xe0, 0xa4, 0xbf, 0xe0, 0xa4, 0xb8, 0xe0, 0xa4, 0xb5, 0xe0,
-                   0xa4, 0xbe, 0x20, 0xe0, 0xa4, 0x85, 0xe0, 0xa4, 0xa3, 0xe0,
-                   0xa4, 0xbe, 0xe0, 0xa4, 0xae, 0xe0, 0xa4, 0xbe, 0xe0, 0xa4,
-                   0xb2, 0xe0, 0xa5, 0x88]);
-  Expect.stringEquals("िसवा अणामालै", string);
-
-  // DESERET CAPITAL LETTER BEE, unicode 0x10412(0xD801+0xDC12)
-  // UTF-8: F0 90 90 92
-  string = decode([0xf0, 0x90, 0x90, 0x92]);
-  Expect.equals(string.length, 2);
-  Expect.equals("𐐒".length, 2);
-  Expect.stringEquals("𐐒", string);
-
-  // TODO(ahe): Add tests of bad input.
+  for (var test in UNICODE_TESTS) {
+    List<int> bytes = test[0];
+    String expected = test[1];
+    Expect.stringEquals(expected, decode(bytes));
+  }
 }
diff --git a/tests/lib/lib.status b/tests/lib/lib.status
index 82cb2b9..1dac7ed 100644
--- a/tests/lib/lib.status
+++ b/tests/lib/lib.status
@@ -14,16 +14,9 @@
 async/run_async6_test: Fail # global error handling is not supported. http://dartbug.com/5958
 async/stream_controller_async_test: Fail, Pass # http://dartbug.com/11953
 
-# SIMD is unsupported on dart2js.
-typed_data/float32x4_test: Fail, OK
-typed_data/float32x4_list_test: Fail, OK
-typed_data/float32x4_unbox_phi_test: Fail, OK
-typed_data/float32x4_unbox_regress_test: Fail, OK
-typed_data/float32x4_shuffle_test: Fail, OK
-typed_data/float32x4_two_arg_shuffle_test: Fail, OK
 
-[ $compiler == dart2js && ($runtime == d8 || $runtime == ie9) ]
-typed_data/byte_data_test: Fail, OK # d8/ie9 doesn't support DataView
+[ $compiler == dart2js && ($runtime == d8) ]
+typed_data/byte_data_test: Fail, OK # d8 doesn't support DataView
 
 [ $csp ]
 mirrors/*: Skip # Issue 6490
@@ -64,22 +57,19 @@
 
 [ $compiler == dart2dart && $minified ]
 json/json_test: Fail                            # Issue 10961
-typed_data/float32x4_test: Fail                 # Issue 10961
-typed_data/float32x4_list_test: Fail            # Issue 10961
-typed_data/float32x4_unbox_phi_test: Fail       # Issue 10961
-typed_data/float32x4_unbox_regress_test: Fail   # Issue 10961
-typed_data/float32x4_shuffle_test: Fail         # Issue 10961
-typed_data/float32x4_two_arg_shuffle_test: Fail # Issue 10961
 
 [ $runtime == ff ]
 # FF setTimeout can fire early: https://bugzilla.mozilla.org/show_bug.cgi?id=291386
 multiple_timer_test: Pass, Fail
 timer_isolate_test: Pass, Fail
 timer_test: Pass, Fail
+convert/chunked_conversion_utf88_test: Pass, Timeout  # Issue 12029
+convert/streamed_conversion_utf8_encode_test: Pass, Timeout  # Issue 12029
 
 [ $runtime == ie9 ]
-typed_data/typed_list_iterable_test: Fail # Issue 11971
-typed_data/typed_data_list_test: Fail # Issue 11971
+# IE9 doesn't support typed arrays.
+typed_data/*: Fail # Issue 11971
+convert/chunked_conversion_utf88_test: Pass, Slow  # Issue 12029
 
 [ $runtime == opera ]
 async/multiple_timer_test: Pass, Fail
@@ -97,9 +87,9 @@
 [ $runtime == vm || ($compiler == none && $runtime == drt) ]
 async/run_async3_test: Fail # _enqueueImmediate runs after Timer. http://dartbug.com/9001.
 mirrors/library_metadata_test: Fail # http://dartbug.com/10906
-mirrors/superclass_test: Fail # http://dartbug.com/11142
 mirrors/parameter_test: Fail # http://dartbug.com/11567
 mirrors/operator_test: Fail # http://dartbug.com/11944
+mirrors/local_isolate_test: Fail # http://dartbug.com/12026
 
 [ $compiler == none && $runtime == drt ]
 async/timer_isolate_test: Skip # See Issue 4997
@@ -116,10 +106,5 @@
 *: Skip
 
 [ $arch == simmips ]
-typed_data/float32x4_unbox_regress_test: Crash # Unimplemented
-typed_data/float32x4_unbox_phi_test: Crash # Unimplemented
-typed_data/float32x4_list_test: Crash # Unimplemented
-typed_data/float32x4_test: Crash # Unimplemented
-typed_data/float32x4_shuffle_test: Crash # Unimplemented
-typed_data/float32x4_two_arg_shuffle_test: Crash # Unimplemented
-
+typed_data/float32x4_shuffle_test: Skip # Issue 11851.
+convert/chunked_conversion_utf88_test: Pass, Slow # Issue 12025.
diff --git a/tests/lib/mirrors/local_isolate_test.dart b/tests/lib/mirrors/local_isolate_test.dart
new file mode 100644
index 0000000..27ca35a
--- /dev/null
+++ b/tests/lib/mirrors/local_isolate_test.dart
@@ -0,0 +1,21 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Test the local IsolateMirror.
+
+library test.local_isolate_test;
+
+import 'dart:mirrors';
+
+import 'package:expect/expect.dart';
+
+class Foo {}
+
+void main() {
+  LibraryMirror rootLibrary = reflectClass(Foo).owner;
+  IsolateMirror isolate = currentMirrorSystem().isolate;
+  Expect.isTrue(isolate.debugName is String);
+  Expect.isTrue(isolate.isCurrent);
+  Expect.equals(rootLibrary, isolate.rootLibrary);
+}
diff --git a/tests/lib/mirrors/method_mirror_returntype_test.dart b/tests/lib/mirrors/method_mirror_returntype_test.dart
index 8057c49..5d13ece 100644
--- a/tests/lib/mirrors/method_mirror_returntype_test.dart
+++ b/tests/lib/mirrors/method_mirror_returntype_test.dart
@@ -6,8 +6,6 @@
 
 import "package:expect/expect.dart";
 
-_n(symbol) => MirrorSystem.getName(symbol);
-
 void voidFunc() {}
 
 dynamicFunc1() {}
@@ -25,20 +23,20 @@
 
   mm = reflect(intFunc).function;
   Expect.equals(true, mm.returnType is TypeMirror);
-  Expect.equals("int", _n(mm.returnType.simpleName));
+  Expect.equals(const Symbol("int"), mm.returnType.simpleName);
   Expect.equals(true, mm.returnType.owner is LibraryMirror);
 
   mm = reflect(dynamicFunc1).function;
   Expect.equals(true, mm.returnType is TypeMirror);
-  Expect.equals("dynamic", _n(mm.returnType.simpleName));
+  Expect.equals(const Symbol("dynamic"), mm.returnType.simpleName);
 
   mm = reflect(dynamicFunc2).function;
   Expect.equals(true, mm.returnType is TypeMirror);
-  Expect.equals("dynamic", _n(mm.returnType.simpleName));
+  Expect.equals(const Symbol("dynamic"), mm.returnType.simpleName);
 
   mm = reflect(voidFunc).function;
   Expect.equals(true, mm.returnType is TypeMirror);
-  Expect.equals("void", _n(mm.returnType.simpleName));
+  Expect.equals(const Symbol("void"), mm.returnType.simpleName);
 
   ClassMirror cm = reflectClass(C);
   mm = cm.members[const Symbol("getE")];
@@ -47,5 +45,5 @@
   // what has to be returned.
   //Expect.equals("E", _n(mm.returnType.simpleName));
   Expect.equals(true, mm.owner is ClassMirror);
-  Expect.equals("C", _n(mm.owner.simpleName));
+  Expect.equals(const Symbol("C"), mm.owner.simpleName);
 }
diff --git a/tests/lib/typed_data/float32x4_list_test.dart b/tests/lib/typed_data/float32x4_list_test.dart
index 0de9492..8942186 100644
--- a/tests/lib/typed_data/float32x4_list_test.dart
+++ b/tests/lib/typed_data/float32x4_list_test.dart
@@ -108,6 +108,30 @@
   Expect.equals(7.0, array[1].w);
 }
 
+testSublist(array) {
+  Expect.equals(8, array.length);
+  Expect.isTrue(array is Float32x4List);
+  var a = array.sublist(0, 1);
+  Expect.equals(1, a.length);
+  Expect.equals(0.0, a[0].x);
+  Expect.equals(1.0, a[0].y);
+  Expect.equals(2.0, a[0].z);
+  Expect.equals(3.0, a[0].w);
+  a = array.sublist(1, 2);
+  Expect.equals(4.0, a[0].x);
+  Expect.equals(5.0, a[0].y);
+  Expect.equals(6.0, a[0].z);
+  Expect.equals(7.0, a[0].w);
+  a = array.sublist(0);
+  Expect.equals(a.length, array.length);
+  for (int i = 0; i < array.length; i++) {
+    Expect.equals(array[i].x, a[i].x);
+    Expect.equals(array[i].y, a[i].y);
+    Expect.equals(array[i].z, a[i].z);
+    Expect.equals(array[i].w, a[i].w);
+  }
+}
+
 main() {
   var list;
 
@@ -120,11 +144,14 @@
   for (int i = 0; i < floatList.length; i++) {
     floatList[i] = i.toDouble();
   }
-  list = new Float32x4List.view(floatList);
+  list = new Float32x4List.view(floatList.buffer);
   for (int i = 0; i < 20; i++) {
     testView(list);
   }
   for (int i = 0; i < 20; i++) {
+    testSublist(list);
+  }
+  for (int i = 0; i < 20; i++) {
     testLoadStore(list);
   }
   for (int i = 0; i < 20; i++) {
diff --git a/tests/lib/typed_data/float32x4_unbox_phi_test.dart b/tests/lib/typed_data/float32x4_unbox_phi_test.dart
index 34c85ef..2c2ee15 100644
--- a/tests/lib/typed_data/float32x4_unbox_phi_test.dart
+++ b/tests/lib/typed_data/float32x4_unbox_phi_test.dart
@@ -19,7 +19,7 @@
 
 main() {
   Float32x4List list = new Float32x4List(10);
-  Float32List floatList = new Float32List.view(list);
+  Float32List floatList = new Float32List.view(list.buffer);
   for (int i = 0; i < floatList.length; i++) {
     floatList[i] = i.toDouble();
   }
diff --git a/tests/standalone/io/raw_secure_server_socket_test.dart b/tests/standalone/io/raw_secure_server_socket_test.dart
index 5756e11..16babde 100644
--- a/tests/standalone/io/raw_secure_server_socket_test.dart
+++ b/tests/standalone/io/raw_secure_server_socket_test.dart
@@ -87,7 +87,8 @@
         Expect.fail("No client connection expected.");
       })
       .catchError((error) {
-        Expect.isTrue(error is HandshakeException);
+        Expect.isTrue(error is SocketException ||
+                      error is HandshakeException);
       });
     server.listen((serverEnd) {
       Expect.fail("No server connection expected.");
diff --git a/tests/standalone/io/secure_bad_certificate_client.dart b/tests/standalone/io/secure_bad_certificate_client.dart
new file mode 100644
index 0000000..440bca2
--- /dev/null
+++ b/tests/standalone/io/secure_bad_certificate_client.dart
@@ -0,0 +1,59 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// Client for secure_bad_certificate_test, that runs in a subprocess.
+// The test verifies that the client bad certificate callback works.
+
+import "dart:async";
+import "dart:io";
+
+class ExpectException implements Exception {
+  ExpectException(this.message);
+  String toString() => "ExpectException: $message";
+  String message;
+}
+
+void expect(condition) {
+  if (!condition) {
+    throw new ExpectException('');
+  }
+}
+
+const HOST_NAME = "localhost";
+
+void runClient(int port, result) {
+  bool badCertificateCallback(X509Certificate certificate) {
+    expect('CN=localhost' == certificate.subject);
+    expect('CN=myauthority' == certificate.issuer);
+    expect(result != 'exception');  // Throw exception if one is requested.
+    if (result == 'true') result = true;
+    if (result == 'false') result = false;
+    return result;
+  }
+
+  SecureSocket.connect(HOST_NAME,
+                       port,
+                       onBadCertificate: badCertificateCallback)
+      .then((SecureSocket socket) {
+        expect(result);
+        socket.close();
+      },
+      onError: (error) {
+        expect(result != true);
+        if (result == false) {
+          expect(error is HandshakeException);
+        } else if (result == 'exception') {
+          expect(error is ExpectException);
+        } else {
+          expect(error is ArgumentError);
+        }
+      });
+}
+
+
+void main() {
+  final args = new Options().arguments;
+  SecureSocket.initialize();
+  runClient(int.parse(args[0]), args[1]);
+}
diff --git a/tests/standalone/io/secure_bad_certificate_test.dart b/tests/standalone/io/secure_bad_certificate_test.dart
new file mode 100644
index 0000000..ccdbf32
--- /dev/null
+++ b/tests/standalone/io/secure_bad_certificate_test.dart
@@ -0,0 +1,63 @@
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// This test verifies that the bad certificate callback works.
+
+import "package:expect/expect.dart";
+import "package:path/path.dart";
+import "dart:async";
+import "dart:io";
+
+const HOST_NAME = "localhost";
+const CERTIFICATE = "localhost_cert";
+
+
+String certificateDatabase() =>
+    join(dirname(new Options().script), 'pkcert');
+
+Future<SecureServerSocket> runServer() {
+  SecureSocket.initialize(database: certificateDatabase(),
+                          password: 'dartdart');
+
+  return SecureServerSocket.bind(HOST_NAME, 0, CERTIFICATE)
+      .then((SecureServerSocket server) {
+    server.listen((SecureSocket socket) {
+      socket.listen((_) { },
+                    onDone: () {
+                      socket.close();
+                    });
+    }, onError: (e) => Expect.isTrue(e is HandshakeException));
+    return server;
+  });
+}
+
+
+void main() {
+  final options = new Options();
+  var clientScript = join(dirname(options.script),
+                          'secure_bad_certificate_client.dart');
+
+  Future clientProcess(int port, String acceptCertificate) {
+    return Process.run(options.executable,
+        [clientScript, port.toString(), acceptCertificate])
+        .then((ProcessResult result) {
+      if (result.exitCode != 0) {
+        print("Client failed, stdout:");
+        print(result.stdout);
+        print("  stderr:");
+        print(result.stderr);
+        Expect.fail('Client subprocess exit code: ${result.exitCode}');
+      }
+    });
+  }
+
+  runServer().then((server) {
+    Future.wait([clientProcess(server.port, 'true'),
+                 clientProcess(server.port, 'false'),
+                 clientProcess(server.port, 'fisk'),
+                 clientProcess(server.port, 'exception')]).then((_) {
+      server.close();
+    });
+  });
+}
diff --git a/tests/standalone/io/secure_no_builtin_roots_database_test.dart b/tests/standalone/io/secure_no_builtin_roots_database_test.dart
index 6c4c06a..7f1276b 100644
--- a/tests/standalone/io/secure_no_builtin_roots_database_test.dart
+++ b/tests/standalone/io/secure_no_builtin_roots_database_test.dart
@@ -9,16 +9,23 @@
 import "dart:async";
 
 void testGoogleUrl() {
-  ReceivePort keepAlive = new ReceivePort();
-  HttpClient client = new HttpClient();
-  client.getUrl(Uri.parse('https://www.google.com'))
-      .then((request) => request.close())
-      .then((response) => Expect.fail("Unexpected successful connection"))
-      .catchError((error) {
-        Expect.isTrue(error is HandshakeException);
-        keepAlive.close();
-        client.close();
-      });
+  // We need to use an external server here because it is backed by a
+  // built-in root certificate authority.
+  InternetAddress.lookup('www.google.com').then((_) {
+    ReceivePort keepAlive = new ReceivePort();
+    HttpClient client = new HttpClient();
+    client.getUrl(Uri.parse('https://www.google.com'))
+        .then((request) => request.close())
+        .then((response) => Expect.fail("Unexpected successful connection"))
+        .catchError((error) {
+          Expect.isTrue(error is HandshakeException);
+          keepAlive.close();
+          client.close();
+        });
+  },
+  onError: (e) {
+    Expect.isTrue(e is SocketException);
+  });
 }
 
 void InitializeSSL() {
diff --git a/tests/standalone/io/secure_no_builtin_roots_test.dart b/tests/standalone/io/secure_no_builtin_roots_test.dart
index 4bcdac8..49026b4 100644
--- a/tests/standalone/io/secure_no_builtin_roots_test.dart
+++ b/tests/standalone/io/secure_no_builtin_roots_test.dart
@@ -8,16 +8,23 @@
 import "dart:async";
 
 void testGoogleUrl() {
-  ReceivePort keepAlive = new ReceivePort();
-  HttpClient client = new HttpClient();
-  client.getUrl(Uri.parse('https://www.google.com'))
-      .then((request) => request.close())
-      .then((response) => Expect.fail("Unexpected successful connection"))
-      .catchError((error) {
-        Expect.isTrue(error is HandshakeException);
-        keepAlive.close();
-        client.close();
-      });
+  // We need to use an external server here because it is backed by a
+  // built-in root certificate authority.
+  InternetAddress.lookup('www.google.com').then((_) {
+    ReceivePort keepAlive = new ReceivePort();
+    HttpClient client = new HttpClient();
+    client.getUrl(Uri.parse('https://www.google.com'))
+        .then((request) => request.close())
+        .then((response) => Expect.fail("Unexpected successful connection"))
+        .catchError((error) {
+          Expect.isTrue(error is HandshakeException);
+          keepAlive.close();
+          client.close();
+        });
+  },
+  onError: (e) {
+    Expect.isTrue(e is SocketException);
+  });
 }
 
 void InitializeSSL() {
diff --git a/tests/standalone/io/secure_server_socket_test.dart b/tests/standalone/io/secure_server_socket_test.dart
index f7892e6..fe838aa 100644
--- a/tests/standalone/io/secure_server_socket_test.dart
+++ b/tests/standalone/io/secure_server_socket_test.dart
@@ -84,7 +84,8 @@
         Expect.fail("No client connection expected.");
       })
       .catchError((error) {
-        Expect.isTrue(error is HandshakeException);
+        Expect.isTrue(error is HandshakeException ||
+                      error is SocketException);
       });
     server.listen((serverEnd) {
       Expect.fail("No server connection expected.");
diff --git a/tests/standalone/io/secure_socket_bad_certificate_test.dart b/tests/standalone/io/secure_socket_bad_certificate_test.dart
deleted file mode 100644
index 5edba31..0000000
--- a/tests/standalone/io/secure_socket_bad_certificate_test.dart
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-//
-// VMOptions=
-// VMOptions=--short_socket_read
-// VMOptions=--short_socket_write
-// VMOptions=--short_socket_write --short_socket_read
-// The --short_socket_write option does not work with external server
-// www.google.dk.  Add this to the test when we have secure server sockets.
-// See TODO below.
-
-import "package:expect/expect.dart";
-import "dart:async";
-import "dart:isolate";
-import "dart:io";
-
-void main() {
-  ReceivePort keepAlive = new ReceivePort();
-  SecureSocket.initialize(useBuiltinRoots: false);
-  testCertificateCallback(host: "www.google.com",
-                          acceptCertificate: false)
-    .then((_) =>
-      testCertificateCallback(host: "www.google.com",
-                              acceptCertificate: true))
-    .then((_) =>
-      keepAlive.close());
-}
-
-Future testCertificateCallback({String host, bool acceptCertificate}) {
-  var x = 7;
-  Expect.throws(() => SecureSocket.connect(host, 443, onBadCertificate: x),
-                (e) => e is ArgumentError || e is TypeError);
-
-  bool badCertificateCallback(X509Certificate certificate) {
-    Expect.isTrue(certificate.subject.contains("O=Google Inc"));
-    Expect.isTrue(certificate.startValidity.isBefore(new DateTime.now()));
-    Expect.isTrue(certificate.endValidity.isAfter(new DateTime.now()));
-    return acceptCertificate;
-  };
-
-  return SecureSocket.connect(host,
-                              443,
-                              onBadCertificate: badCertificateCallback)
-      .then((socket) {
-        Expect.isTrue(acceptCertificate);
-        socket.write("GET / HTTP/1.0\r\nHost: $host\r\n\r\n");
-        socket.close();
-        return socket.fold(<int>[], (message, data)  => message..addAll(data))
-            .then((message) {
-              String received = new String.fromCharCodes(message);
-              Expect.isTrue(received.startsWith('HTTP/1.0 '));
-            });
-      }).catchError((e) {
-        if (e is HandshakeException) {
-          Expect.isFalse(acceptCertificate);
-        } else {
-          Expect.isTrue(e is SocketException);
-        }
-      });
-}
diff --git a/tests/standalone/io/secure_socket_renegotiate_test.dart b/tests/standalone/io/secure_socket_renegotiate_test.dart
index 8e772e5..b938244 100644
--- a/tests/standalone/io/secure_socket_renegotiate_test.dart
+++ b/tests/standalone/io/secure_socket_renegotiate_test.dart
@@ -7,7 +7,7 @@
 // can request a client certificate to be sent.
 
 import "package:expect/expect.dart";
-import "package:path/path.dart" as path;
+import "package:path/path.dart";
 import "dart:async";
 import "dart:io";
 
@@ -15,13 +15,12 @@
 const CERTIFICATE = "localhost_cert";
 
 
-String certificateDatabase() =>
-    path.join(path.dirname(new Options().script), 'pkcert', '');
+String certificateDatabase() => join(dirname(new Options().script), 'pkcert');
 
 
 Future<SecureServerSocket> runServer() {
   SecureSocket.initialize(database: certificateDatabase(),
-      password: 'dartdart');
+                          password: 'dartdart');
 
   return SecureServerSocket.bind(HOST_NAME, 0, CERTIFICATE)
     .then((SecureServerSocket server) {
diff --git a/tests/standalone/oom_error_stacktrace_test.dart b/tests/standalone/oom_error_stacktrace_test.dart
new file mode 100644
index 0000000..dc20fc9
--- /dev/null
+++ b/tests/standalone/oom_error_stacktrace_test.dart
@@ -0,0 +1,44 @@
+// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+// Dart test program for testing throw statement
+
+import "package:expect/expect.dart";
+
+class Helper1 {
+  static int func1() {
+    return func2();
+  }
+  static int func2() {
+    return func3();
+  }
+  static int func3() {
+    return func4();
+  }
+  static int func4() {
+    var i = 0;
+    try {
+      i = 10;
+      func5();
+    } on OutOfMemoryError catch (e) {
+      i = 100;
+      Expect.isNull(e.stackTrace, "OOM should not have a stackTrace on throw");
+    }
+    return i;
+  }
+  static List func5() {
+    // Cause an OOM(out of memory) exception.
+    var l1 = new List(268435455);
+    return l1;
+  }
+}
+
+class OOMErrorStackTraceTest {
+  static testMain() {
+    Expect.equals(100, Helper1.func1());
+  }
+}
+
+main() {
+  OOMErrorStackTraceTest.testMain();
+}
diff --git a/tests/standalone/standalone.status b/tests/standalone/standalone.status
index 840d7f0..784c209 100644
--- a/tests/standalone/standalone.status
+++ b/tests/standalone/standalone.status
@@ -49,6 +49,8 @@
 coverage_test: Skip
 http_launch_test: Skip
 vmservice/*: Skip # Do not run standalone vm service tests in browser.
+53bit_overflow_literal_test: fail # Issue 12044
+53bit_overflow_test: fail # Issue 12044
 
 
 [ $compiler == dartanalyzer ]
@@ -114,6 +116,7 @@
 http_launch_test: Skip
 53bit_overflow_test: Skip
 53bit_overflow_literal_test: Skip
+oom_error_stacktrace_test: Fail, OK # (OOM on JS may produce a stacktrace).
 vmservice/*: Skip # Do not run standalone vm service tests with dart2js.
 
 
@@ -144,6 +147,7 @@
 
 [ $arch == simarm ]
 out_of_memory_test: Skip # passes on Mac, crashes on Linux
+oom_error_stacktrace_test: Skip # Fails on Linux
 io/web_socket_ping_test: Skip # TODO(ajohnsen): Timeout issue
 
 [ $arch == mips ]
@@ -152,4 +156,5 @@
 [ $arch == simmips ]
 io/file_fuzz_test: Pass, Timeout
 out_of_memory_test: Skip # passes on Mac, crashes on Linux
+oom_error_stacktrace_test: Skip # Fails on Linux
 io/web_socket_ping_test: Skip # TODO(ajohnsen): Timeout issue
diff --git a/tools/VERSION b/tools/VERSION
index ac61405..ce86438 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -1,4 +1,4 @@
 MAJOR 0
 MINOR 6
-BUILD 11
+BUILD 12
 PATCH 0
diff --git a/tools/dom/dom.json b/tools/dom/dom.json
index 946b502..5d067fe 100644
--- a/tools/dom/dom.json
+++ b/tools/dom/dom.json
@@ -10373,6 +10373,9 @@
       "_createObjectUrlFromWebKitMediaSource": {
         "support_level": "untriaged"
       },
+      "_createObjectUrlFromWebKitSource": {
+        "support_level": "untriaged"
+      },
       "createObjectURL": {},
       "createObjectUrlFromBlob": {},
       "createObjectUrlFromSource": {},
diff --git a/tools/dom/templates/html/impl/impl_Element.darttemplate b/tools/dom/templates/html/impl/impl_Element.darttemplate
index c64cb74..29533d8 100644
--- a/tools/dom/templates/html/impl/impl_Element.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Element.darttemplate
@@ -1090,6 +1090,46 @@
    */
   @Experimental()
   CssRect get marginEdge => new _MarginCssRect(this);
+
+  /** 
+   * Provides the coordinates of the element relative to the top of the 
+   * document. 
+   *
+   * This method is the Dart equivalent to jQuery's 
+   * [offset](http://api.jquery.com/offset/) method.
+   */
+  Point get documentOffset => offsetTo(document.documentElement);
+
+  /** 
+   * Provides the offset of this element's [borderEdge] relative to the
+   * specified [parent].
+   * 
+   * This is the Dart equivalent of jQuery's
+   * [position](http://api.jquery.com/position/) method. Unlike jQuery's
+   * position, however, [parent] can be any parent element of `this`, 
+   * rather than only `this`'s immediate [offsetParent]. If the specified
+   * element is _not_ an offset parent or transitive offset parent to this
+   * element, an [ArgumentError] is thrown.
+   */
+  Point offsetTo(Element parent) {
+    return Element._offsetToHelper(this, parent);
+  }
+
+  static Point _offsetToHelper(Element current, Element parent) {
+    // We're hopping from _offsetParent_ to offsetParent (not just parent), so
+    // offsetParent, "tops out" at BODY. But people could conceivably pass in 
+    // the document.documentElement and I want it to return an absolute offset,
+    // so we have the special case checking for HTML.
+    bool foundAsParent = identical(current, parent) || parent.tagName == 'HTML';
+    if (current == null || identical(current, parent)) {
+      if (foundAsParent) return new Point(0, 0);
+      throw new ArgumentError("Specified element is not a transitive offset "
+          "parent of this element.");
+    } 
+    Element parentOffset = current.offsetParent;
+    Point p = Element._offsetToHelper(parentOffset, parent);
+    return new Point(p.x + current.offsetLeft, p.y + current.offsetTop);
+  }
 $!MEMBERS
 }
 
diff --git a/tools/dom/templates/html/impl/impl_Node.darttemplate b/tools/dom/templates/html/impl/impl_Node.darttemplate
index 543cf1b..1eb46fa 100644
--- a/tools/dom/templates/html/impl/impl_Node.darttemplate
+++ b/tools/dom/templates/html/impl/impl_Node.darttemplate
@@ -199,6 +199,7 @@
   TemplateInstance(this.firstNode, this.lastNode, this.model);
 }
 
+
 $(ANNOTATIONS)$(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS$NATIVESPEC {
   List<Node> get nodes {
     return new _ChildNodeListLazy(this);
@@ -271,14 +272,28 @@
    */
   String toString() => nodeValue == null ? super.toString() : nodeValue;
 
+
+  /**
+   * Creates a binding to the attribute [name] to the [path] of the [model].
+   *
+   * This can be overridden by custom elements to provide the binding used in
+   * [Node.bind]. This will only create the binding; it will not add it to
+   * [bindings].
+   *
+   * You should not need to call this directly except from [Node.bind].
+   */
+  @Experimental()
+  createBinding(String name, model, String path) =>
+      TemplateElement.mdvPackage(this).createBinding(name, model, path);
+
   /**
    * Binds the attribute [name] to the [path] of the [model].
    * Path is a String of accessors such as `foo.bar.baz`.
+   * Returns the `NodeBinding` instance.
    */
   @Experimental()
-  void bind(String name, model, String path) {
-    TemplateElement.mdvPackage(this).bind(name, model, path);
-  }
+  bind(String name, model, String path) =>
+      TemplateElement.mdvPackage(this).bind(name, model, path);
 
   /** Unbinds the attribute [name]. */
   @Experimental()
@@ -292,6 +307,11 @@
     TemplateElement.mdvPackage(this).unbindAll();
   }
 
+  /** Gets the data bindings that are associated with this node. */
+  @Experimental()
+  Map<String, dynamic> get bindings =>
+      TemplateElement.mdvPackage(this).bindings;
+
   /** Gets the template instance that instantiated this node, if any. */
   @Experimental()
   TemplateInstance get templateInstance =>