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 85154 5fcbfafa4471e35b362bfd17da0ad59df5b3606d
parent 85153 f79582c3fd75772dd1d654b9745b37186f61f1a6
child 85155 271838a8bc5ea69c8f1634072973fd2cfa676b8e
push id21906
push usermak77@bonardo.net
push dateTue, 24 Jan 2012 12:27:58 +0000
treeherdermozilla-central@6e3018bb5cfe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs720511
milestone12.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 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