Bug 919948 - Convert Array.prototype.@@iterator to use new iteration protocol. r=jorendorff
authorAndy Wingo <wingo@igalia.com>
Thu, 17 Oct 2013 12:10:02 +0200
changeset 151086 ceb4bd44eb3497c808cdc552be7e2acfafe81a15
parent 151085 45d9e6cd34736aadeac40925170bca7e9b42b3bd
child 151087 60f90ee1eb33c28e3f62f8616619971191668e7e
push id35067
push usertschneidereit@gmail.com
push dateThu, 17 Oct 2013 10:13:28 +0000
treeherdermozilla-inbound@ceb4bd44eb34 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs919948
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 919948 - Convert Array.prototype.@@iterator to use new iteration protocol. r=jorendorff
dom/bindings/Codegen.py
js/src/builtin/Array.js
js/src/builtin/Iterator.js
js/src/jit-test/tests/basic/spread-array.js
js/src/jit-test/tests/basic/spread-call-eval.js
js/src/jit-test/tests/basic/spread-call-funapply.js
js/src/jit-test/tests/basic/spread-call-length.js
js/src/jit-test/tests/basic/spread-call.js
js/src/jit-test/tests/collections/iterator-1.js
js/src/jit-test/tests/for-of/array-iterator-changing.js
js/src/jit-test/tests/for-of/array-iterator-gc.js
js/src/jit-test/tests/for-of/array-iterator-generic.js
js/src/jit-test/tests/for-of/array-iterator-growing-1.js
js/src/jit-test/tests/for-of/array-iterator-null.js
js/src/jit-test/tests/for-of/array-iterator-proxy.js
js/src/jit-test/tests/for-of/array-iterator-shrinking.js
js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js
js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js
js/src/jit-test/tests/for-of/next-2.js
js/src/jit-test/tests/for-of/next-3.js
js/src/jit-test/tests/for-of/semantics-06.js
js/src/jit-test/tests/for-of/string-iterator-generic.js
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsiter.cpp
js/src/jsiter.h
js/src/jsstr.cpp
js/src/vm/GlobalObject.h
js/src/vm/SelfHosting.cpp
js/src/vm/TypedArrayObject.cpp
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1511,17 +1511,17 @@ class MethodDefiner(PropertyDefiner):
                 self.chrome.append(method)
             else:
                 self.regular.append(method)
 
         # FIXME Check for an existing iterator on the interface first.
         if any(m.isGetter() and m.isIndexed() for m in methods):
             self.regular.append({"name": "@@iterator",
                                  "methodInfo": False,
-                                 "selfHostedName": "ArrayIterator",
+                                 "selfHostedName": "ArrayValues",
                                  "length": 0,
                                  "flags": "JSPROP_ENUMERATE",
                                  "condition": MemberCondition(None, None) })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
             if stringifier:
                 toStringDesc = { "name": "toString",
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -462,16 +462,83 @@ function ArrayFindIndex(predicate/*, thi
                 return k;
         }
     }
 
     /* Step 10. */
     return -1;
 }
 
+#define ARRAY_ITERATOR_SLOT_ITERATED_OBJECT 0
+#define ARRAY_ITERATOR_SLOT_NEXT_INDEX 1
+#define ARRAY_ITERATOR_SLOT_ITEM_KIND 2
+
+#define ITEM_KIND_VALUE 0
+#define ITEM_KIND_KEY_AND_VALUE 1
+#define ITEM_KIND_KEY 2
+
+// ES6 draft specification, section 22.1.5.1, version 2013-09-05.
+function CreateArrayIterator(obj, kind) {
+    var iteratedObject = ToObject(obj);
+    var iterator = NewArrayIterator();
+    UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT, iteratedObject);
+    UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_NEXT_INDEX, 0);
+    UnsafeSetReservedSlot(iterator, ARRAY_ITERATOR_SLOT_ITEM_KIND, kind);
+    return iterator;
+}
+
+function ArrayIteratorIdentity() {
+    return this;
+}
+
+function ArrayIteratorNext() {
+    // FIXME: ArrayIterator prototype should not pass this test.  Bug 924059.
+    if (!IsObject(this) || !IsArrayIterator(this))
+        ThrowError(JSMSG_INCOMPATIBLE_METHOD, "ArrayIterator", "next", ToString(this));
+
+    var a = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_ITERATED_OBJECT);
+    var index = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX);
+    var itemKind = UnsafeGetReservedSlot(this, ARRAY_ITERATOR_SLOT_ITEM_KIND);
+
+    // FIXME: This should be ToLength, which clamps at 2**53.  Bug 924058.
+    if (index >= TO_UINT32(a.length)) {
+        // When the above is changed to ToLength, use +1/0 here instead
+        // of MAX_UINT32.
+        UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, 0xffffffff);
+        return { value: undefined, done: true };
+    }
+
+    UnsafeSetReservedSlot(this, ARRAY_ITERATOR_SLOT_NEXT_INDEX, index + 1);
+
+    if (itemKind === ITEM_KIND_VALUE)
+        return { value: a[index], done: false };
+
+    if (itemKind === ITEM_KIND_KEY_AND_VALUE) {
+        var pair = NewDenseArray(2);
+        pair[0] = index;
+        pair[1] = a[index];
+        return { value: pair, done : false };
+    }
+
+    assert(itemKind === ITEM_KIND_KEY, itemKind);
+    return { value: index, done: false };
+}
+
+function ArrayValues() {
+    return CreateArrayIterator(this, ITEM_KIND_VALUE);
+}
+
+function ArrayEntries() {
+    return CreateArrayIterator(this, ITEM_KIND_KEY_AND_VALUE);
+}
+
+function ArrayKeys() {
+    return CreateArrayIterator(this, ITEM_KIND_KEY);
+}
+
 #ifdef ENABLE_PARALLEL_JS
 
 /*
  * Strawman spec:
  *   http://wiki.ecmascript.org/doku.php?id=strawman:data_parallelism
  */
 
 /* The mode asserts options object. */
