Bug 667527 - Remove the array-length limitation from the method used in certain cases to append values to newborn arrays, and name it more generally than previously. r=dmandelin
authorJeff Walden <jwalden@mit.edu>
Tue, 28 Jun 2011 10:05:40 -0700
changeset 72370 bc1e401e5bb5
parent 72369 c3ceee49ac37
child 72371 f25365ded0a9
push id20706
push usercleary@mozilla.com
push date2011-07-06 00:33 +0000
treeherdermozilla-central@6840fbf4dcdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdmandelin
bugs667527
milestone7.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 667527 - Remove the array-length limitation from the method used in certain cases to append values to newborn arrays, and name it more generally than previously. r=dmandelin
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsbuiltins.h
js/src/jsinterp.cpp
js/src/jsonparser.cpp
js/src/jstracer.cpp
js/src/tests/ecma_5/JSON/jstests.list
js/src/tests/ecma_5/JSON/parse-mega-huge-array.js
js/src/tests/js1_8_5/regress/jstests.list
js/src/tests/js1_8_5/regress/no-array-comprehension-length-limit.js
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2099,68 +2099,59 @@ array_push1_dense(JSContext* cx, JSObjec
 
     if (!obj->makeDenseArraySlow(cx))
         return false;
     Value tmp = v;
     return array_push_slowly(cx, obj, 1, &tmp, rval);
 }
 
 JS_ALWAYS_INLINE JSBool
-ArrayCompPushImpl(JSContext *cx, JSObject *obj, const Value &v)
+NewbornArrayPushImpl(JSContext *cx, JSObject *obj, const Value &v)
 {
+    JS_ASSERT(!v.isMagic());
+
     uint32 length = obj->getArrayLength();
     if (obj->isSlowArray()) {
         /* This can happen in one evil case. See bug 630377. */
         jsid id;
         return IndexToId(cx, length, &id) &&
                js_DefineProperty(cx, obj, id, &v, NULL, NULL, JSPROP_ENUMERATE);
     }
 
     JS_ASSERT(obj->isDenseArray());
     JS_ASSERT(length <= obj->getDenseArrayCapacity());
 
-    if (length == obj->getDenseArrayCapacity()) {
-        if (length > JS_ARGS_LENGTH_MAX) {
-            JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
-                                   JSMSG_ARRAY_INIT_TOO_BIG);
-            return false;
-        }
-
-        /*
-         * An array comprehension cannot add holes to the array. So we can use
-         * ensureSlots instead of ensureDenseArrayElements.
-         */
-        if (!obj->ensureSlots(cx, length + 1))
-            return false;
-    }
+    if (length == obj->getDenseArrayCapacity() && !obj->ensureSlots(cx, length + 1))
+        return false;
+
     obj->setArrayLength(length + 1);
     obj->setDenseArrayElement(length, v);
     return true;
 }
 
 JSBool
-js_ArrayCompPush(JSContext *cx, JSObject *obj, const Value &vp)
+js_NewbornArrayPush(JSContext *cx, JSObject *obj, const Value &vp)
 {
-    return ArrayCompPushImpl(cx, obj, vp);
+    return NewbornArrayPushImpl(cx, obj, vp);
 }
 
 #ifdef JS_TRACER
 JSBool JS_FASTCALL
