[INFER] Fix cases where dense arrays have initialized length < capacity with disabled inference, bug 648357.
authorBrian Hackett <bhackett1024@gmail.com>
Thu, 07 Apr 2011 17:14:15 -0700
changeset 74911 9f0cb8d7da58032335ce12a1323c856b54b3026d
parent 74910 6228c71f399448689cb6d788c6377131b4c9c9a3
child 74912 4dcb83428de7e35028a7d09c7a824c524585ef5e
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs648357
milestone2.2a1pre
[INFER] Fix cases where dense arrays have initialized length < capacity with disabled inference, bug 648357.
js/src/jit-test/tests/basic/bug648357.js
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsobj.cpp
js/src/methodjit/FrameState.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug648357.js
@@ -0,0 +1,3 @@
+var x = [1, 2, 3, 4, 5, 6, 7, 8];
+x.pop();
+x.push(9);
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1504,24 +1504,30 @@ InitArrayObject(JSContext *cx, JSObject 
         return true;
 
     if (!InitArrayTypes(cx, obj->getType(), vector, length))
         return false;
 
     /* Avoid ensureDenseArrayElements to skip sparse array checks there. */
     if (!obj->ensureSlots(cx, length))
         return false;
-    obj->setDenseArrayInitializedLength(length);
+
+    if (cx->typeInferenceEnabled())
+        obj->setDenseArrayInitializedLength(length);
+    else
+        obj->backfillDenseArrayHoles();
+
     bool hole = false;
     for (jsuint i = 0; i < length; i++) {
         obj->setDenseArrayElement(i, vector[i]);
         hole |= vector[i].isMagic(JS_ARRAY_HOLE);
     }
     if (hole && !obj->setDenseArrayNotPacked(cx))
         return false;
+
     return true;
 }
 
 /*
  * Perl-inspired join, reverse, and sort.
  */
 static JSBool
 array_join(JSContext *cx, uintN argc, Value *vp)