--- a/js/src/builtin/Iterator.js
+++ b/js/src/builtin/Iterator.js
@@ -79,12 +79,8 @@ function NewLegacyIterator(iter, wrapper
 
 function LegacyIteratorShim() {
     return NewLegacyIterator(ToObject(this), LegacyIterator);
 }
 
 function LegacyGeneratorIteratorShim() {
     return NewLegacyIterator(ToObject(this), LegacyGeneratorIterator);
 }
-
-function ArrayIterator() {
-    return NewLegacyIterator(callFunction(std_Array_iterator, this), LegacyIterator);
-}
--- a/js/src/jit-test/tests/basic/spread-array.js
+++ b/js/src/jit-test/tests/basic/spread-array.js
@@ -12,17 +12,17 @@ assertEqArray([1,, ...[2],, 3,, 4,], [1,
 assertEqArray([...[1, 2, 3],,,,], [1, 2, 3,,,,]);
 assertEqArray([,,...[1, 2, 3],,,,], [,,1,2,3,,,,]);
 
 assertEqArray([...[undefined]], [undefined]);
 
 // other iterable objects
 assertEqArray([...Int32Array([1, 2, 3])], [1, 2, 3]);
 assertEqArray([..."abc"], ["a", "b", "c"]);
-assertEqArray([...[1, 2, 3].iterator()], [1, 2, 3]);
+assertEqArray([...[1, 2, 3][std_iterator]()], [1, 2, 3]);
 assertEqArray([...Set([1, 2, 3])], [1, 2, 3]);
 assertEqArray([...Map([["a", "A"], ["b", "B"], ["c", "C"]])].map(([k, v]) => k + v), ["aA", "bB", "cC"]);
 let itr = {};
 itr[std_iterator] = function () {
     return {
         i: 1,
         next: function() {
             if (this.i < 4)
--- a/js/src/jit-test/tests/basic/spread-call-eval.js
+++ b/js/src/jit-test/tests/basic/spread-call-eval.js
@@ -19,17 +19,17 @@ with ({ a: 30 }) {
 let line0 = Error().lineNumber;
 try {             // line0 + 1
   eval(...["("]); // line0 + 2
 } catch (e) {
   assertEq(e.lineNumber, line0 + 2);
 }
 
 // other iterable objects
-assertEq(eval(...["a + b"].iterator()), 11);
+assertEq(eval(...["a + b"][std_iterator]()), 11);
 assertEq(eval(...Set(["a + b"])), 11);
 let itr = {};
 itr[std_iterator] = function() {
     return {
         i: 0,
         next: function() {
             this.i++;
             if (this.i == 1)
--- a/js/src/jit-test/tests/basic/spread-call-funapply.js
+++ b/js/src/jit-test/tests/basic/spread-call-funapply.js
@@ -5,17 +5,17 @@ load(libdir + "iteration.js");
 function checkCommon(f) {
   assertEqArray(f.apply(null, ...[[1, 2, 3]]), [1, 2, 3]);
   assertEqArray(f.apply(...[null], [1, 2, 3]), [1, 2, 3]);
   assertEqArray(f.apply(...[null], ...[[1, 2, 3]]), [1, 2, 3]);
   assertEqArray(f.apply(...[null, [1, 2, 3]]), [1, 2, 3]);
 
   // other iterable objects
   assertEqArray(f.apply(...Set([null, [1, 2, 3]])), [1, 2, 3]);
-  assertEqArray(f.apply(...[null, [1, 2, 3]].iterator()), [1, 2, 3]);
+  assertEqArray(f.apply(...[null, [1, 2, 3]][std_iterator]()), [1, 2, 3]);
   let itr = {};
   itr[std_iterator] = function() {
       return {
           i: 0,
           next: function() {
               this.i++;
               if (this.i == 1)
                   return { value: null, done: false };
--- a/js/src/jit-test/tests/basic/spread-call-length.js
+++ b/js/src/jit-test/tests/basic/spread-call-length.js
@@ -16,17 +16,17 @@ function checkLength(f, makeFn) {
   assertEq(makeFn("...[1, 2, 3, 4]")(f), 4);
   assertEq(makeFn("1, ...[2, 3, 4], 5")(f), 5);
 
   assertEq(makeFn("...[undefined]")(f), 1);
 
   // other iterable objects
   assertEq(makeFn("...arg")(f, Int32Array([1, 2, 3])), 3);
   assertEq(makeFn("...arg")(f, "abc"), 3);
-  assertEq(makeFn("...arg")(f, [1, 2, 3].iterator()), 3);
+  assertEq(makeFn("...arg")(f, [1, 2, 3][std_iterator]()), 3);
   assertEq(makeFn("...arg")(f, Set([1, 2, 3])), 3);
   assertEq(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])), 3);
   let itr = {};
   itr[std_iterator] = function() {
       return {
           i: 1,
           next: function() {
               if (this.i < 4)
--- a/js/src/jit-test/tests/basic/spread-call.js
+++ b/js/src/jit-test/tests/basic/spread-call.js
@@ -11,17 +11,17 @@ function checkCommon(f, makeFn) {
   assertEqArray(makeFn("1, ...[2], 3")(f), [1, 2, 3]);
   assertEqArray(makeFn("1, ...[2], ...[3]")(f), [1, 2, 3]);
   assertEqArray(makeFn("1, ...[2, 3]")(f), [1, 2, 3]);
   assertEqArray(makeFn("1, ...[], 2, 3")(f), [1, 2, 3]);
 
   // other iterable objects
   assertEqArray(makeFn("...arg")(f, Int32Array([1, 2, 3])), [1, 2, 3]);
   assertEqArray(makeFn("...arg")(f, "abc"), ["a", "b", "c"]);
-  assertEqArray(makeFn("...arg")(f, [1, 2, 3].iterator()), [1, 2, 3]);
+  assertEqArray(makeFn("...arg")(f, [1, 2, 3][std_iterator]()), [1, 2, 3]);
   assertEqArray(makeFn("...arg")(f, Set([1, 2, 3])), [1, 2, 3]);
   assertEqArray(makeFn("...arg")(f, Map([["a", "A"], ["b", "B"], ["c", "C"]])).map(([k, v]) => k + v), ["aA", "bB", "cC"]);
   let itr = {};
   itr[std_iterator] = function() {
       return {
           i: 1,
           next: function() {
               if (this.i < 4)
--- a/js/src/jit-test/tests/collections/iterator-1.js
+++ b/js/src/jit-test/tests/collections/iterator-1.js
@@ -4,13 +4,11 @@ load(libdir + "iteration.js");
 
 function test(obj, name) {
     var iter = obj[std_iterator]();
     assertEq(typeof iter, "object");
     assertEq(iter instanceof Iterator, true);
     assertEq(iter.toString(), "[object " + obj.constructor.name + " Iterator]");
 }
 
-// FIXME: Until arrays are converted to use the new iteration protocol,
-// toString on this iterator doesn't work.  Bug 919948.
-// test([]);
+test([]);
 test(new Map);
 test(new Set);
--- a/js/src/jit-test/tests/for-of/array-iterator-changing.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-changing.js
@@ -1,11 +1,13 @@
 // Array iterators reflect changes to elements of the underlying array.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
 var arr = [0, 1, 2];
-var it = arr.iterator();
+var it = arr[std_iterator]();
 arr[0] = 1000;
 arr[2] = 2000;
-assertEq(it.next(), 1000);
-assertEq(it.next(), 1);
-assertEq(it.next(), 2000);
-assertThrowsValue(function () { it.next(); }, StopIteration);
+assertIteratorResult(it.next(), 1000, false);
+assertIteratorResult(it.next(), 1, false);
+assertIteratorResult(it.next(), 2000, false);
+assertIteratorResult(it.next(), undefined, true);
--- a/js/src/jit-test/tests/for-of/array-iterator-gc.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-gc.js
@@ -1,12 +1,13 @@
 // Array iterators keep the underlying array, arraylike object, or string alive.
 
 load(libdir + "referencesVia.js");
+load(libdir + "iteration.js");
 
 function test(obj) {
-    var it = Array.prototype.iterator.call(obj);
+    var it = Array.prototype[std_iterator].call(obj);
     assertEq(referencesVia(it, "**UNKNOWN SLOT 0**", obj), true);
 }
 
 test([]);
 test([1, 2, 3, 4]);
 test({});
--- a/js/src/jit-test/tests/for-of/array-iterator-generic.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-generic.js
@@ -1,18 +1,19 @@
 // Array.prototype.iterator is generic.
 // That is, it can be applied to arraylike objects and strings, not just arrays.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 function test(obj) {
-    var it = Array.prototype.iterator.call(obj);
+    var it = Array.prototype[std_iterator].call(obj);
     for (var i = 0; i < (obj.length >>> 0); i++)
-        assertEq(it.next(), obj[i]);
-    assertThrowsValue(function () { it.next(); }, StopIteration);
+        assertIteratorResult(it.next(), obj[i], false);
+    assertIteratorResult(it.next(), undefined, true);
 }
 
 test({length: 0});
 test({length: 0, 0: 'x', 1: 'y'});
 test({length: 2, 0: 'x', 1: 'y'});
 test(Object.create(['x', 'y', 'z']));
 test(Object.create({length: 2, 0: 'x', 1: 'y'}));
 test("");
--- a/js/src/jit-test/tests/for-of/array-iterator-growing-1.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-growing-1.js
@@ -1,12 +1,14 @@
 // If an array with an active iterator is lengthened, the iterator visits the new elements.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
 var arr = [0, 1];
-var it = arr.iterator();
-it.next();
-it.next();
+var it = arr[std_iterator]();
+assertIteratorResult(it.next(), 0, false);
+assertIteratorResult(it.next(), 1, false);
 arr[2] = 2;
 arr.length = 4;
-assertEq(it.next(), 2);
-assertEq(it.next(), undefined);
-assertThrowsValue(function () { it.next(); }, StopIteration);
+assertIteratorResult(it.next(), 2, false);
+assertIteratorResult(it.next(), undefined, false);
+assertIteratorResult(it.next(), undefined, true);
--- a/js/src/jit-test/tests/for-of/array-iterator-null.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-null.js
@@ -1,9 +1,9 @@
-// Array.prototype.iterator applied to undefined or null does not throw (until .next is called).
+// Array.prototype.iterator applied to undefined or null throws directly.
 
 load(libdir + "asserts.js");
-for (var v of [undefined, null]) {
-    var it = Array.prototype.iterator.call(v);
+load(libdir + "iteration.js");
 
-    // This will throw because the iterator is trying to get v.length.
-    assertThrowsInstanceOf(function () { it.next(); }, TypeError);
+for (var v of [undefined, null]) {
+    // ES6 draft 2013-09-05 section 22.1.5.1.
+    assertThrowsInstanceOf(function () { Array.prototype[std_iterator].call(v); }, TypeError);
 }
--- a/js/src/jit-test/tests/for-of/array-iterator-proxy.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-proxy.js
@@ -1,23 +1,24 @@
 // An array iterator for a proxy calls the traps in a predictable order.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 var s = '';
-var it = Array.prototype.iterator.call(Proxy.create({
+var it = Array.prototype[std_iterator].call(Proxy.create({
     get: function (recipient, name) {
         if (name == 'length') {
             s += 'L';
             return 2;
         } else {
             s += name;
             return name;
         }
     }
 }));
 
-assertEq(it.next(), "0");
+assertIteratorResult(it.next(), "0", false);
 s += ' ';
-assertEq(it.next(), "1");
+assertIteratorResult(it.next(), "1", false);
 s += ' ';
-assertThrowsValue(function () { it.next(); }, StopIteration);
+assertIteratorResult(it.next(), undefined, true);
 assertEq(s, "L0 L1 L");
--- a/js/src/jit-test/tests/for-of/array-iterator-shrinking.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-shrinking.js
@@ -1,9 +1,11 @@
-// If an array is truncated to the left of an iterator it, it.next() throws StopIteration.
+// If an array is truncated to the left of an iterator it, it.next() returns { done: true }.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
 var arr = [0, 1, 2];
-var it = arr.iterator();
-it.next();
-it.next();
+var it = arr[std_iterator]();
+assertIteratorResult(it.next(), 0, false);
+assertIteratorResult(it.next(), 1, false);
 arr.length = 1;
-assertThrowsValue(function () { it.next(); }, StopIteration);
+assertIteratorResult(it.next(), undefined, true);
--- a/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-surfaces-1.js
@@ -1,10 +1,12 @@
-// Superficial tests of the Array.prototype.iterator builtin function and its workalikes.
+// Superficial tests of the Array.prototype[@@iterator] builtin function and its workalikes.
+
+load(libdir + "iteration.js");
 
 var constructors = [Array, String, Uint8Array, Uint8ClampedArray];
 for (var c of constructors) {
-    assertEq(c.prototype.iterator.length, 0);
-    var desc = Object.getOwnPropertyDescriptor(c.prototype, "iterator");
+    assertEq(c.prototype[std_iterator].length, 0);
+    var desc = Object.getOwnPropertyDescriptor(c.prototype, std_iterator);
     assertEq(desc.configurable, true);
     assertEq(desc.enumerable, false);
     assertEq(desc.writable, true);
 }
--- a/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js
+++ b/js/src/jit-test/tests/for-of/array-iterator-surfaces-2.js
@@ -16,9 +16,8 @@ function check(it) {
     var s = '';
     for (var p in it)
         s += p + '.';
     assertEq(s, 'x.');
 }
 
 check([][std_iterator]());
 check(Array.prototype[std_iterator].call({}));
-check(Array.prototype[std_iterator].call(undefined));
--- a/js/src/jit-test/tests/for-of/next-2.js
+++ b/js/src/jit-test/tests/for-of/next-2.js
@@ -1,6 +1,8 @@
 // Iterator.prototype.next throws if applied to a non-iterator that inherits from an iterator.
 
 load(libdir + "asserts.js");
-var it = [1, 2].iterator();
+load(libdir + "iteration.js");
+
+var it = [1, 2][std_iterator]();
 var v = Object.create(it);
 assertThrowsInstanceOf(function () { Iterator.prototype.next.call(v); }, TypeError);
--- a/js/src/jit-test/tests/for-of/next-3.js
+++ b/js/src/jit-test/tests/for-of/next-3.js
@@ -1,8 +1,13 @@
-// The .next method of array iterators works across compartment boundaries.
+// Iterators from another compartment work with their own .next method
+// when called from another compartment, but not with the other
+// compartment's .next method.
+
+// FIXME: 'next' should work cross-realm.  Bug 924059.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
+
 var g = newGlobal();
-g.eval("var it = [1, 2].iterator();");
-assertEq(g.it.next(), 1);
-assertEq([].iterator().next.call(g.it), 2);
-assertThrowsValue([].iterator().next.bind(g.it), StopIteration);
+g.eval("var it = [1, 2]['" + std_iterator + "']();");
+assertIteratorResult(g.it.next(), 1, false);
+assertThrowsInstanceOf([][std_iterator]().next.bind(g.it), TypeError)
--- a/js/src/jit-test/tests/for-of/semantics-06.js
+++ b/js/src/jit-test/tests/for-of/semantics-06.js
@@ -1,6 +1,8 @@
 // Deleting the .next method makes for-of stop working on arrays.
 
 load(libdir + "asserts.js");
-var iterProto = Object.getPrototypeOf([].iterator());
+load(libdir + "iteration.js");
+
+var iterProto = Object.getPrototypeOf([][std_iterator]());
 delete iterProto.next;
 assertThrowsInstanceOf(function () { for (var v of []) ; }, TypeError);
--- a/js/src/jit-test/tests/for-of/string-iterator-generic.js
+++ b/js/src/jit-test/tests/for-of/string-iterator-generic.js
@@ -1,14 +1,15 @@
 // String.prototype.iterator is generic.
 
 load(libdir + "asserts.js");
+load(libdir + "iteration.js");
 
 function test(obj) {
-    var it = Array.prototype.iterator.call(obj);
+    var it = String.prototype[std_iterator].call(obj);
     for (var i = 0; i < (obj.length >>> 0); i++)
-        assertEq(it.next(), obj[i]);
-    assertThrowsValue(function () { it.next(); }, StopIteration);
+        assertIteratorResult(it.next(), obj[i], false);
+    assertIteratorResult(it.next(), undefined, true);
 }
 
 test({length: 0});
 test(Object.create(['x', 'y', 'z']));
 test(Object.create({length: 2, 0: 'x', 1: 'y'}));
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3842,32 +3842,16 @@ JS_NextProperty(JSContext *cx, JSObject 
         } else {
             *idp = ida->vector[--i];
             iterobj->setSlot(JSSLOT_ITER_INDEX, Int32Value(i));
         }
     }
     return true;
 }
 
-JS_PUBLIC_API(bool)
-JS_ArrayIterator(JSContext *cx, unsigned argc, jsval *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    Rooted<Value> target(cx, args.thisv());
-    AssertHeapIsIdle(cx);
-    assertSameCompartment(cx, target);
-    CHECK_REQUEST(cx);
-
-    JSObject *iterobj = ElementIteratorObject::create(cx, target);
-    if (!iterobj)
-        return false;
-    vp->setObject(*iterobj);
-    return true;
-}
-
 JS_PUBLIC_API(jsval)
 JS_GetReservedSlot(JSObject *obj, uint32_t index)
 {
     return obj->getReservedSlot(index);
 }
 
 JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject *obj, uint32_t index, Value value)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3189,25 +3189,16 @@ JS_NewPropertyIterator(JSContext *cx, JS
 /*
  * Return true on success with *idp containing the id of the next enumerable
  * property to visit using iterobj, or JSID_IS_VOID if there is no such property
  * left to visit.  Return false on error.
  */
 extern JS_PUBLIC_API(bool)
 JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp);
 
-/*
- * A JSNative that creates and returns a new iterator that iterates over the
- * elements of |this|, up to |this.length|, in index order. This can be used to
- * make any array-like object iterable. Just give the object an obj.iterator()
- * method using this JSNative as the implementation.
- */
-extern JS_PUBLIC_API(bool)
-JS_ArrayIterator(JSContext *cx, unsigned argc, jsval *vp);
-
 extern JS_PUBLIC_API(bool)
 JS_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                jsval *vp, unsigned *attrsp);
 
 extern JS_PUBLIC_API(jsval)
 JS_GetReservedSlot(JSObject *obj, uint32_t index);
 
 extern JS_PUBLIC_API(void)
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2952,18 +2952,17 @@ static const JSFunctionSpec array_method
     JS_SELF_HOSTED_FN("scatterPar",  "ArrayScatterPar",  5,0),
     JS_SELF_HOSTED_FN("filterPar",   "ArrayFilterPar",   2,0),
 #endif
 
     /* ES6 additions */
     JS_SELF_HOSTED_FN("find",        "ArrayFind",        1,0),
     JS_SELF_HOSTED_FN("findIndex",   "ArrayFindIndex",   1,0),
 
-    JS_SELF_HOSTED_FN("@@iterator",  "ArrayIterator",    0,0),
-    JS_FN("iterator",           JS_ArrayIterator,   0,0),
+    JS_SELF_HOSTED_FN("@@iterator",  "ArrayValues",      0,0),
     JS_FS_END
 };
 
 static const JSFunctionSpec array_static_methods[] = {
     JS_FN("isArray",            array_isArray,      1,0),
     JS_SELF_HOSTED_FN("lastIndexOf", "ArrayStaticLastIndexOf", 2,0),
     JS_SELF_HOSTED_FN("indexOf",     "ArrayStaticIndexOf", 2,0),
     JS_SELF_HOSTED_FN("forEach",     "ArrayStaticForEach", 2,0),
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -836,112 +836,40 @@ const Class PropertyIteratorObject::clas
     trace,
     {
         nullptr,             /* outerObject    */
         nullptr,             /* innerObject    */
         iterator_iteratorObject,
     }
 };
 
-static const uint32_t CLOSED_INDEX = UINT32_MAX;
-
-JSObject *
-ElementIteratorObject::create(JSContext *cx, Handle<Value> target)
-{
-    RootedObject proto(cx, cx->global()->getOrCreateElementIteratorPrototype(cx));
-    if (!proto)
-        return nullptr;
-    RootedObject iterobj(cx, NewObjectWithGivenProto(cx, &class_, proto, cx->global()));
-    if (iterobj) {
-        iterobj->setReservedSlot(TargetSlot, target);
-        iterobj->setReservedSlot(IndexSlot, Int32Value(0));
-    }
-    return iterobj;
-}
-
-static bool
-IsElementIterator(HandleValue v)
-{
-    return v.isObject() && v.toObject().is<ElementIteratorObject>();
-}
-
-bool
-ElementIteratorObject::next(JSContext *cx, unsigned argc, Value *vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-    return CallNonGenericMethod(cx, IsElementIterator, next_impl, args);
-}
-
-bool
-ElementIteratorObject::next_impl(JSContext *cx, CallArgs args)
-{
-    RootedObject iterobj(cx, &args.thisv().toObject());
-    uint32_t i, length;
-    RootedValue target(cx, iterobj->getReservedSlot(TargetSlot));
-    RootedObject obj(cx);
+enum {
+    ArrayIteratorSlotIteratedObject,
+    ArrayIteratorSlotNextIndex,
+    ArrayIteratorSlotItemKind,
+    ArrayIteratorSlotCount
+};
 
-    // Get target.length.
-    if (target.isString()) {
-        length = uint32_t(target.toString()->length());
-    } else {
-        obj = ToObjectFromStack(cx, target);
-        if (!obj)
-            goto close;
-        if (!GetLengthProperty(cx, obj, &length))
-            goto close;
-    }
-
-    // Check target.length.
-    i = uint32_t(iterobj->getReservedSlot(IndexSlot).toInt32());
-    if (i >= length) {
-        js_ThrowStopIteration(cx);
-        goto close;
-    }
-
-    // Get target[i].
-    JS_ASSERT(i + 1 > i);
-    if (target.isString()) {
-        JSString *c = cx->runtime()->staticStrings.getUnitStringForElement(cx, target.toString(), i);
-        if (!c)
-            goto close;
-        args.rval().setString(c);
-    } else {
-        if (!JSObject::getElement(cx, obj, obj, i, args.rval()))
-            goto close;
-    }
-
-    // On success, bump the index.
-    iterobj->setReservedSlot(IndexSlot, Int32Value(int32_t(i + 1)));
-    return true;
-
-  close:
-    // Close the iterator. The TargetSlot will never be used again, so don't keep a
-    // reference to it.
-    iterobj->setReservedSlot(TargetSlot, UndefinedValue());
-    iterobj->setReservedSlot(IndexSlot, Int32Value(int32_t(CLOSED_INDEX)));
-    return false;
-}
-
-const Class ElementIteratorObject::class_ = {
+const Class ArrayIteratorObject::class_ = {
     "Array Iterator",
     JSCLASS_IMPLEMENTS_BARRIERS |
-    JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots),
+    JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount),
     JS_PropertyStub,         /* addProperty */
     JS_DeletePropertyStub,   /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub,
     nullptr                  /* finalize    */
 };
 
-const JSFunctionSpec ElementIteratorObject::methods[] = {
-    JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
-    JS_FN("next", next, 0, 0),
+static const JSFunctionSpec array_iterator_methods[] = {
+    JS_SELF_HOSTED_FN("@@iterator", "ArrayIteratorIdentity", 0, 0),
+    JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
     JS_FS_END
 };
 
 static bool
 CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
 
 bool
 js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
@@ -1912,22 +1840,22 @@ GlobalObject::initIteratorClasses(JSCont
             return false;
         if (!DefinePropertiesAndBrand(cx, iteratorProto, nullptr, iterator_methods))
             return false;
         if (!DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto))
             return false;
     }
 
     RootedObject proto(cx);
-    if (global->getSlot(ELEMENT_ITERATOR_PROTO).isUndefined()) {
-        const Class *cls = &ElementIteratorObject::class_;
+    if (global->getSlot(ARRAY_ITERATOR_PROTO).isUndefined()) {
+        const Class *cls = &ArrayIteratorObject::class_;
         proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto);
-        if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, ElementIteratorObject::methods))
+        if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, array_iterator_methods))
             return false;
-        global->setReservedSlot(ELEMENT_ITERATOR_PROTO, ObjectValue(*proto));
+        global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
     }
 
     if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
         proto = NewObjectWithObjectPrototype(cx, global);
         if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, legacy_generator_methods))
             return false;
         global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
     }
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -128,43 +128,20 @@ class PropertyIteratorObject : public JS
 
     size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const;
 
   private:
     static void trace(JSTracer *trc, JSObject *obj);
     static void finalize(FreeOp *fop, JSObject *obj);
 };
 
-/*
- * Array iterators are roughly like this:
- *
- *   Array.prototype.iterator = function iterator() {
- *       for (var i = 0; i < (this.length >>> 0); i++)
- *           yield this[i];
- *   }
- *
- * However they are not generators. They are a different class. The semantics
- * of Array iterators will be given in the eventual ES6 spec in full detail.
- */
-class ElementIteratorObject : public JSObject
+class ArrayIteratorObject : public JSObject
 {
   public:
     static const Class class_;
-
-    static JSObject *create(JSContext *cx, Handle<Value> target);
-    static const JSFunctionSpec methods[];
-
-    enum {
-        TargetSlot,
-        IndexSlot,
-        NumSlots
-    };
-
-    static bool next(JSContext *cx, unsigned argc, Value *vp);
-    static bool next_impl(JSContext *cx, JS::CallArgs args);
 };
 
 bool
 VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap);
 
 bool
 GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp);
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -3653,19 +3653,17 @@ static const JSFunctionSpec string_metho
     JS_FN("anchor",            str_anchor,            1,0),
     JS_FN("strike",            str_strike,            0,0),
     JS_FN("small",             str_small,             0,0),
     JS_FN("big",               str_big,               0,0),
     JS_FN("blink",             str_blink,             0,0),
     JS_FN("sup",               str_sup,               0,0),
     JS_FN("sub",               str_sub,               0,0),
 #endif
-
-    JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator",  0,0),
-    JS_FN("iterator",          JS_ArrayIterator,      0,0),
+    JS_SELF_HOSTED_FN("@@iterator", "ArrayValues",    0,0),
     JS_FS_END
 };
 
 bool
 js_String(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
--- a/js/src/vm/GlobalObject.h
+++ b/js/src/vm/GlobalObject.h
@@ -90,18 +90,18 @@ class GlobalObject : public JSObject
     static const unsigned FROM_BUFFER_INT16 = FROM_BUFFER_UINT16 + 1;
     static const unsigned FROM_BUFFER_UINT32 = FROM_BUFFER_INT16 + 1;
     static const unsigned FROM_BUFFER_INT32 = FROM_BUFFER_UINT32 + 1;
     static const unsigned FROM_BUFFER_FLOAT32 = FROM_BUFFER_INT32 + 1;
     static const unsigned FROM_BUFFER_FLOAT64 = FROM_BUFFER_FLOAT32 + 1;
     static const unsigned FROM_BUFFER_UINT8CLAMPED = FROM_BUFFER_FLOAT64 + 1;
 
     /* One-off properties stored after slots for built-ins. */
-    static const unsigned ELEMENT_ITERATOR_PROTO  = FROM_BUFFER_UINT8CLAMPED + 1;
-    static const unsigned LEGACY_GENERATOR_OBJECT_PROTO = ELEMENT_ITERATOR_PROTO + 1;
+    static const unsigned ARRAY_ITERATOR_PROTO  = FROM_BUFFER_UINT8CLAMPED + 1;
+    static const unsigned LEGACY_GENERATOR_OBJECT_PROTO = ARRAY_ITERATOR_PROTO + 1;
     static const unsigned STAR_GENERATOR_OBJECT_PROTO = LEGACY_GENERATOR_OBJECT_PROTO + 1;
     static const unsigned MAP_ITERATOR_PROTO      = STAR_GENERATOR_OBJECT_PROTO + 1;
     static const unsigned SET_ITERATOR_PROTO      = MAP_ITERATOR_PROTO + 1;
     static const unsigned COLLATOR_PROTO          = SET_ITERATOR_PROTO + 1;
     static const unsigned NUMBER_FORMAT_PROTO     = COLLATOR_PROTO + 1;
     static const unsigned DATE_TIME_FORMAT_PROTO  = NUMBER_FORMAT_PROTO + 1;
     static const unsigned REGEXP_STATICS          = DATE_TIME_FORMAT_PROTO + 1;
     static const unsigned FUNCTION_NS             = REGEXP_STATICS + 1;
@@ -457,18 +457,18 @@ class GlobalObject : public JSObject
     }
 
   public:
     JSObject *getOrCreateIteratorPrototype(JSContext *cx) {
         return getOrCreateObject(cx, APPLICATION_SLOTS + JSProto_LIMIT + JSProto_Iterator,
                                  initIteratorClasses);
     }
 
-    JSObject *getOrCreateElementIteratorPrototype(JSContext *cx) {
-        return getOrCreateObject(cx, ELEMENT_ITERATOR_PROTO, initIteratorClasses);
+    JSObject *getOrCreateArrayIteratorPrototype(JSContext *cx) {
+        return getOrCreateObject(cx, ARRAY_ITERATOR_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateLegacyGeneratorObjectPrototype(JSContext *cx) {
         return getOrCreateObject(cx, LEGACY_GENERATOR_OBJECT_PROTO, initIteratorClasses);
     }
 
     JSObject *getOrCreateStarGeneratorObjectPrototype(JSContext *cx) {
         return getOrCreateObject(cx, STAR_GENERATOR_OBJECT_PROTO, initIteratorClasses);
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -464,16 +464,45 @@ intrinsic_GetIteratorPrototype(JSContext
     JSObject *obj = cx->global()->getOrCreateIteratorPrototype(cx);
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
+static bool
+intrinsic_NewArrayIterator(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(args.length() == 0);
+
+    RootedObject proto(cx, cx->global()->getOrCreateArrayIteratorPrototype(cx));
+    if (!proto)
+        return false;
+
+    JSObject *obj = NewObjectWithGivenProto(cx, proto->getClass(), proto, cx->global());
+    if (!obj)
+        return false;
+
+    args.rval().setObject(*obj);
+    return true;
+}
+
+static bool
+intrinsic_IsArrayIterator(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    JS_ASSERT(args.length() == 1);
+    JS_ASSERT(args[0].isObject());
+
+    args.rval().setBoolean(args[0].toObject().is<ArrayIteratorObject>());
+    return true;
+}
+
 /*
  * ParallelTestsShouldPass(): Returns false if we are running in a
  * mode (such as --ion-eager) that is known to cause additional
  * bailouts or disqualifications for parallel array tests.
  *
  * This is needed because the parallel tests generally assert that,
  * under normal conditions, they will run without bailouts or
  * compilation failures, but this does not hold under "stress-testing"
@@ -537,23 +566,26 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("ThrowError",              intrinsic_ThrowError,              4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
     JS_FN("SetScriptHints",          intrinsic_SetScriptHints,          2,0),
     JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       1,0),
     JS_FN("MakeWrappable",           intrinsic_MakeWrappable,           1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
 
-    JS_FN("UnsafePutElements",               intrinsic_UnsafePutElements,               3,0),
+    JS_FN("UnsafePutElements",       intrinsic_UnsafePutElements,       3,0),
     JS_FN("UnsafeSetReservedSlot",   intrinsic_UnsafeSetReservedSlot,   3,0),
     JS_FN("UnsafeGetReservedSlot",   intrinsic_UnsafeGetReservedSlot,   2,0),
     JS_FN("HaveSameClass",           intrinsic_HaveSameClass,           2,0),
 
     JS_FN("GetIteratorPrototype",    intrinsic_GetIteratorPrototype,    0,0),
 
+    JS_FN("NewArrayIterator",        intrinsic_NewArrayIterator,        0,0),
+    JS_FN("IsArrayIterator",         intrinsic_IsArrayIterator,         1,0),
+
     JS_FN("ForkJoin",                intrinsic_ForkJoin,                2,0),
     JS_FN("ForkJoinSlices",          intrinsic_ForkJoinSlices,          0,0),
     JS_FN("NewParallelArray",        intrinsic_NewParallelArray,        3,0),
     JS_FN("NewDenseArray",           intrinsic_NewDenseArray,           1,0),
     JS_FN("ShouldForceSequential",   intrinsic_ShouldForceSequential,   0,0),
     JS_FN("ParallelTestsShouldPass", intrinsic_ParallelTestsShouldPass, 0,0),
 
     // See builtin/Intl.h for descriptions of the intl_* functions.
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -3422,28 +3422,26 @@ const JSFunctionSpec ArrayBufferObject::
 
 /*
  * TypedArrayObject boilerplate
  */
 
 #ifndef RELEASE_BUILD
 # define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                     \
 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
-    JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0, 0),                        \
-    JS_FN("iterator", JS_ArrayIterator, 0, 0),                                     \
+    JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
     JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),           \
     JS_FN("move", _typedArray##Object::fun_move, 3, JSFUN_GENERIC_NATIVE),         \
     JS_FS_END                                                                      \
 }
 #else
 # define IMPL_TYPED_ARRAY_STATICS(_typedArray)                                     \
 const JSFunctionSpec _typedArray##Object::jsfuncs[] = {                            \
-    JS_SELF_HOSTED_FN("@@iterator", "ArrayIterator", 0, 0),                        \
-    JS_FN("iterator", JS_ArrayIterator, 0, 0),                                     \
+    JS_SELF_HOSTED_FN("@@iterator", "ArrayValues", 0, 0),                          \
     JS_FN("subarray", _typedArray##Object::fun_subarray, 2, JSFUN_GENERIC_NATIVE), \
     JS_FN("set", _typedArray##Object::fun_set, 2, JSFUN_GENERIC_NATIVE),           \
     JS_FS_END                                                                      \
 }
 #endif
 
 #define IMPL_TYPED_ARRAY_JSAPI_CONSTRUCTORS(Name,NativeType)                                 \
   JS_FRIEND_API(JSObject *) JS_New ## Name ## Array(JSContext *cx, uint32_t nelements)       \