-js_ArrayCompPush_tn(JSContext *cx, JSObject *obj, ValueArgType v)
+js_NewbornArrayPush_tn(JSContext *cx, JSObject *obj, ValueArgType v)
 {
     TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
 
-    if (!ArrayCompPushImpl(cx, obj, ValueArgToConstRef(v))) {
+    if (!NewbornArrayPushImpl(cx, obj, ValueArgToConstRef(v))) {
         SetBuiltinError(tm);
         return JS_FALSE;
     }
 
     return WasBuiltinSuccessful(tm);
 }
-JS_DEFINE_CALLINFO_3(extern, BOOL_FAIL, js_ArrayCompPush_tn, CONTEXT, OBJECT,
+JS_DEFINE_CALLINFO_3(extern, BOOL_FAIL, js_NewbornArrayPush_tn, CONTEXT, OBJECT,
                      VALUE, 0, nanojit::ACCSET_STORE_ANY)
 #endif
 
 static JSBool
 array_push(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ToObject(cx, &vp[1]);
     if (!obj)
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -248,18 +248,25 @@ extern JSBool
 array_sort(JSContext *cx, uintN argc, js::Value *vp);
 }
 
 #ifdef DEBUG
 extern JSBool
 js_ArrayInfo(JSContext *cx, uintN argc, jsval *vp);
 #endif
 
+/*
+ * Append the given (non-hole) value to the end of an array.  The array must be
+ * a newborn array -- that is, one which has not been exposed to script for
+ * arbitrary manipulation.  (This method optimizes on the assumption that
+ * extending the array to accommodate the element will never make the array
+ * sparse, which requires that the array be completely filled.)
+ */
 extern JSBool
-js_ArrayCompPush(JSContext *cx, JSObject *obj, const js::Value &vp);
+js_NewbornArrayPush(JSContext *cx, JSObject *obj, const js::Value &v);
 
 JSBool
 js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj);
 
 /*
  * Utility to access the value from the id returned by array_lookupProperty.
  */
 JSBool
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -576,17 +576,17 @@ js_dmod(jsdouble a, jsdouble b);
 #endif /* !JS_TRACER */
 
 /* Defined in jsarray.cpp. */
 namespace js {
 JS_DECLARE_CALLINFO(NewDenseEmptyArray)
 JS_DECLARE_CALLINFO(NewDenseAllocatedArray)
 JS_DECLARE_CALLINFO(NewDenseUnallocatedArray)
 }
-JS_DECLARE_CALLINFO(js_ArrayCompPush_tn)
+JS_DECLARE_CALLINFO(js_NewbornArrayPush_tn)
 JS_DECLARE_CALLINFO(js_EnsureDenseArrayCapacity)
 
 /* Defined in jsbuiltins.cpp. */
 JS_DECLARE_CALLINFO(js_UnboxNumberAsDouble)
 JS_DECLARE_CALLINFO(js_UnboxNumberAsInt32)
 JS_DECLARE_CALLINFO(js_dmod)
 JS_DECLARE_CALLINFO(js_imod)
 JS_DECLARE_CALLINFO(js_DoubleToInt32)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -5958,17 +5958,17 @@ BEGIN_CASE(JSOP_YIELD)
     goto exit;
 
 BEGIN_CASE(JSOP_ARRAYPUSH)
 {
     uint32 slot = GET_UINT16(regs.pc);
     JS_ASSERT(script->nfixed <= slot);
     JS_ASSERT(slot < script->nslots);
     JSObject *obj = &regs.fp()->slots()[slot].toObject();
-    if (!js_ArrayCompPush(cx, obj, regs.sp[-1]))
+    if (!js_NewbornArrayPush(cx, obj, regs.sp[-1]))
         goto error;
     regs.sp--;
 }
 END_CASE(JSOP_ARRAYPUSH)
 #endif /* JS_HAS_GENERATORS */
 
 #if JS_THREADED_INTERP
   L_JSOP_BACKPATCH:
--- a/js/src/jsonparser.cpp
+++ b/js/src/jsonparser.cpp
@@ -579,17 +579,17 @@ JSONParser::parse(Value *vp)
             if (token == OOM)
                 return false;
             if (token != Error)
                 error("property names must be double-quoted strings");
             return errorReturn();
 
           case FinishArrayElement: {
             Value v = valueStack.popCopy();
-            if (!js_ArrayCompPush(cx, &valueStack.back().toObject(), v))
+            if (!js_NewbornArrayPush(cx, &valueStack.back().toObject(), v))
                 return false;
             token = advanceAfterArrayElement();
             if (token == Comma) {
                 if (!stateStack.append(FinishArrayElement))
                     return false;
                 goto JSONValue;
             }
             if (token == ArrayClose)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -16264,17 +16264,17 @@ TraceRecorder::record_JSOP_ARRAYPUSH()
     JS_ASSERT(arrayval.isObject());
     LIns *array_ins = get(&arrayval);
     Value &elt = stackval(-1);
     LIns *elt_ins = box_value_for_native_call(elt, get(&elt));
 
     enterDeepBailCall();
 
     LIns *args[] = { elt_ins, array_ins, cx_ins };
-    pendingGuardCondition = w.call(&js_ArrayCompPush_tn_ci, args);
+    pendingGuardCondition = w.call(&js_NewbornArrayPush_tn_ci, args);
 
     leaveDeepBailCall();
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_ENUMCONSTELEM()
 {
--- a/js/src/tests/ecma_5/JSON/jstests.list
+++ b/js/src/tests/ecma_5/JSON/jstests.list
@@ -1,14 +1,15 @@
 url-prefix ../../jsreftest.html?test=ecma_5/JSON/
 script cyclic-stringify.js
 script small-codepoints.js
 script parse.js
 script parse-arguments.js
 script parse-crockford-01.js
+script parse-mega-huge-array.js
 script parse-number-syntax.js
 script parse-octal-syntax-error.js
 script parse-primitives.js
 script parse-reviver.js
 script parse-reviver-array-delete.js
 script parse-syntax-errors-01.js
 script parse-syntax-errors-02.js
 script parse-syntax-errors-03.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/JSON/parse-mega-huge-array.js
@@ -0,0 +1,28 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var gTestfile = 'parse-mega-huge-array.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 667527;
+var summary = "JSON.parse should parse arrays of essentially unlimited size";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var str = '[';
+for (var i = 0, sz = Math.pow(2, 21); i < sz; i++)
+  str += '0,';
+str += '0]';
+
+var arr = JSON.parse(str);
+assertEq(arr.length, Math.pow(2, 21) + 1);
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/js1_8_5/regress/jstests.list
+++ b/js/src/tests/js1_8_5/regress/jstests.list
@@ -1,9 +1,10 @@
 url-prefix ../../jsreftest.html?test=js1_8_5/regress/
+script no-array-comprehension-length-limit.js
 script regress-383902.js
 script regress-500528.js
 script regress-533876.js
 script regress-541255-0.js
 script regress-541255-1.js
 script regress-541255-2.js
 script regress-541255-3.js
 script regress-541255-4.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/no-array-comprehension-length-limit.js
@@ -0,0 +1,14 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+function range(n)
+{
+  var i = 0;
+  while (i < n)
+    yield i++;
+}
+
+[0 for (_ in range(Math.pow(2, 20)))];
+
+reportCompare(true, true);