Bug 907077: Adapt addon SDK custom iterators to work with new for-of behavior r=rFobic
authorAndy Wingo <wingo@igalia.com>
Thu, 03 Oct 2013 13:37:57 +0100
changeset 149772 1065b60241f7a5c2c09d8bf2a3994a7de213b275
parent 149771 a4f96de49668a86063da67a8de77e4eb23e49b0d
child 149773 cfd77a083d5ff69f6c18d9fa7f9910615d255f2f
push id34669
push userjcoppeard@mozilla.com
push dateThu, 03 Oct 2013 12:39:28 +0000
treeherdermozilla-inbound@1065b60241f7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrFobic
bugs907077
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 907077: Adapt addon SDK custom iterators to work with new for-of behavior r=rFobic
addon-sdk/source/lib/sdk/deprecated/list.js
addon-sdk/source/lib/sdk/selection.js
addon-sdk/source/lib/sdk/util/iteration.js
addon-sdk/source/lib/sdk/util/list.js
--- a/addon-sdk/source/lib/sdk/deprecated/list.js
+++ b/addon-sdk/source/lib/sdk/deprecated/list.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Trait } = require('../deprecated/traits');
+const { iteratorSymbol } = require('../util/iteration');
 
 /**
  * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list
  */
 const Iterable = Trait.compose({
   /**
    * Hash map of key-values to iterate over.
    * Note: That this property can be a getter if you need dynamic behavior.
@@ -33,17 +34,17 @@ exports.Iterable = Iterable;
 
 /**
  * An ordered collection (also known as a sequence) disallowing duplicate
  * elements. List is composed out of `Iterable` there for it provides custom
  * enumeration behavior that is similar to array (enumerates only on the
  * elements of the list). List is a base trait and is meant to be a part of
  * composition, since all of it's API is private except length property.
  */
-const List = Trait.resolve({ toString: null }).compose({
+const listOptions = {
   _keyValueMap: null,
   /**
    * List constructor can take any number of element to populate itself.
    * @params {Object|String|Number} element
    * @example
    *    List(1,2,3).length == 3 // true
    */
   constructor: function List() {
@@ -109,17 +110,17 @@ const List = Trait.resolve({ toString: n
    * @param {Boolean} onKeys
    */
   __iterator__: function __iterator__(onKeys, onKeyValue) {
     let array = this._keyValueMap.slice(0),
         i = -1;
     for (let element of array)
       yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
   },
-  iterator: function iterator() {
-    let array = this._keyValueMap.slice(0);
+};
+listOptions[iteratorSymbol] = function* iterator() {
+  let array = this._keyValueMap.slice(0);
 
-    for (let element of array)
-      yield element;
-  }
-
-});
+  for (let element of array)
+    yield element;
+}
+const List = Trait.resolve({ toString: null }).compose(listOptions);
 exports.List = List;
--- a/addon-sdk/source/lib/sdk/selection.js
+++ b/addon-sdk/source/lib/sdk/selection.js
@@ -17,17 +17,18 @@ const { Ci, Cc } = require("chrome"),
     { Class, obscure } = require("./core/heritage"),
     { EventTarget } = require("./event/target"),
     { ns } = require("./core/namespace"),
     { when: unload } = require("./system/unload"),
     { ignoreWindow } = require('./private-browsing/utils'),
     { getTabs, getTabContentWindow, getTabForContentWindow,
       getAllTabContentWindows } = require('./tabs/utils'),
     winUtils = require("./window/utils"),
-    events = require("./system/events");
+    events = require("./system/events"),
+    { iteratorSymbol, forInIterator } = require("./util/iteration");
 
 // The selection types
 const HTML = 0x01,
       TEXT = 0x02,
       DOM  = 0x03; // internal use only
 
 // A more developer-friendly message than the caught exception when is not
 // possible change a selection.
@@ -94,35 +95,36 @@ const selectionListener = {
 /**
  * Defines iterators so that discontiguous selections can be iterated.
  * Empty selections are skipped - see `safeGetRange` for further details.
  *
  * If discontiguous selections are in a text field, only the first one
  * is returned because the text field selection APIs doesn't support
  * multiple selections.
  */
-function iterator() {
-    let selection = getSelection(DOM);
-    let count = 0;
+function* forOfIterator() {
+  let selection = getSelection(DOM);
+  let count = 0;
 
-    if (selection)
-      count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
+  if (selection)
+    count = selection.rangeCount || (getElementWithSelection() ? 1 : 0);
 
-    for (let i = 0; i < count; i++) {
-      let sel = Selection(i);
+  for (let i = 0; i < count; i++) {
+    let sel = Selection(i);
 
-      if (sel.text)
-        yield Selection(i);
-    }
+    if (sel.text)
+      yield Selection(i);
+  }
 }
 
-const selectionIterator = obscure({
-  __iterator__: iterator, // for...in; for each...in
-  iterator: iterator // for....of
-});
+const selectionIteratorOptions = {
+  __iterator__: forInIterator
+}
+selectionIteratorOptions[iteratorSymbol] = forOfIterator;
+const selectionIterator = obscure(selectionIteratorOptions);
 
 /**
  * Returns the most recent focused window.
  * if private browsing window is most recent and not supported,
  * then ignore it and return `null`, because the focused window
  * can't be targeted.
  */
 function getFocusedWindow() {
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/util/iteration.js
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+'use strict';
+
+module.metadata = {
+  "stability": "experimental"
+};
+
+// This is known as @@iterator in the ES6 spec.  Until it is bound to
+// some well-known name, find the @@iterator object by expecting it as
+// the first property accessed on a for-of iterable.
+const iteratorSymbol = (function() {
+  try {
+    for (var _ of Proxy.create({get: function(_, name) { throw name; } }))
+      break;
+  } catch (name) {
+    return name;
+  }
+  throw new TypeError;
+})();
+
+exports.iteratorSymbol = iteratorSymbol;
+
+// An adaptor that, given an object that is iterable with for-of, is
+// suitable for being bound to __iterator__ in order to make the object
+// iterable in the same way via for-in.
+function forInIterator() {
+    for (let item of this)
+        yield item;
+}
+
+exports.forInIterator = forInIterator;
--- a/addon-sdk/source/lib/sdk/util/list.js
+++ b/addon-sdk/source/lib/sdk/util/list.js
@@ -4,18 +4,19 @@
 'use strict';
 
 module.metadata = {
   "stability": "experimental"
 };
 
 const { Class } = require('../core/heritage');
 const listNS = require('../core/namespace').ns();
+const { iteratorSymbol } = require('../util/iteration');
 
-const List = Class({
+const listOptions = {
   /**
    * List constructor can take any number of element to populate itself.
    * @params {Object|String|Number} element
    * @example
    *    List(1,2,3).length == 3 // true
    */
   initialize: function List() {
     listNS(this).keyValueMap = [];
@@ -41,24 +42,21 @@ const List = Class({
    * @param {Boolean} onKeys
    */
   __iterator__: function __iterator__(onKeys, onKeyValue) {
     let array = listNS(this).keyValueMap.slice(0),
                 i = -1;
     for each(let element in array)
       yield onKeyValue ? [++i, element] : onKeys ? ++i : element;
   },
-  iterator: function iterator() {
-    let array = listNS(this).keyValueMap.slice(0),
-                i = -1;
-
-    for (let element of array)
-      yield element;
-  }
-});
+};
+listOptions[iteratorSymbol] = function iterator() {
+    return listNS(this).keyValueMap.slice(0)[iteratorSymbol]();
+};
+const List = Class(listOptions);
 exports.List = List;
 
 function addListItem(that, value) {
   let list = listNS(that).keyValueMap,
       index = list.indexOf(value);
 
   if (-1 === index) {
     try {