Bug 909602 - Don't blow away most/all elements above the purported "length" of a non-Array object passed to Array.prototype.pop. r=bhackett
authorJeff Walden <jwalden@mit.edu>
Mon, 16 Sep 2013 18:44:45 -0700
changeset 147796 9bce276f9c2d00cd43a65cc0297e24b1ce55f097
parent 147795 28214460272816ae950284b6776801484bd3ebcb
child 147797 5f8518a7584dcb2e7b1a7d700ea96955a5e3c5b7
push id25317
push useremorley@mozilla.com
push dateThu, 19 Sep 2013 14:37:38 +0000
treeherdermozilla-central@70a765607344 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbhackett
bugs909602
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 909602 - Don't blow away most/all elements above the purported "length" of a non-Array object passed to Array.prototype.pop. r=bhackett
js/src/jsarray.cpp
js/src/tests/ecma_5/Array/pop-nonarray-higher-elements.js
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1989,20 +1989,24 @@ js::array_pop(JSContext *cx, unsigned ar
         if (!GetElement(cx, obj, index, &hole, args.rval()))
             return false;
 
         /* Step 5c. */
         if (!hole && !DeletePropertyOrThrow(cx, obj, index))
             return false;
     }
 
-    // Keep dense initialized length optimal, if possible.  Note that this just
-    // reflects the possible deletion above: in particular, it's okay to do
-    // this even if the length is non-writable and SetLengthProperty throws.
-    if (obj->isNative() && obj->getDenseInitializedLength() > index)
+    // If this was an array, then there are no elements above the one we just
+    // deleted (if we deleted an element).  Thus we can shrink the dense
+    // initialized length accordingly.  (This is fine even if the array length
+    // is non-writable: length-changing occurs after element-deletion effects.)
+    // Don't do anything if this isn't an array, as any deletion above has no
+    // effect on any elements after the "last" one indicated by the "length"
+    // property.
+    if (obj->is<ArrayObject>() && obj->getDenseInitializedLength() > index)
         obj->setDenseInitializedLength(index);
 
     /* Steps 4a, 5d. */
     return SetLengthProperty(cx, obj, index);
 }
 
 void
 js::ArrayShiftMoveElements(JSObject *obj)
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Array/pop-nonarray-higher-elements.js
@@ -0,0 +1,91 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 909602;
+var summary =
+  "Array.prototype.pop shouldn't touch elements greater than length on " +
+  "non-arrays";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+function doTest(obj, index)
+{
+  // print("testing " + JSON.stringify(obj) + " with index " + index);
+  assertEq(Array.prototype.pop.call(obj), undefined);
+  assertEq(index in obj, true);
+  assertEq(obj[index], 42);
+}
+
+// not-super-much-later element
+
+// non-zero length
+function testPop1()
+{
+  var obj = { length: 2, 3: 42 };
+  doTest(obj, 3);
+}
+for (var i = 0; i < 50; i++)
+  testPop1();
+
+// zero length
+function testPop2()
+{
+  var obj = { length: 0, 3: 42 };
+  doTest(obj, 3);
+}
+for (var i = 0; i < 50; i++)
+  testPop2();
+
+// much-later (but dense) element
+
+// non-zero length
+function testPop3()
+{
+  var obj = { length: 2, 55: 42 };
+  doTest(obj, 55);
+}
+for (var i = 0; i < 50; i++)
+  testPop3();
+
+// zero length
+function testPop4()
+{
+  var obj = { length: 0, 55: 42 };
+  doTest(obj, 55);
+}
+for (var i = 0; i < 50; i++)
+  testPop4();
+
+// much much much later (sparse) element
+
+// non-zero length
+function testPop5()
+{
+  var obj = { length: 2, 65530: 42 };
+  doTest(obj, 65530);
+}
+for (var i = 0; i < 50; i++)
+  testPop5();
+
+// zero length
+function testPop6()
+{
+  var obj = { length: 0, 65530: 42 };
+  doTest(obj, 65530);
+}
+for (var i = 0; i < 50; i++)
+  testPop6();
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");