Bug 1198701 - ArrayIterator gets length property after iteration has finished. r=till
☠☠ backed out by 2678b91f4165 ☠ ☠
authorGreg Weng <snowmantw@gmail.com>
Fri, 18 Mar 2016 00:43:00 -0400
changeset 289618 dbf70d71fe29d0c0738f1fb08312c260c9351774
parent 289617 a41a84351d7c358b094f508e44edef6fb96fe17c
child 289619 ed173749281de53ef1b08b2ef889cceea54a949a
push id19656
push usergwagner@mozilla.com
push dateMon, 04 Apr 2016 13:43:23 +0000
treeherderb2g-inbound@e99061fde28a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1198701
milestone48.0a1
Bug 1198701 - ArrayIterator gets length property after iteration has finished. r=till
js/src/builtin/Array.js
js/src/tests/ecma_6/Array/iterator_next.js
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -710,53 +710,71 @@ function CreateArrayIteratorAt(obj, kind
     UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_NEXT_INDEX, n);
     UnsafeSetReservedSlot(iterator, ITERATOR_SLOT_ITEM_KIND, kind);
     return iterator;
 }
 function CreateArrayIterator(obj, kind) {
     return CreateArrayIteratorAt(obj, kind, 0);
 }
 
-
+// ES6, 22.1.5.2.1
+// https://tc39.github.io/ecma262/#sec-%arrayiteratorprototype%.next
 function ArrayIteratorNext() {
+    // Step 1-3.
     if (!IsObject(this) || !IsArrayIterator(this)) {
         return callFunction(CallArrayIteratorMethodIfWrapped, this,
                             "ArrayIteratorNext");
     }
-    var a = UnsafeGetObjectFromReservedSlot(this, ITERATOR_SLOT_TARGET);
+
+    // Step 4.
+    var a = UnsafeGetReservedSlot(this, ITERATOR_SLOT_TARGET);
+    var result = { value: undefined, done: false };
+
+    // Step 5.
+    if (!a) {
+      result.done = true;
+      return result;
+    }
+
+    // Step 6.
     // The index might not be an integer, so we have to do a generic get here.
     var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX);
+
+    // Step 7.
     var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND);
-    var result = { value: undefined, done: false };
+
+    // Step 8-9.
     var len = IsPossiblyWrappedTypedArray(a)
               ? PossiblyWrappedTypedArrayLength(a)
               : TO_UINT32(a.length);
 
-    // FIXME: This should be ToLength, which clamps at 2**53.  Bug 924058.
+    // Step 10.
     if (index >= len) {
-        // When the above is changed to ToLength, use +1/0 here instead
-        // of MAX_UINT32.
-        UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, 0xffffffff);
+        UnsafeSetReservedSlot(this, ITERATOR_SLOT_TARGET, null);
         result.done = true;
         return result;
     }
 
+    // Step 11.
     UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1);
 
+    // Step 16.
     if (itemKind === ITEM_KIND_VALUE) {
         result.value = a[index];
         return result;
     }
 
+    // Step 13.
     if (itemKind === ITEM_KIND_KEY_AND_VALUE) {
         var pair = [index, a[index]];
         result.value = pair;
         return result;
     }
 
+    // Step 12.
     assert(itemKind === ITEM_KIND_KEY, itemKind);
     result.value = index;
     return result;
 }
 
 function ArrayValuesAt(n) {
     return CreateArrayIteratorAt(this, ITEM_KIND_VALUE, n);
 }
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/Array/iterator_next.js
@@ -0,0 +1,23 @@
+function testIteratorNextGetLength() {
+  var lengthCalledTimes = 0;
+  var array = {
+    __proto__: Array.prototype,
+    get length() {
+      lengthCalledTimes += 1;
+      return {
+        valueOf() {
+          return 0;
+        }
+      };
+    }
+  };
+  var it = array[Symbol.iterator]();
+  it.next();
+  it.next();
+  if (typeof reportCompare === 'function') {
+    reportCompare(1, lengthCalledTimes,
+      "when an iterator get length zero, it shouldn't access it again");
+  }
+}
+testIteratorNextGetLength();
+