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 bc1e401e5bb59e5e7863b8d595221499e86b300b
parent 72369 c3ceee49ac37061748459147fe52841be9f0bc47
child 72371 f25365ded0a9de9be4eb2c9b98eb5e3c25c7dc17
push idunknown
push userunknown
push dateunknown
reviewersdmandelin
bugs667527
milestone7.0a1
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);