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 =>