@@ -1578,16 +1584,17 @@ array_reverse(JSContext *cx, uintN argc,
                 return false;
             JS_ASSERT(result == JSObject::ED_SPARSE);
             break;
         }
 
         /* Fill out the array's initialized length to its proper length. */
         jsuint initlen = obj->getDenseArrayInitializedLength();
         if (len > initlen) {
+            JS_ASSERT(cx->typeInferenceEnabled());
             if (!obj->setDenseArrayNotPacked(cx))
                 return false;
             ClearValueRange(obj->getDenseArrayElements() + initlen, len - initlen, true);
             obj->setDenseArrayInitializedLength(len);
         }
 
         uint32 lo = 0, hi = len - 1;
         for (; lo < hi; lo++, hi--) {
@@ -2164,19 +2171,23 @@ ArrayCompPushImpl(JSContext *cx, JSObjec
         }
 
         /*
          * 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 (!cx->typeInferenceEnabled())
+            obj->backfillDenseArrayHoles();
     }
+
+    if (cx->typeInferenceEnabled())
+        obj->setDenseArrayInitializedLength(length + 1);
     obj->setDenseArrayLength(length + 1);
-    obj->setDenseArrayInitializedLength(length + 1);
     obj->setDenseArrayElement(length, v);
     return true;
 }
 
 JSBool
 js_ArrayCompPush(JSContext *cx, JSObject *obj, const Value &vp)
 {
     return ArrayCompPushImpl(cx, obj, vp);
@@ -2254,19 +2265,26 @@ array_pop_dense(JSContext *cx, JSObject*
     }
     index--;
     if (!GetElement(cx, obj, index, &hole, vp))
         return JS_FALSE;
     if (hole && !cx->markTypeCallerUnexpected(TYPE_UNDEFINED))
         return JS_FALSE;
     if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
         return JS_FALSE;
+
     obj->setDenseArrayLength(index);
-    if (index == obj->getDenseArrayInitializedLength() - 1)
-        obj->setDenseArrayInitializedLength(index);
+    if (cx->typeInferenceEnabled()) {
+        JS_ASSERT(!obj->isPackedDenseArray());
+        if (index == obj->getDenseArrayInitializedLength() - 1)
+            obj->setDenseArrayInitializedLength(index);
+        if (!cx->markTypeArrayShrank(obj->getType()))
+            return JS_FALSE;
+    }
+
     return JS_TRUE;
 }
 
 static JSBool
 array_pop(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ToObject(cx, &vp[1]);
     if (!obj)
@@ -2289,33 +2307,34 @@ array_shift(JSContext *cx, uintN argc, V
 
     if (length == 0) {
         vp->setUndefined();
         if (!cx->markTypeCallerUnexpected(TYPE_UNDEFINED))
             return JS_FALSE;
     } else {
         length--;
 
-        if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) {
-            Value *elems = obj->getDenseArrayElements();
-            jsuint initlen = obj->getDenseArrayInitializedLength();
-            if (initlen > 0) {
-                *vp = obj->getDenseArrayElement(0);
-                if (vp->isMagic(JS_ARRAY_HOLE)) {
-                    vp->setUndefined();
-                    if (!cx->markTypeCallerUnexpected(TYPE_UNDEFINED))
-                        return JS_FALSE;
-                }
-                memmove(elems, elems + 1, (initlen - 1) * sizeof(jsval));
-                obj->setDenseArrayInitializedLength(initlen - 1);
-            } else {
+        if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
+            length < obj->getDenseArrayCapacity() &&
+            0 < obj->getDenseArrayInitializedLength()) {
+            *vp = obj->getDenseArrayElement(0);
+            if (vp->isMagic(JS_ARRAY_HOLE)) {
                 vp->setUndefined();
                 if (!cx->markTypeCallerUnexpected(TYPE_UNDEFINED))
                     return JS_FALSE;
             }
+            Value *elems = obj->getDenseArrayElements();
+            memmove(elems, elems + 1, length * sizeof(jsval));
+            if (cx->typeInferenceEnabled()) {
+                obj->setDenseArrayInitializedLength(obj->getDenseArrayInitializedLength() - 1);
+                if (!cx->markTypeArrayShrank(obj->getType()))
+                    return JS_FALSE;
+            } else {
+                obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
+            }
             JS_ALWAYS_TRUE(obj->setArrayLength(cx, length));
             if (!js_SuppressDeletedIndexProperties(cx, obj, length, length + 1))
                 return JS_FALSE;
             return JS_TRUE;
         }
 
         /* Get the to-be-deleted property's value into vp ASAP. */
         JSBool hole;
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -100,16 +100,17 @@ JSObject::ensureDenseArrayElements(JSCon
     JS_ASSERT_IF(!cx->typeInferenceEnabled(), currentCapacity == initLength);
 
     uintN requiredCapacity;
     if (extra == 1) {
         /* Optimize for the common case. */
         if (index < initLength)
             return ED_OK;
         if (index < currentCapacity) {
+            JS_ASSERT(cx->typeInferenceEnabled());
             if (index > initLength) {
                 if (!setDenseArrayNotPacked(cx))
                     return ED_FAILED;
                 ClearValueRange(getSlots() + initLength, index - initLength, true);
             }
             setDenseArrayInitializedLength(index + 1);
             return ED_OK;
         }
@@ -122,16 +123,17 @@ JSObject::ensureDenseArrayElements(JSCon
         requiredCapacity = index + extra;
         if (requiredCapacity < index) {
             /* Overflow. */
             return ED_SPARSE;
         }
         if (requiredCapacity <= initLength)
             return ED_OK;
         if (requiredCapacity <= currentCapacity) {
+            JS_ASSERT(cx->typeInferenceEnabled());
             if (index > initLength) {
                 ClearValueRange(getSlots() + initLength, index - initLength, true);
                 if (!setDenseArrayNotPacked(cx))
                     return ED_FAILED;
             }
             setDenseArrayInitializedLength(requiredCapacity);
             return ED_OK;
         }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4219,18 +4219,20 @@ JSObject::growSlots(JSContext *cx, size_
         return allocSlots(cx, actualCapacity);
 
     Value *tmpslots = (Value*) cx->realloc_(slots, oldcap * sizeof(Value), actualCapacity * sizeof(Value));
     if (!tmpslots)
         return false;    /* Leave dslots as its old size. */
     slots = tmpslots;
     capacity = actualCapacity;
 
-    /* Initialize the additional slots we added. */
-    ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray());
+    if (!isDenseArray()) {
+        /* Initialize the additional slots we added. This is not required for dense arrays. */
+        ClearValueRange(slots + oldcap, actualCapacity - oldcap, false);
+    }
     return true;
 }
 
 void
 JSObject::shrinkSlots(JSContext *cx, size_t newcap)
 {
     uint32 oldcap = numSlots();
     JS_ASSERT(newcap <= oldcap);
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -742,17 +742,17 @@ FrameState::computeAllocation(jsbytecode
             continue;
         FrameEntry *fe = regstate(reg).fe();
         if (fe == callee_)
             continue;
         if (fe < spBase && !variableLive(fe, target))
             continue;
         if (fe >= spBase && !isTemporary(fe))
             continue;
-        if (isTemporary(fe) && target - script->code > loop->backedgeOffset())
+        if (isTemporary(fe) && uint32(target - script->code) > loop->backedgeOffset())
             continue;
         alloc->set(reg, indexOfFe(fe), fe->data.synced());
     }
 
 #ifdef DEBUG
     if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
         JaegerSpew(JSpew_Regalloc, "allocation at %u:", target - script->code);
         dumpAllocation(alloc);