Bug 1198701 - ArrayIterator gets length property after iteration has finished. r=till
authorGreg Weng <snowmantw@gmail.com>
Tue, 12 Apr 2016 18:29:00 +0200
changeset 293007 f88bfa3062821076a4aabef319422ce705d9715c
parent 293006 2c9daa36cfd7d8ba0a75220cb9453817f816acde
child 293008 92a78fabeb8d727258cb5313bbc394f3e3786c09
push id30172
push userkwierso@gmail.com
push dateWed, 13 Apr 2016 21:18:48 +0000
treeherdermozilla-central@bc2373295e31 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstill
bugs1198701
milestone48.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 1198701 - ArrayIterator gets length property after iteration has finished. r=till
js/src/builtin/Array.js
js/src/tests/ecma_6/Array/iterator_edge_cases.js
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -711,53 +711,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
+// http://www.ecma-international.org/ecma-262/6.0/index.html#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 === null) {
+      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);
 }
--- a/js/src/tests/ecma_6/Array/iterator_edge_cases.js
+++ b/js/src/tests/ecma_6/Array/iterator_edge_cases.js
@@ -18,10 +18,33 @@ function TestArrayIteratorWrappers() {
     var iter = [][Symbol.iterator]();
     assertDeepEq(iter.next.call(newGlobal().eval('[5][Symbol.iterator]()')),
 		 { value: 5, done: false })
 }
 if (typeof newGlobal === "function") {
     TestArrayIteratorWrappers();
 }
 
+// Tests that calling |next| on an array iterator after iteration has finished
+// doesn't get the array's |length| property.
+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();
+  assertEq(1, lengthCalledTimes);
+}
+TestIteratorNextGetLength();
+
+
 if (typeof reportCompare === "function")
   reportCompare(true, true);