Bug 720511 - Make sure indexed elements on prototypes of an array being joined show through in the final join-string. r=luke
authorJeff Walden <jwalden@mit.edu>
Sat, 21 Jan 2012 04:05:53 -0800
changeset 86379 5fcbfafa4471e35b362bfd17da0ad59df5b3606d
parent 86378 f79582c3fd75772dd1d654b9745b37186f61f1a6
child 86380 271838a8bc5ea69c8f1634072973fd2cfa676b8e
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs720511
milestone12.0a1
Bug 720511 - Make sure indexed elements on prototypes of an array being joined show through in the final join-string. r=luke
js/src/jsarray.cpp
js/src/tests/ecma_5/Array/join-01.js
js/src/tests/ecma_5/Array/jstests.list
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1620,25 +1620,46 @@ array_toString_sub(JSContext *cx, JSObje
 
     jsuint length;
     if (!js_GetLengthProperty(cx, obj, &length))
         return false;
 
     StringBuffer sb(cx);
 
     if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
-        /* Elements beyond the initialized length are 'undefined' and thus can be ignored. */
-        const Value *beg = obj->getDenseArrayElements();
-        const Value *end = beg + Min(length, obj->getDenseArrayInitializedLength());
-        for (const Value *vp = beg; vp != end; ++vp) {
+        const Value *start = obj->getDenseArrayElements();
+        const Value *end = start + obj->getDenseArrayInitializedLength();
+        const Value *elem;
+        for (elem = start; elem < end; elem++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx))
                 return false;
 
-            if (!vp->isMagic(JS_ARRAY_HOLE) && !vp->isNullOrUndefined()) {
-                if (!ValueToStringBuffer(cx, *vp, sb))
+            /*
+             * Object stringifying is slow; delegate it to a separate loop to
+             * keep this one tight.
+             */
+            if (elem->isObject())
+                break;
+
+            if (!elem->isMagic(JS_ARRAY_HOLE) && !elem->isNullOrUndefined()) {
+                if (!ValueToStringBuffer(cx, *elem, sb))
+                    return false;
+            }
+        }
+
+        for (uint32_t i = uint32_t(PointerRangeSize(start, elem)); i < length; i++) {
+            if (!JS_CHECK_OPERATION_LIMIT(cx))
+                return false;
+
+            JSBool hole;
+            Value v;
+            if (!GetElement(cx, obj, i, &hole, &v))
+                return false;
+            if (!hole && !v.isNullOrUndefined()) {
+                if (!ValueToStringBuffer(cx, v, sb))
                     return false;
             }
         }
     } else {
         for (jsuint index = 0; index < length; index++) {
             if (!JS_CHECK_OPERATION_LIMIT(cx))
                 return false;
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/Array/join-01.js
@@ -0,0 +1,76 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ * Contributor:
+ *   Jeff Walden <jwalden+code@mit.edu>
+ */
+
+//-----------------------------------------------------------------------------
+print("ES5: Array.prototype.join");
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var count;
+var stringifyCounter = { toString: function() { count++; return "obj"; } };
+
+var arr = [1, 2, 3, 4, 5];
+assertEq(arr.join(), "1,2,3,4,5");
+assertEq(arr.join(","), "1,2,3,4,5");
+assertEq(arr.join(undefined), "1,2,3,4,5");
+assertEq(arr.join(4), "142434445");
+assertEq(arr.join(""), "12345");
+
+count = 0;
+assertEq(arr.join(stringifyCounter), "1obj2obj3obj4obj5");
+assertEq(count, 1);
+
+var holey = [1, 2, , 4, 5];
+assertEq(holey.join(), "1,2,,4,5");
+assertEq(holey.join(","), "1,2,,4,5");
+assertEq(holey.join(undefined), "1,2,,4,5");
+assertEq(holey.join(4), "14244445");
+
+count = 0;
+assertEq(holey.join(stringifyCounter), "1obj2objobj4obj5");
+assertEq(count, 1);
+
+var nully = [1, 2, 3, null, 5];
+assertEq(nully.join(), "1,2,3,,5");
+assertEq(nully.join(","), "1,2,3,,5");
+assertEq(nully.join(undefined), "1,2,3,,5");
+assertEq(nully.join(4), "14243445");
+
+count = 0;
+assertEq(nully.join(stringifyCounter), "1obj2obj3objobj5");
+assertEq(count, 1);
+
+var undefiney = [1, undefined, 3, 4, 5];
+assertEq(undefiney.join(), "1,,3,4,5");
+assertEq(undefiney.join(","), "1,,3,4,5");
+assertEq(undefiney.join(undefined), "1,,3,4,5");
+assertEq(undefiney.join(4), "14434445");
+
+count = 0;
+assertEq(undefiney.join(stringifyCounter), "1objobj3obj4obj5");
+assertEq(count, 1);
+
+var funky =
+  {
+    toString: function()
+    {
+      Array.prototype[1] = "chorp";
+      Object.prototype[3] = "fnord";
+      return "funky";
+    }
+  };
+var trailingHoles = [0, funky, /* 2 */, /* 3 */,];
+assertEq(trailingHoles.join(""), "0funkyfnord");
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/ecma_5/Array/jstests.list
+++ b/js/src/tests/ecma_5/Array/jstests.list
@@ -1,9 +1,10 @@
 url-prefix ../../jsreftest.html?test=ecma_5/Array/
+script join-01.js
 script length-01.js
 script length-set-object.js
 script regress-599159.js
 script sort-01.js
 script splice-return-array-elements-defined-not-set.js
 script splice-suppresses-unvisited-indexes.js
 script toLocaleString-01.js
 script toString-01.js