[JAEGER] Merge from tracemonkey.
authorDavid Mandelin <dmandelin@mozilla.com>
Fri, 23 Jul 2010 18:47:24 -0700
changeset 53161 03307f6a51528d98327fc9183973953fb31c4054
parent 53160 f0799817ddbaeee5d15a1cacef62bb106e80034d (current diff)
parent 48555 0a242b1501168ef0f40aa903b212e3bd0a8613f9 (diff)
child 53166 cb49590b7f0ca3604e0c4b4d33743a8019eab7c1
push id15660
push userrsayre@mozilla.com
push dateSat, 11 Sep 2010 19:16:24 +0000
treeherdermozilla-central@f1bd314e64ac [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b2pre
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
[JAEGER] Merge from tracemonkey.
content/base/src/nsFrameMessageManager.cpp
dom/base/nsJSEnvironment.cpp
js/src/ctypes/CTypes.cpp
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsbuiltins.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsemit.cpp
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jslock.cpp
js/src/jslock.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsopcode.cpp
js/src/jsparse.cpp
js/src/jspubtd.h
js/src/jsregexp.cpp
js/src/jsregexp.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsstr.cpp
js/src/jsstr.h
js/src/jstracer.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/StubCalls.cpp
modules/plugin/base/src/nsNPAPIPlugin.cpp
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -217,36 +217,30 @@ nsFrameMessageManager::SendSyncMessage()
       NS_ENSURE_STATE(ncc);
 
       JSContext* ctx = nsnull;
       rv = ncc->GetJSContext(&ctx);
       NS_ENSURE_SUCCESS(rv, rv);
       JSAutoRequest ar(ctx);
 
       PRUint32 len = retval.Length();
-      jsval* dest = nsnull;
-      JSObject* dataArray = js_NewArrayObjectWithCapacity(ctx, len, &dest);
+      JSObject* dataArray = JS_NewArrayObject(ctx, len, NULL);
       NS_ENSURE_TRUE(dataArray, NS_ERROR_OUT_OF_MEMORY);
-      nsAutoGCRoot arrayGCRoot(&dataArray, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
 
       for (PRUint32 i = 0; i < len; ++i) {
-        dest[i] = JSVAL_NULL;
         if (!retval[i].Length())
           continue;
 
         jsval ret = JSVAL_VOID;
-        nsAutoGCRoot root(&ret, &rv);
-        NS_ENSURE_SUCCESS(rv, rv);
         JSONParser* parser = JS_BeginJSONParse(ctx, &ret);
         JSBool ok = JS_ConsumeJSONText(ctx, parser, (jschar*)retval[i].get(),
                                        (uint32)retval[i].Length());
         ok = JS_FinishJSONParse(ctx, parser, JSVAL_NULL) && ok;
         if (ok) {
-          dest[i] = ret;
+          NS_ENSURE_TRUE(JS_SetElement(ctx, dataArray, i, &ret), NS_ERROR_OUT_OF_MEMORY);
         }
       }
 
       jsval* retvalPtr;
       ncc->GetRetValPtr(&retvalPtr);
       *retvalPtr = OBJECT_TO_JSVAL(dataArray);
       ncc->SetReturnValueWasSet(PR_TRUE);
     }
@@ -345,20 +339,19 @@ nsFrameMessageManager::ReceiveMessage(ns
         NS_ENSURE_SUCCESS(rv, rv);
         nsContentUtils::WrapNative(ctx,
                                    JS_GetGlobalObject(ctx),
                                    aTarget, &targetv);
 
         // To keep compatibility with e10s message manager,
         // define empty objects array.
         if (!aObjectsArray) {
-          jsval* dest = nsnull;
           // Because we want JS messages to have always the same properties,
           // create array even if len == 0.
-          aObjectsArray = js_NewArrayObjectWithCapacity(ctx, 0, &dest);
+          aObjectsArray = JS_NewArrayObject(ctx, 0, NULL);
           if (!aObjectsArray) {
             return false;
           }
         }
         nsAutoGCRoot arrayGCRoot(&aObjectsArray, &rv);
         NS_ENSURE_SUCCESS(rv, rv);
 
         jsval json = JSVAL_NULL;
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -3820,24 +3820,16 @@ nsCanvasRenderingContext2D::AsyncDrawXUL
 
     return DrawWindow(window, aX, aY, aW, aH, aBGColor, flags);
 #endif
 }
 
 //
 // device pixel getting/setting
 //
-extern "C" {
-#include "jstypes.h"
-JS_FRIEND_API(JSBool)
-js_CoerceArrayToCanvasImageData(JSObject *obj, jsuint offset, jsuint count,
-                                JSUint8 *dest);
-JS_FRIEND_API(JSObject *)
-js_NewArrayObjectWithCapacity(JSContext *cx, jsuint capacity, jsval **vector);
-}
 
 void
 nsCanvasRenderingContext2D::EnsureUnpremultiplyTable() {
   if (sUnpremultiplyTable)
     return;
 
   // Infallably alloc the unpremultiply table.
   sUnpremultiplyTable = new PRUint8[256][256];
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -898,30 +898,16 @@ PrintWinCodebase(nsGlobalWindow *win)
 
 void
 DumpString(const nsAString &str)
 {
   printf("%s\n", NS_ConvertUTF16toUTF8(str).get());
 }
 #endif
 
-static void
-MaybeGC(JSContext *cx)
-{
-  size_t bytes = cx->runtime->gcBytes;
-  size_t lastBytes = cx->runtime->gcLastBytes;
-  if ((bytes > 8192 && bytes > lastBytes * 16)
-#ifdef DEBUG
-      || cx->runtime->gcZeal > 0
-#endif
-      ) {
-    JS_GC(cx);
-  }
-}
-
 static already_AddRefed<nsIPrompt>
 GetPromptFromContext(nsJSContext* ctx)
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(ctx->GetGlobalObject()));
   NS_ENSURE_TRUE(win, nsnull);
 
   nsIDocShell *docShell = win->GetDocShell();
   NS_ENSURE_TRUE(docShell, nsnull);
@@ -950,17 +936,17 @@ nsJSContext::DOMOperationCallback(JSCont
 
   // XXX Save the operation callback time so we can restore it after the GC,
   // because GCing can cause JS to run on our context, causing our
   // ScriptEvaluated to be called, and clearing our operation callback time.
   // See bug 302333.
   PRTime callbackTime = ctx->mOperationCallbackTime;
   PRTime modalStateTime = ctx->mModalStateTime;
 
-  MaybeGC(cx);
+  JS_MaybeGC(cx);
 
   // Now restore the callback time and count, in case they got reset.
   ctx->mOperationCallbackTime = callbackTime;
   ctx->mModalStateTime = modalStateTime;
 
   // Check to see if we are running OOM
   nsCOMPtr<nsIMemory> mem;
   NS_GetMemoryManager(getter_AddRefs(mem));
@@ -2179,16 +2165,18 @@ nsJSContext::CallEventHandler(nsISupport
 
   if (!mScriptsEnabled) {
     return NS_OK;
   }
 
   NS_TIME_FUNCTION_FMT(1.0, "%s (line %d) (function: %s)", MOZ_FUNCTION_NAME,
                        __LINE__, JS_GetFunctionName(static_cast<JSFunction *>(JS_GetPrivate(mContext, static_cast<JSObject *>(aHandler)))));
 
+ 
+  JSAutoRequest ar(mContext);
   JSObject* target = nsnull;
   nsresult rv = JSObjectFromInterface(aTarget, aScope, &target);
   NS_ENSURE_SUCCESS(rv, rv);
 
   js::AutoObjectRooter targetVal(mContext, target);
   jsval rval = JSVAL_VOID;
 
   // This one's a lot easier than EvaluateString because we don't have to
@@ -2222,17 +2210,16 @@ nsJSContext::CallEventHandler(nsISupport
     rv = ConvertSupportsTojsvals(aargv, target, &argc,
                                  &argv, poolRelease, tvr);
     if (NS_FAILED(rv)) {
       stack->Pop(nsnull);
       return rv;
     }
 
     jsval funval = OBJECT_TO_JSVAL(static_cast<JSObject *>(aHandler));
-    JSAutoRequest ar(mContext);
     JSAutoCrossCompartmentCall accc;
     if (!accc.enter(mContext, target)) {
       stack->Pop(nsnull);
       return NS_ERROR_FAILURE;
     }
 
     ++mExecuteDepth;
     PRBool ok = ::JS_CallFunctionValue(mContext, target,
@@ -2254,17 +2241,16 @@ nsJSContext::CallEventHandler(nsISupport
     }
   }
 
   if (NS_FAILED(stack->Pop(nsnull)))
     return NS_ERROR_FAILURE;
 
   // Convert to variant before calling ScriptEvaluated, as it may GC, meaning
   // we would need to root rval.
-  JSAutoRequest ar(mContext);
   if (NS_SUCCEEDED(rv)) {
     if (rval == JSVAL_NULL)
       *arv = nsnull;
     else
       rv = nsContentUtils::XPConnect()->JSToVariant(mContext, rval, arv);
   }
 
   // ScriptEvaluated needs to come after we pop the stack
@@ -3518,22 +3504,22 @@ nsJSContext::ScriptEvaluated(PRBool aTer
     }
     delete start;
   }
 
   mNumEvaluations++;
 
 #ifdef JS_GC_ZEAL
   if (mContext->runtime->gcZeal >= 2) {
-    MaybeGC(mContext);
+    JS_MaybeGC(mContext);
   } else
 #endif
   if (mNumEvaluations > 20) {
     mNumEvaluations = 0;
-    MaybeGC(mContext);
+    JS_MaybeGC(mContext);
   }
 
   if (aTerminated) {
     mOperationCallbackTime = 0;
     mModalStateTime = 0;
   }
 }
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -519,30 +519,24 @@ TabParent::ReceiveMessage(const nsString
   nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner =
     do_QueryInterface(mFrameElement);
   if (frameLoaderOwner) {
     nsRefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
     if (frameLoader && frameLoader->GetFrameMessageManager()) {
       nsFrameMessageManager* manager = frameLoader->GetFrameMessageManager();
       JSContext* ctx = manager->GetJSContext();
       JSAutoRequest ar(ctx);
-      jsval* dest;
       PRUint32 len = 0; //TODO: obtain a real value in bug 572685
       // Because we want JS messages to have always the same properties,
       // create array even if len == 0.
-      JSObject* objectsArray =
-        js_NewArrayObjectWithCapacity(ctx, len, &dest);
+      JSObject* objectsArray = JS_NewArrayObject(ctx, len, NULL);
       if (!objectsArray) {
         return false;
       }
 
-      nsresult rv = NS_OK;
-      nsAutoGCRoot arrayGCRoot(&objectsArray, &rv);
-      NS_ENSURE_SUCCESS(rv, false);
-      
       manager->ReceiveMessage(mFrameElement,
                               aMessage,
                               aSync,
                               aJSON,
                               objectsArray,
                               aJSONRetVal);
     }
   }
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -3906,26 +3906,25 @@ ExtractStructField(JSContext* cx, jsval 
   if (!CType::GetSafeSize(cx, *typeObj, &size) || size == 0) {
     JS_ReportError(cx, "struct field types must have defined and nonzero size");
     return NULL;
   }
 
   return name;
 }
 
-// For a struct field with 'name' and 'type', add an element to field
-// descriptor array 'arrayObj' of the form { name : type }.
+// For a struct field with 'name' and 'type', add an element of the form
+// { name : type }.
 static JSBool
 AddFieldToArray(JSContext* cx,
-                JSObject* arrayObj,
                 jsval* element,
                 JSString* name,
                 JSObject* typeObj)
 {
-  JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, arrayObj);
+  JSObject* fieldObj = JS_NewObject(cx, NULL, NULL, NULL);
   if (!fieldObj)
     return false;
 
   *element = OBJECT_TO_JSVAL(fieldObj);
 
   if (!JS_DefineUCProperty(cx, fieldObj,
          name->chars(), name->length(),
          OBJECT_TO_JSVAL(typeObj), NULL, NULL,
@@ -4320,32 +4319,32 @@ StructType::BuildFieldsArray(JSContext* 
   JS_ASSERT(CType::IsCType(cx, obj));
   JS_ASSERT(CType::GetTypeCode(cx, obj) == TYPE_struct);
   JS_ASSERT(CType::IsSizeDefined(cx, obj));
 
   const FieldInfoHash* fields = GetFieldInfo(cx, obj);
   size_t len = fields->count();
 
   // Prepare a new array for the 'fields' property of the StructType.
-  jsval* fieldsVec;
-  JSObject* fieldsProp =
-    js_NewArrayObjectWithCapacity(cx, len, &fieldsVec);
-  if (!fieldsProp)
+  Array<jsval, 16> fieldsVec;
+  if (!fieldsVec.resize(len))
     return NULL;
-  js::AutoObjectRooter root(cx, fieldsProp);
-  JS_ASSERT(len == 0 || fieldsVec);
 
   for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
     const FieldInfoHash::Entry& entry = r.front();
     // Add the field descriptor to the array.
-    if (!AddFieldToArray(cx, fieldsProp, &fieldsVec[entry.value.mIndex],
-           entry.key, entry.value.mType))
+    if (!AddFieldToArray(cx, &fieldsVec[entry.value.mIndex],
+                         entry.key, entry.value.mType))
       return NULL;
   }
 
+  JSObject* fieldsProp = JS_NewArrayObject(cx, len, fieldsVec.begin());
+  if (!fieldsProp)
+    return NULL;
+
   // Seal the fields array.
   if (!JS_SealObject(cx, fieldsProp, JS_FALSE))
     return NULL;
 
   return fieldsProp;
 }
 
 JSBool
@@ -5034,27 +5033,27 @@ FunctionType::ArgTypesGetter(JSContext* 
   ASSERT_OK(JS_GetReservedSlot(cx, obj, SLOT_ARGS_T, vp));
   if (!JSVAL_IS_VOID(*vp))
     return JS_TRUE;
 
   FunctionInfo* fninfo = GetFunctionInfo(cx, obj);
   size_t len = fninfo->mArgTypes.length();
 
   // Prepare a new array.
-  jsval* vec;
-  JSObject* argTypes =
-    js_NewArrayObjectWithCapacity(cx, len, &vec);
-  if (!argTypes)
+  Array<jsval, 16> vec;
+  if (!vec.resize(len))
     return JS_FALSE;
-  js::AutoObjectRooter argsroot(cx, argTypes);
-  JS_ASSERT(len == 0 || vec);
 
   for (size_t i = 0; i < len; ++i)
     vec[i] = OBJECT_TO_JSVAL(fninfo->mArgTypes[i]);
 
+  JSObject* argTypes = JS_NewArrayObject(cx, len, vec.begin());
+  if (!argTypes)
+    return JS_FALSE;
+
   // Seal and cache it.
   if (!JS_SealObject(cx, argTypes, JS_FALSE) ||
       !JS_SetReservedSlot(cx, obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes)))
     return JS_FALSE;
 
   *vp = OBJECT_TO_JSVAL(argTypes);
   return JS_TRUE;
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -116,16 +116,20 @@ JS_PUBLIC_DATA(jsid) JSID_VOID = { (size
 JS_PUBLIC_DATA(jsval) JSVAL_NULL  = { BUILD_JSVAL(JSVAL_TAG_NULL,      0) };
 JS_PUBLIC_DATA(jsval) JSVAL_ZERO  = { BUILD_JSVAL(JSVAL_TAG_INT32,     0) };
 JS_PUBLIC_DATA(jsval) JSVAL_ONE   = { BUILD_JSVAL(JSVAL_TAG_INT32,     1) };
 JS_PUBLIC_DATA(jsval) JSVAL_FALSE = { BUILD_JSVAL(JSVAL_TAG_BOOLEAN,   JS_FALSE) };
 JS_PUBLIC_DATA(jsval) JSVAL_TRUE  = { BUILD_JSVAL(JSVAL_TAG_BOOLEAN,   JS_TRUE) };
 JS_PUBLIC_DATA(jsval) JSVAL_VOID  = { BUILD_JSVAL(JSVAL_TAG_UNDEFINED, 0) };
 #endif
 
+/* Make sure that jschar is two bytes unsigned integer */
+JS_STATIC_ASSERT((jschar)-1 > 0);
+JS_STATIC_ASSERT(sizeof(jschar) == 2);
+
 JS_PUBLIC_API(int64)
 JS_Now()
 {
     return PRMJ_Now();
 }
 
 JS_PUBLIC_API(jsval)
 JS_GetNaNValue(JSContext *cx)
@@ -1840,17 +1844,17 @@ JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p)
 {
     return cx->free(p);
 }
 
 JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes)
 {
-    return cx->updateMallocCounter(nbytes);
+    cx->runtime->updateMallocCounter(nbytes);
 }
 
 JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s)
 {
     size_t n;
     void *p;
 
@@ -2436,75 +2440,26 @@ JS_GC(JSContext *cx)
 JS_PUBLIC_API(void)
 JS_MaybeGC(JSContext *cx)
 {
     JSRuntime *rt;
     uint32 bytes, lastBytes;
 
     rt = cx->runtime;
 
-#ifdef JS_GC_ZEAL
-    if (rt->gcZeal > 0) {
-        JS_GC(cx);
-        return;
-    }
-#endif
-
     bytes = rt->gcBytes;
     lastBytes = rt->gcLastBytes;
 
-    /*
-     * We run the GC if we used all available free GC cells and had to
-     * allocate extra 1/3 of GC arenas since the last run of GC, or if
-     * we have malloc'd more bytes through JS_malloc than we were told
-     * to allocate by JS_NewRuntime.
-     *
-     * The reason for
-     *   bytes > 4/3 lastBytes
-     * condition is the following. Bug 312238 changed bytes and lastBytes
-     * to mean the total amount of memory that the GC uses now and right
-     * after the last GC.
-     *
-     * Before the bug the variables meant the size of allocated GC things
-     * now and right after the last GC. That size did not include the
-     * memory taken by free GC cells and the condition was
-     *   bytes > 3/2 lastBytes.
-     * That is, we run the GC if we have half again as many bytes of
-     * GC-things as the last time we GC'd. To be compatible we need to
-     * express that condition through the new meaning of bytes and
-     * lastBytes.
-     *
-     * We write the original condition as
-     *   B*(1-F) > 3/2 Bl*(1-Fl)
-     * where B is the total memory size allocated by GC and F is the free
-     * cell density currently and Sl and Fl are the size and the density
-     * right after GC. The density by definition is memory taken by free
-     * cells divided by total amount of memory. In other words, B and Bl
-     * are bytes and lastBytes with the new meaning and B*(1-F) and
-     * Bl*(1-Fl) are bytes and lastBytes with the original meaning.
-     *
-     * Our task is to exclude F and Fl from the last statement. According
-     * to the stats from bug 331966 comment 23, Fl is about 10-25% for a
-     * typical run of the browser. It means that the original condition
-     * implied that we did not run GC unless we exhausted the pool of
-     * free cells. Indeed if we still have free cells, then B == Bl since
-     * we did not yet allocated any new arenas and the condition means
-     *   1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F
-     * That implies 3/2 Fl > 1/2 or Fl > 1/3. That cannot be fulfilled
-     * for the state described by the stats. So we can write the original
-     * condition as:
-     *   F == 0 && B > 3/2 Bl(1-Fl)
-     * Again using the stats we see that Fl is about 11% when the browser
-     * starts up and when we are far from hitting rt->gcMaxBytes. With
-     * this F we have
-     * F == 0 && B > 3/2 Bl(1-0.11)
-     * or approximately F == 0 && B > 4/3 Bl.
-     */
-    if ((bytes > 8192 && bytes > lastBytes + lastBytes / 3) ||
-        rt->isGCMallocLimitReached()) {
+    if (rt->gcIsNeeded ||
+#ifdef JS_GC_ZEAL
+        rt->gcZeal > 0 ||
+#endif
+        (bytes > 8192 && bytes > lastBytes * 16) ||
+        bytes >= rt->gcTriggerBytes ||
+        rt->overQuota()) {
         JS_GC(cx);
     }
 }
 
 JS_PUBLIC_API(JSGCCallback)
 JS_SetGCCallback(JSContext *cx, JSGCCallback cb)
 {
     CHECK_REQUEST(cx);
@@ -2615,17 +2570,17 @@ JS_NewExternalString(JSContext *cx, jsch
 {
     CHECK_REQUEST(cx);
     JS_ASSERT(uintN(type) < JS_EXTERNAL_STRING_LIMIT);
 
     JSString *str = js_NewGCExternalString(cx, uintN(type));
     if (!str)
         return NULL;
     str->initFlat(chars, length);
-    cx->updateMallocCounter((length + 1) * sizeof(jschar));
+    cx->runtime->updateMallocCounter((length + 1) * sizeof(jschar));
     return str;
 }
 
 JS_PUBLIC_API(intN)
 JS_GetExternalStringGCType(JSRuntime *rt, JSString *str)
 {
     /*
      * No need to test this in js_GetExternalStringGCType, which asserts its
@@ -2768,17 +2723,17 @@ JS_InstanceOf(JSContext *cx, JSObject *o
     assertSameCompartment(cx, obj);
     return InstanceOf(cx, obj, Valueify(clasp), Valueify(argv));
 }
 
 JS_PUBLIC_API(JSBool)
 JS_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 {
     assertSameCompartment(cx, obj, v);
-    return js_HasInstance(cx, obj, Valueify(&v), bp);
+    return HasInstance(cx, obj, Valueify(&v), bp);
 }
 
 JS_PUBLIC_API(void *)
 JS_GetPrivate(JSContext *cx, JSObject *obj)
 {
     assertSameCompartment(cx, obj);
     return obj->getPrivate();
 }
@@ -4849,23 +4804,31 @@ JS_GetOperationCallback(JSContext *cx)
 {
     return cx->operationCallback;
 }
 
 JS_PUBLIC_API(void)
 JS_TriggerOperationCallback(JSContext *cx)
 {
     /*
-     * Use JS_ATOMIC_SET_MASK in the hope that it will make sure the write
-     * will become immediately visible to other processors polling
-     * cx->interruptFlag. Note that we only care about visibility here,
-     * not read/write ordering.
+     * We allow for cx to come from another thread. Thus we must deal with
+     * possible JS_ClearContextThread calls when accessing cx->thread. But we
+     * assume that the calling thread is in a request so JSThread cannot be
+     * GC-ed.
      */
-    JS_ATOMIC_SET_MASK(const_cast<jsword*>(&cx->interruptFlags),
-                       JSContext::INTERRUPT_OPERATION_CALLBACK);
+    JSThreadData *td;
+#ifdef JS_THREADSAFE
+    JSThread *thread = cx->thread;
+    if (!thread)
+        return;
+    td = &thread->data;
+#else
+    td = JS_THREAD_DATA(cx);
+#endif
+    td->triggerOperationCallback();
 }
 
 JS_PUBLIC_API(void)
 JS_TriggerAllOperationCallbacks(JSRuntime *rt)
 {
     js_TriggerAllOperationCallbacks(rt, JS_FALSE);
 }
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -43,30 +43,22 @@
  *
  * Array objects begin as "dense" arrays, optimized for index-only property
  * access over a vector of slots with high load factor.  Array methods
  * optimize for denseness by testing that the object's class is
  * &js_ArrayClass, and can then directly manipulate the slots for efficiency.
  *
  * We track these pieces of metadata for arrays in dense mode:
  *  - The array's length property as a uint32, accessible with
- *    getArrayLength(), setDenseArrayLength().
- *  - The number of indices that are filled (non-holes), accessible with
- *    {get,set}DenseArrayCount().
+ *    getArrayLength(), setArrayLength().
  *  - The number of element slots (capacity), gettable with
  *    getDenseArrayCapacity().
- *  - The minimum of length and capacity (minLenCap).  There are no explicit
- *    setters, it's updated automatically by setDenseArrayLength() and
- *    setDenseArrayCapacity().  There are also no explicit getters, the only
- *    user is TraceRecorder which can access it directly because it's a
- *    friend.  The function isDenseArrayMinLenCapOk() checks that it is set
- *    correctly;  a call to it should be put in an assertion at use points.
  *
  * In dense mode, holes in the array are represented by (JS_ARRAY_HOLE) invalid
- * values.  The final slot in fslots is unused.
+ * values.  The final two slot in fslots are unused.
  *
  * NB: the capacity and length of a dense array are entirely unrelated!  The
  * length may be greater than, less than, or equal to the capacity.  See
  * array_length_setter for an explanation of how the first, most surprising
  * case may occur.
  *
  * Arrays are converted to use js_SlowArrayClass when any of these conditions
  * are met:
@@ -126,23 +118,36 @@ using namespace js;
 
 /* Iteration depends on all indexes of a dense array to fit into a JSVAL-sized int. */
 static inline bool
 INDEX_TOO_BIG(jsuint index)
 {
     return index > JS_BIT(29) - 1;
 }
 
-#define INDEX_TOO_SPARSE(array, index)                                           \
-    (INDEX_TOO_BIG(index) ||                                                     \
-     ((index) > array->getDenseArrayCapacity() && (index) >= MIN_SPARSE_INDEX && \
-      (index) > ((array)->getDenseArrayCount() + 1) * 4))
-
-#define ENSURE_SLOW_ARRAY(cx, obj)                                             \
-    (obj->getClass() == &js_SlowArrayClass || obj->makeDenseArraySlow(cx))
+static inline  bool
+INDEX_TOO_SPARSE(JSObject *array, jsuint index)
+{
+    /* Small arrays with less than 256 elements are dense, no matter what. */
+    if (index < 256)
+        return false;
+
+    /*
+     * Otherwise if the index becomes too large or is more than 256 past
+     * the current capacity, we have to slowify.
+     */
+    return INDEX_TOO_BIG(index) || (index > array->getDenseArrayCapacity() + 256);
+}
+
+static inline bool
+ENSURE_SLOW_ARRAY(JSContext *cx, JSObject *obj)
+{
+    return obj->getClass() == &js_SlowArrayClass ||
+           obj->makeDenseArraySlow(cx);
+}
 
 /*
  * Determine if the id represents an array index or an XML property index.
  *
  * An id is an array index according to ECMA by (15.4):
  *
  * "Array objects give special treatment to a certain class of property names.
  * A property name P (in the form of a string value) is an array index if and
@@ -299,67 +304,64 @@ BigIndexToId(JSContext *cx, JSObject *ob
             return JS_FALSE;
     }
 
     *idp = ATOM_TO_JSID(atom);
     return JS_TRUE;
 }
 
 bool
-JSObject::resizeDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap,
-                                   bool initializeAllSlots)
+JSObject::growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap)
 {
     JS_ASSERT(isDenseArray());
-
-    if (newcap == 0) {
-        freeDenseArrayElements(cx);
-        return JS_TRUE;
-    }
+    JS_ASSERT(newcap >= ARRAY_CAPACITY_MIN);
+    JS_ASSERT(newcap >= oldcap);
 
     if (newcap > MAX_DSLOTS_LENGTH32) {
-        js_ReportAllocationOverflow(cx);
+        if (!JS_ON_TRACE(cx))
+            js_ReportAllocationOverflow(cx);
         return JS_FALSE;
     }
 
+    /* dslots can be briefly NULL during array creation */
     Value *slots = dslots ? dslots - 1 : NULL;
     Value *newslots = (Value *) cx->realloc(slots, (size_t(newcap) + 1) * sizeof(Value));
     if (!newslots)
         return false;
 
     dslots = newslots + 1;
     setDenseArrayCapacity(newcap);
 
-    if (initializeAllSlots) {
-        Value *base = addressOfDenseArrayElement(0);
-        for (Value *vp = base + oldcap, *end = base + newcap; vp < end; ++vp)
-            vp->setMagic(JS_ARRAY_HOLE);
-    }
+    Value *base = addressOfDenseArrayElement(0);
+    for (Value *vp = base + oldcap, *end = base + newcap; vp < end; ++vp)
+        vp->setMagic(JS_ARRAY_HOLE);
 
     return true;
 }
 
 bool
-JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap, bool initializeAllSlots)
+JSObject::ensureDenseArrayElements(JSContext *cx, uint32 newcap)
 {
     /*
      * When a dense array with CAPACITY_DOUBLING_MAX or fewer slots needs to
      * grow, double its capacity, to push() N elements in amortized O(N) time.
      *
      * Above this limit, grow by 12.5% each time. Speed is still amortized
      * O(N), with a higher constant factor, and we waste less space.
      */
     static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
 
     /*
      * Round up all large allocations to a multiple of this (1MB), so as not
      * to waste space if malloc gives us 1MB-sized chunks (as jemalloc does).
      */
     static const size_t CAPACITY_CHUNK = 1024 * 1024 / sizeof(Value);
 
-    uint32 oldcap = getDenseArrayCapacity();
+    /* While creating arrays, dslots can be NULL. */
+    uint32 oldcap = dslots ? getDenseArrayCapacity() : 0;
 
     if (newcap > oldcap) {
         /*
          * If this overflows uint32, newcap is very large. nextsize will end
          * up being less than newcap, the code below will thus disregard it,
          * and resizeDenseArrayElements() will fail.
          *
          * The way we use dslots[-1] forces a few +1s and -1s here. For
@@ -372,31 +374,49 @@ JSObject::ensureDenseArrayElements(JSCon
                           : oldcap + (oldcap >> 3);
 
         uint32 actualCapacity = JS_MAX(newcap, nextsize);
         if (actualCapacity >= CAPACITY_CHUNK)
             actualCapacity = JS_ROUNDUP(actualCapacity + 1, CAPACITY_CHUNK) - 1; /* -1 for dslots[-1] */
         else if (actualCapacity < ARRAY_CAPACITY_MIN)
             actualCapacity = ARRAY_CAPACITY_MIN;
 
-        if (!resizeDenseArrayElements(cx, oldcap, actualCapacity, initializeAllSlots))
+        if (!growDenseArrayElements(cx, oldcap, actualCapacity))
             return false;
-
-        if (!initializeAllSlots) {
-            /*
-             * Initialize the slots caller didn't actually ask for.
-             */
-            for (uint32 i = newcap; i < actualCapacity; i++) {
-                setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
-            }
-        }
     }
     return true;
 }
 
+bool
+JSObject::shrinkDenseArrayElements(JSContext *cx, uint32 newcap)
+{
+    JS_ASSERT(isDenseArray());
+    JS_ASSERT(newcap < getDenseArrayCapacity());
+    JS_ASSERT(dslots);
+
+    uint32 fill = newcap;
+
+    if (newcap < ARRAY_CAPACITY_MIN)
+        newcap = ARRAY_CAPACITY_MIN;
+
+    Value *newslots = (Value *) cx->realloc(dslots - 1, (size_t(newcap) + 1) * sizeof(Value));
+    if (!newslots)
+        return false;
+
+    dslots = newslots + 1;
+    setDenseArrayCapacity(newcap);
+
+    /* we refuse to shrink below a minimum value, so we have to clear the excess space */
+    Value *base = addressOfDenseArrayElement(0);
+    while (fill < newcap)
+        base[fill++].setMagic(JS_ARRAY_HOLE);
+
+    return true;
+}
+
 static bool
 ReallyBigIndexToId(JSContext* cx, jsdouble index, jsid* idp)
 {
     return js_ValueToStringId(cx, DoubleValue(index), idp);
 }
 
 static bool
 IndexToId(JSContext* cx, JSObject* obj, jsdouble index, JSBool* hole, jsid* idp,
@@ -473,19 +493,17 @@ SetArrayElement(JSContext *cx, JSObject 
         /* Predicted/prefetched code should favor the remains-dense case. */
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
             if (!INDEX_TOO_SPARSE(obj, idx)) {
                 JS_ASSERT(idx + 1 > idx);
                 if (!obj->ensureDenseArrayElements(cx, idx + 1))
                     return JS_FALSE;
                 if (idx >= obj->getArrayLength())
-                    obj->setDenseArrayLength(idx + 1);
-                if (obj->getDenseArrayElement(idx).isMagic(JS_ARRAY_HOLE))
-                    obj->incDenseArrayCountBy(1);
+                    obj->setArrayLength(idx + 1);
                 obj->setDenseArrayElement(idx, v);
                 return JS_TRUE;
             }
         }
 
         if (!obj->makeDenseArraySlow(cx))
             return JS_FALSE;
     }
@@ -502,19 +520,17 @@ SetArrayElement(JSContext *cx, JSObject 
 
 static JSBool
 DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index)
 {
     JS_ASSERT(index >= 0);
     if (obj->isDenseArray()) {
         if (index <= jsuint(-1)) {
             jsuint idx = jsuint(index);
-            if (!INDEX_TOO_SPARSE(obj, idx) && idx < obj->getDenseArrayCapacity()) {
-                if (!obj->getDenseArrayElement(idx).isMagic(JS_ARRAY_HOLE))
-                    obj->decDenseArrayCountBy(1);
+            if (idx < obj->getDenseArrayCapacity()) {
                 obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE));
                 return JS_TRUE;
             }
         }
         return JS_TRUE;
     }
 
     AutoIdRooter idr(cx);
@@ -618,36 +634,38 @@ array_length_setter(JSContext *cx, JSObj
         return false;
     oldlen = obj->getArrayLength();
 
     if (oldlen == newlen)
         return true;
 
     vp->setNumber(newlen);
     if (oldlen < newlen) {
-        if (obj->isDenseArray())
-            obj->setDenseArrayLength(newlen);
-        else
-            obj->setSlowArrayLength(newlen);
+        obj->setArrayLength(newlen);
         return true;
     }
 
     if (obj->isDenseArray()) {
-        /* Don't reallocate if we're not actually shrinking our slots. */
-        jsuint capacity = obj->getDenseArrayCapacity();
-        if (capacity > newlen && !obj->resizeDenseArrayElements(cx, capacity, newlen))
+        /*
+         * Don't reallocate if we're not actually shrinking our slots. If we do
+         * shrink slots here, resizeDenseArrayElements will fill all slots to the
+         * right of newlen with JS_ARRAY_HOLE. This permits us to disregard
+         * length when reading from arrays as long we are within the capacity.
+         */
+        jsuint oldcap = obj->getDenseArrayCapacity();
+        if (oldcap > newlen && !obj->shrinkDenseArrayElements(cx, newlen))
             return false;
-        obj->setDenseArrayLength(newlen);
+        obj->setArrayLength(newlen);
     } else if (oldlen - newlen < (1 << 24)) {
         do {
             --oldlen;
             if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, oldlen))
                 return false;
         } while (oldlen != newlen);
-        obj->setSlowArrayLength(newlen);
+        obj->setArrayLength(newlen);
     } else {
         /*
          * We are going to remove a lot of indexes in a presumably sparse
          * array. So instead of looping through indexes between newlen and
          * oldlen, we iterate through all properties and remove those that
          * correspond to indexes in the half-open range [newlen, oldlen).  See
          * bug 322135.
          */
@@ -664,17 +682,17 @@ array_length_setter(JSContext *cx, JSObj
                 return false;
             if (JSID_IS_VOID(id))
                 break;
             if (js_IdIsIndex(id, &index) && index - newlen < gap &&
                 !obj->deleteProperty(cx, id, &junk)) {
                 return false;
             }
         }
-        obj->setSlowArrayLength(newlen);
+        obj->setArrayLength(newlen);
     }
 
     return true;
 }
 
 /*
  * We have only indexed properties up to capacity (excepting holes), plus the
  * length property. For all else, we delegate to the prototype.
@@ -781,17 +799,17 @@ static JSBool
 slowarray_addProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     jsuint index, length;
 
     if (!js_IdIsIndex(id, &index))
         return JS_TRUE;
     length = obj->getArrayLength();
     if (index >= length)
-        obj->setSlowArrayLength(index + 1);
+        obj->setArrayLength(index + 1);
     return JS_TRUE;
 }
 
 static JSType
 array_typeOf(JSContext *cx, JSObject *obj)
 {
     return JSTYPE_OBJECT;
 }
@@ -812,19 +830,17 @@ array_setProperty(JSContext *cx, JSObjec
             return JS_FALSE;
         return js_SetProperty(cx, obj, id, vp);
     }
 
     if (!obj->ensureDenseArrayElements(cx, i + 1))
         return JS_FALSE;
 
     if (i >= obj->getArrayLength())
-        obj->setDenseArrayLength(i + 1);
-    if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE))
-        obj->incDenseArrayCountBy(1);
+        obj->setArrayLength(i + 1);
     obj->setDenseArrayElement(i, *vp);
     return JS_TRUE;
 }
 
 JSBool
 js_PrototypeHasIndexedProperties(JSContext *cx, JSObject *obj)
 {
     /*
@@ -874,18 +890,17 @@ dense_grow(JSContext* cx, JSObject* obj,
     if ((u >= capacity) && (INDEX_TOO_SPARSE(obj, u) || !obj->ensureDenseArrayElements(cx, u + 1)))
         return JS_FALSE;
 
     if (obj->getDenseArrayElement(u).isMagic()) {
         if (js_PrototypeHasIndexedProperties(cx, obj))
             return JS_FALSE;
 
         if (u >= obj->getArrayLength())
-            obj->setDenseArrayLength(u + 1);
-        obj->incDenseArrayCountBy(1);
+            obj->setArrayLength(u + 1);
     }
 
     obj->setDenseArrayElement(u, v);
     return JS_TRUE;
 }
 
 JSBool FASTCALL
 js_Array_dense_setelem(JSContext* cx, JSObject* obj, jsint i, ValueArgType v)
@@ -957,21 +972,18 @@ array_deleteProperty(JSContext *cx, JSOb
     if (!obj->isDenseArray())
         return js_DeleteProperty(cx, obj, id, rval);
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
         rval->setBoolean(false);
         return JS_TRUE;
     }
 
-    if (js_IdIsIndex(id, &i) && i < obj->getDenseArrayCapacity() &&
-        !obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
-        obj->decDenseArrayCountBy(1);
+    if (js_IdIsIndex(id, &i) && i < obj->getDenseArrayCapacity())
         obj->setDenseArrayElement(i, MagicValue(JS_ARRAY_HOLE));
-    }
 
     if (!js_SuppressDeletedProperty(cx, obj, id))
         return false;
 
     rval->setBoolean(true);
     return JS_TRUE;
 }
 
@@ -982,19 +994,35 @@ array_finalize(JSContext *cx, JSObject *
 }
 
 static void
 array_trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isDenseArray());
     obj->traceProtoAndParent(trc);
 
+    if (!obj->dslots)
+        return;
+
+    size_t holes = 0;
     uint32 capacity = obj->getDenseArrayCapacity();
-    for (uint32 i = 0; i < capacity; i++)
-        MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
+    for (uint32 i = 0; i < capacity; i++) {
+        Value v = obj->getDenseArrayElement(i);
+        if (v.isMagic(JS_ARRAY_HOLE))
+            ++holes;
+        else
+            MarkValue(trc, obj->getDenseArrayElement(i), "dense_array_elems");
+    }
+
+    if (trc == trc->context->runtime->gcMarkingTracer &&
+        holes > MIN_SPARSE_INDEX &&
+        holes > capacity / 4 * 3) {
+        /* This might fail, in which case we don't slowify it. */
+        reinterpret_cast<JSGCTracer *>(trc)->arraysToSlowify.append(obj);
+    }
 }
 
 extern JSObjectOps js_ArrayObjectOps;
 
 static const JSObjectMap SharedArrayMap(&js_ArrayObjectOps, JSObjectMap::SHAPELESS);
 
 JSObjectOps js_ArrayObjectOps = {
     &SharedArrayMap,
@@ -1059,17 +1087,18 @@ JSObject::makeDenseArraySlow(JSContext *
         /* arrayProto is Array.prototype. */
         JS_ASSERT(arrayProto->getClass() == &js_SlowArrayClass);
         emptyShape = arrayProto->scope()->emptyScope->shape;
     }
     JSScope *scope = JSScope::create(cx, &js_ObjectOps, &js_SlowArrayClass, obj, emptyShape);
     if (!scope)
         return JS_FALSE;
 
-    uint32 capacity = obj->getDenseArrayCapacity();
+    /* For a brief moment the class object has NULL dslots until we slowify it during construction. */
+    uint32 capacity = dslots ? obj->getDenseArrayCapacity() : 0;
     if (capacity) {
         scope->freeslot = obj->numSlots() + JS_INITIAL_NSLOTS;
         // XXX: changing the capacity like this is awful.  Bug 558263 will remove
         // the need for this.
         obj->setDenseArrayCapacity(JS_INITIAL_NSLOTS + capacity);
     } else {
         scope->freeslot = obj->numSlots();
     }
@@ -1376,87 +1405,38 @@ array_toLocaleString(JSContext *cx, uint
 
     /*
      *  Passing comma here as the separator. Need a way to get a
      *  locale-specific version.
      */
     return array_toString_sub(cx, obj, JS_TRUE, NULL, vp);
 }
 
-enum TargetElementsType {
-    TargetElementsAllHoles,
-    TargetElementsMayContainValues
-};
-
-enum SourceVectorType {
-    SourceVectorAllValues,
-    SourceVectorMayContainHoles
-};
-
 static JSBool
-InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector,
-                  TargetElementsType targetType, SourceVectorType vectorType)
+InitArrayElements(JSContext *cx, JSObject *obj, jsuint start, jsuint count, Value *vector)
 {
     JS_ASSERT(count < MAXINDEX);
 
     /*
      * Optimize for dense arrays so long as adding the given set of elements
      * wouldn't otherwise make the array slow.
      */
     if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
         start <= MAXINDEX - count && !INDEX_TOO_BIG(start + count)) {
 
-#ifdef DEBUG_jwalden
-        {
-            /* Verify that overwriteType and writeType were accurate. */
-            AutoIdRooter idr(cx);
-            for (jsuint i = 0; i < count; i++) {
-                JS_ASSERT_IF(vectorType == SourceVectorAllValues, !vector[i].isMagic(JS_ARRAY_HOLE));
-
-                jsdouble index = jsdouble(start) + i;
-                if (targetType == TargetElementsAllHoles && index < jsuint(-1)) {
-                    JS_ASSERT(ReallyBigIndexToId(cx, index, idr.addr()));
-                    JSObject* obj2;
-                    JSProperty* prop;
-                    JS_ASSERT(obj->lookupProperty(cx, idr.id(), &obj2, &prop));
-                    JS_ASSERT(!prop);
-                }
-            }
-        }
-#endif
-
         jsuint newlen = start + count;
         JS_ASSERT(jsdouble(start) + count == jsdouble(newlen));
         if (!obj->ensureDenseArrayElements(cx, newlen))
             return JS_FALSE;
 
         if (newlen > obj->getArrayLength())
-            obj->setDenseArrayLength(newlen);
+            obj->setArrayLength(newlen);
 
         JS_ASSERT(count < uint32(-1) / sizeof(Value));
-        if (targetType == TargetElementsMayContainValues) {
-            jsuint valueCount = 0;
-            for (jsuint i = 0; i < count; i++) {
-                 if (!obj->getDenseArrayElement(start + i).isMagic(JS_ARRAY_HOLE))
-                     valueCount++;
-            }
-            JS_ASSERT(obj->getDenseArrayCount() >= valueCount);
-            obj->decDenseArrayCountBy(valueCount);
-        }
         memcpy(obj->getDenseArrayElements() + start, vector, sizeof(jsval) * count);
-        if (vectorType == SourceVectorAllValues) {
-            obj->incDenseArrayCountBy(count);
-        } else {
-            jsuint valueCount = 0;
-            for (jsuint i = 0; i < count; i++) {
-                 if (!obj->getDenseArrayElement(start + i).isMagic(JS_ARRAY_HOLE))
-                     valueCount++;
-            }
-            obj->incDenseArrayCountBy(valueCount);
-        }
         JS_ASSERT_IF(count != 0, !obj->getDenseArrayElement(newlen - 1).isMagic(JS_ARRAY_HOLE));
         return JS_TRUE;
     }
 
     Value* end = vector + count;
     while (vector != end && start < MAXINDEX) {
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !SetArrayElement(cx, obj, start++, *vector++)) {
@@ -1483,47 +1463,28 @@ InitArrayElements(JSContext *cx, JSObjec
         }
         idval.getDoubleRef() += 1;
     } while (vector != end);
 
     return JS_TRUE;
 }
 
 static JSBool
-InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector,
-                bool holey = false)
+InitArrayObject(JSContext *cx, JSObject *obj, jsuint length, const Value *vector)
 {
     JS_ASSERT(obj->isArray());
 
-    if (vector) {
-        JS_ASSERT(obj->isDenseArray());
-        obj->setDenseArrayLength(length);
-        if (!obj->ensureDenseArrayElements(cx, length))
-            return JS_FALSE;
-
-        jsuint count = length;
-        if (!holey) {
-            memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value));
-        } else {
-            for (jsuint i = 0; i < length; i++) {
-                if (vector[i].isMagic(JS_ARRAY_HOLE))
-                    --count;
-                obj->setDenseArrayElement(i, vector[i]);
-            }
-        }
-        obj->setDenseArrayCount(count);
-    } else {
-        if (obj->isDenseArray()) {
-            obj->setDenseArrayLength(length);
-            obj->setDenseArrayCount(0);
-        } else {
-            obj->setSlowArrayLength(length);
-        }
-    }
-    return JS_TRUE;
+    JS_ASSERT(obj->isDenseArray());
+    obj->setArrayLength(length);
+    if (!vector || !length)
+        return obj->ensureDenseArrayElements(cx, ARRAY_CAPACITY_MIN);
+    if (!obj->ensureDenseArrayElements(cx, length))
+        return false;
+    memcpy(obj->getDenseArrayElements(), vector, length * sizeof(Value));
+    return true;
 }
 
 /*
  * Perl-inspired join, reverse, and sort.
  */
 static JSBool
 array_join(JSContext *cx, uintN argc, Value *vp)
 {
@@ -2038,20 +1999,18 @@ array_sort(JSContext *cx, uintN argc, Va
         }
 
         /*
          * We no longer need to root the scratch space for the merge sort, so
          * unroot it now to make the job of a potential GC under
          * InitArrayElements easier.
          */
         tvr.changeLength(newlen);
-        if (!InitArrayElements(cx, obj, 0, newlen, vec, TargetElementsMayContainValues,
-                               SourceVectorAllValues)) {
+        if (!InitArrayElements(cx, obj, 0, newlen, vec))
             return false;
-        }
     }
 
     /* Set undefs that sorted after the rest of elements. */
     while (undefs != 0) {
         --undefs;
         if (!JS_CHECK_OPERATION_LIMIT(cx) ||
             !SetArrayElement(cx, obj, newlen++, UndefinedValue())) {
             return false;
@@ -2072,20 +2031,18 @@ array_sort(JSContext *cx, uintN argc, Va
  */
 static JSBool
 array_push_slowly(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     jsuint length;
 
     if (!js_GetLengthProperty(cx, obj, &length))
         return JS_FALSE;
-    if (!InitArrayElements(cx, obj, length, argc, argv, TargetElementsMayContainValues,
-                           SourceVectorAllValues)) {
+    if (!InitArrayElements(cx, obj, length, argc, argv))
         return JS_FALSE;
-    }
 
     /* Per ECMA-262, return the new array length. */
     jsdouble newlength = length + jsdouble(argc);
     rval->setNumber(newlength);
     return js_SetLengthProperty(cx, obj, newlength);
 }
 
 static JSBool
@@ -2096,20 +2053,19 @@ array_push1_dense(JSContext* cx, JSObjec
         if (!obj->makeDenseArraySlow(cx))
             return JS_FALSE;
         Value tmp = v;
         return array_push_slowly(cx, obj, 1, &tmp, rval);
     }
 
     if (!obj->ensureDenseArrayElements(cx, length + 1))
         return JS_FALSE;
-    obj->setDenseArrayLength(length + 1);
+    obj->setArrayLength(length + 1);
 
     JS_ASSERT(obj->getDenseArrayElement(length).isMagic(JS_ARRAY_HOLE));
-    obj->incDenseArrayCountBy(1);
     obj->setDenseArrayElement(length, v);
     rval->setNumber(obj->getArrayLength());
     return JS_TRUE;
 }
 
 JS_ALWAYS_INLINE JSBool
 ArrayCompPushImpl(JSContext *cx, JSObject *obj, const Value &v)
 {
@@ -2122,18 +2078,17 @@ ArrayCompPushImpl(JSContext *cx, JSObjec
             JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL,
                                    JSMSG_ARRAY_INIT_TOO_BIG);
             return JS_FALSE;
         }
 
         if (!obj->ensureDenseArrayElements(cx, length + 1))
             return JS_FALSE;
     }
-    obj->setDenseArrayLength(length + 1);
-    obj->incDenseArrayCountBy(1);
+    obj->setArrayLength(length + 1);
     obj->setDenseArrayElement(length, v);
     return JS_TRUE;
 }
 
 JSBool
 js_ArrayCompPush(JSContext *cx, JSObject *obj, const Value &vp)
 {
     return ArrayCompPushImpl(cx, obj, vp);
@@ -2193,17 +2148,17 @@ array_pop_dense(JSContext *cx, JSObject*
         vp->setUndefined();
         return JS_TRUE;
     }
     index--;
     if (!GetArrayElement(cx, obj, index, &hole, vp))
         return JS_FALSE;
     if (!hole && !DeleteArrayElement(cx, obj, index))
         return JS_FALSE;
-    obj->setDenseArrayLength(index);
+    obj->setArrayLength(index);
     return JS_TRUE;
 }
 
 static JSBool
 array_pop(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *obj = ComputeThisFromVp(cx, vp);
     if (!obj)
@@ -2228,22 +2183,20 @@ array_shift(JSContext *cx, uintN argc, V
     } else {
         length--;
 
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             length < obj->getDenseArrayCapacity()) {
             *vp = obj->getDenseArrayElement(0);
             if (vp->isMagic(JS_ARRAY_HOLE))
                 vp->setUndefined();
-            else
-                obj->decDenseArrayCountBy(1);
             Value *elems = obj->getDenseArrayElements();
             memmove(elems, elems + 1, length * sizeof(jsval));
             obj->setDenseArrayElement(length, MagicValue(JS_ARRAY_HOLE));
-            obj->setDenseArrayLength(length);
+            obj->setArrayLength(length);
             return JS_TRUE;
         }
 
         /* Get the to-be-deleted property's value into vp ASAP. */
         if (!GetArrayElement(cx, obj, 0, &hole, vp))
             return JS_FALSE;
 
         /* Slide down the array above the first element. */
@@ -2299,17 +2252,17 @@ array_unshift(JSContext *cx, uintN argc,
                         !SetOrDeleteArrayElement(cx, obj, upperIndex, hole, tvr.value())) {
                         return JS_FALSE;
                     }
                 } while (last != 0);
             }
         }
 
         /* Copy from argv to the bottom of the array. */
-        if (!InitArrayElements(cx, obj, 0, argc, argv, TargetElementsAllHoles, SourceVectorAllValues))
+        if (!InitArrayElements(cx, obj, 0, argc, argv))
             return JS_FALSE;
 
         newlen += argc;
         if (!js_SetLengthProperty(cx, obj, newlen))
             return JS_FALSE;
     }
 
     /* Follow Perl by returning the new array length. */
@@ -2379,20 +2332,18 @@ array_splice(JSContext *cx, uintN argc, 
 
     AutoValueRooter tvr(cx);
 
     /* If there are elements to remove, put them into the return value. */
     if (count > 0) {
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             !js_PrototypeHasIndexedProperties(cx, obj2) &&
             end <= obj->getDenseArrayCapacity()) {
-            if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin,
-                                 obj->getDenseArrayCount() != obj->getArrayLength())) {
+            if (!InitArrayObject(cx, obj2, count, obj->getDenseArrayElements() + begin))
                 return JS_FALSE;
-            }
         } else {
             for (last = begin; last < end; last++) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr())) {
                     return JS_FALSE;
                 }
 
                 /* Copy tvr.value() to the new array unless it's a hole. */
@@ -2414,24 +2365,20 @@ array_splice(JSContext *cx, uintN argc, 
             (length == 0 || !obj->getDenseArrayElement(length - 1).isMagic(JS_ARRAY_HOLE))) {
             if (!obj->ensureDenseArrayElements(cx, length + delta))
                 return JS_FALSE;
 
             Value *arraybeg = obj->getDenseArrayElements();
             Value *srcbeg = arraybeg + last - 1;
             Value *srcend = arraybeg + end - 1;
             Value *dstbeg = srcbeg + delta;
-            for (Value *src = srcbeg, *dst = dstbeg; src > srcend; --src, --dst) {
-                Value srcval = *src;
-                if (JS_UNLIKELY(!srcval.isMagic(JS_ARRAY_HOLE) && dst->isMagic(JS_ARRAY_HOLE)))
-                    obj->incDenseArrayCountBy(1);
-                *dst = srcval;
-            }
-
-            obj->setDenseArrayLength(obj->getArrayLength() + delta);
+            for (Value *src = srcbeg, *dst = dstbeg; src > srcend; --src, --dst)
+                *dst = *src;
+
+            obj->setArrayLength(obj->getArrayLength() + delta);
         } else {
             /* (uint) end could be 0, so we can't use a vanilla >= test. */
             while (last-- > end) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
                     !SetOrDeleteArrayElement(cx, obj, last + delta, hole, tvr.value())) {
                     return JS_FALSE;
                 }
@@ -2442,40 +2389,35 @@ array_splice(JSContext *cx, uintN argc, 
         delta = count - (jsuint)argc;
         if (obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj) &&
             length <= obj->getDenseArrayCapacity()) {
 
             Value *arraybeg = obj->getDenseArrayElements();
             Value *srcbeg = arraybeg + end;
             Value *srcend = arraybeg + length;
             Value *dstbeg = srcbeg - delta;
-            for (Value *src = srcbeg, *dst = dstbeg; src < srcend; ++src, ++dst) {
-                Value srcval = *src;
-                if (JS_UNLIKELY(!srcval.isMagic(JS_ARRAY_HOLE) && dst->isMagic(JS_ARRAY_HOLE)))
-                    obj->incDenseArrayCountBy(1);
-                *dst = srcval;
-            }
+            for (Value *src = srcbeg, *dst = dstbeg; src < srcend; ++src, ++dst)
+                *dst = *src;
         } else {
             for (last = end; last < length; last++) {
                 if (!JS_CHECK_OPERATION_LIMIT(cx) ||
                     !GetArrayElement(cx, obj, last, &hole, tvr.addr()) ||
                     !SetOrDeleteArrayElement(cx, obj, last - delta, hole, tvr.value())) {
                     return JS_FALSE;
                 }
             }
         }
         length -= delta;
     }
 
     /*
      * Copy from argv into the hole to complete the splice, and update length in
      * case we deleted elements from the end.
      */
-    return InitArrayElements(cx, obj, begin, argc, argv, TargetElementsMayContainValues,
-                             SourceVectorAllValues) &&
+    return InitArrayElements(cx, obj, begin, argc, argv) &&
            js_SetLengthProperty(cx, obj, length);
 }
 
 /*
  * Python-esque sequence operations.
  */
 static JSBool
 array_concat(JSContext *cx, uintN argc, Value *vp)
@@ -2493,21 +2435,20 @@ array_concat(JSContext *cx, uintN argc, 
          * handle a = [1,2,3]; a.length = 10000 "dense" cases efficiently. In
          * such a case we'll pass 8 (not 3) due to ARRAY_CAPACITY_MIN, which
          * will cause nobj to be over-allocated to 16. But in the normal case
          * where length is <= capacity, nobj and aobj will have the same
          * capacity.
          */
         length = aobj->getArrayLength();
         jsuint capacity = aobj->getDenseArrayCapacity();
-        nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements(),
-                                 aobj->getDenseArrayCount() != length);
+        nobj = js_NewArrayObject(cx, JS_MIN(length, capacity), aobj->getDenseArrayElements());
         if (!nobj)
             return JS_FALSE;
-        nobj->setDenseArrayLength(length);
+        nobj->setArrayLength(length);
         vp->setObject(*nobj);
         if (argc == 0)
             return JS_TRUE;
         argc--;
         p++;
     } else {
         nobj = js_NewArrayObject(cx, 0, NULL);
         if (!nobj)
@@ -2609,18 +2550,17 @@ array_slice(JSContext *cx, uintN argc, V
         }
     }
 
     if (begin > end)
         begin = end;
 
     if (obj->isDenseArray() && end <= obj->getDenseArrayCapacity() &&
         !js_PrototypeHasIndexedProperties(cx, obj)) {
-        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin,
-                                 obj->getDenseArrayCount() != obj->getArrayLength());
+        nobj = js_NewArrayObject(cx, end - begin, obj->getDenseArrayElements() + begin);
         if (!nobj)
             return JS_FALSE;
         vp->setObject(*nobj);
         return JS_TRUE;
     }
 
     /* Create a new Array object and root it using *vp. */
     nobj = js_NewArrayObject(cx, 0, NULL);
@@ -3053,110 +2993,88 @@ js_Array(JSContext *cx, JSObject *obj, u
         if (argv[0].isNull())
             return JS_FALSE;
         vector = NULL;
     }
     return InitArrayObject(cx, obj, length, vector);
 }
 
 JSObject* JS_FASTCALL
-js_NewEmptyArray(JSContext* cx, JSObject* proto)
+js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len)
 {
     JS_ASSERT(proto->isArray());
 
     JSObject* obj = js_NewGCObject(cx);
     if (!obj)
         return NULL;
 
     /* Initialize all fields of JSObject. */
     obj->map = const_cast<JSObjectMap *>(&SharedArrayMap);
-
     obj->init(&js_ArrayClass, proto, proto->getParent(), NullValue());
-    obj->setDenseArrayLength(0);
-    obj->setDenseArrayCount(0);
-    return obj;
-}
-#ifdef JS_TRACER
-JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, nanojit::ACC_STORE_ANY)
-#endif
-
-JSObject* JS_FASTCALL
-js_NewEmptyArrayWithLength(JSContext* cx, JSObject* proto, int32 len)
-{
-    if (len < 0)
-        return NULL;
-    JSObject *obj = js_NewEmptyArray(cx, proto);
-    if (!obj)
-        return NULL;
-    obj->setDenseArrayLength(len);
-    return obj;
-}
-#ifdef JS_TRACER
-JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewEmptyArrayWithLength, CONTEXT, OBJECT, INT32, 0,
-                     nanojit::ACC_STORE_ANY)
-#endif
-
-JSObject* JS_FASTCALL
-js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len)
-{
-    JSObject* obj = js_NewEmptyArray(cx, proto);
-    if (!obj)
-        return NULL;
-    obj->setDenseArrayLength(len);
-    if (!obj->resizeDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
+    obj->setArrayLength(len);
+    if (!obj->growDenseArrayElements(cx, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
         return NULL;
     return obj;
 }
 #ifdef JS_TRACER
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArrayWithSlots, CONTEXT, OBJECT, UINT32, 0,
                      nanojit::ACC_STORE_ANY)
 #endif
 
+JSObject* JS_FASTCALL
+js_NewEmptyArray(JSContext* cx, JSObject* proto)
+{
+    return js_NewArrayWithSlots(cx, proto, 0);
+}
+#ifdef JS_TRACER
+JS_DEFINE_CALLINFO_2(extern, OBJECT, js_NewEmptyArray, CONTEXT, OBJECT, 0, nanojit::ACC_STORE_ANY)
+#endif
+
 JSObject *
 js_InitArrayClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto = js_InitClass(cx, obj, NULL, &js_ArrayClass, js_Array, 1,
                                    NULL, array_methods, NULL, array_static_methods);
-
-    /* Initialize the Array prototype object so it gets a length property. */
-    if (!proto || !InitArrayObject(cx, proto, 0, NULL))
+    if (!proto)
         return NULL;
+    proto->setArrayLength(0);
+
     return proto;
 }
 
 JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector, bool holey)
+js_NewArrayObject(JSContext *cx, jsuint length, const Value *vector)
 {
     JSObject *obj = NewDenseArrayObject(cx);
     if (!obj)
         return NULL;
 
     /*
      * If this fails, the global object was not initialized and its class does
      * not have JSCLASS_IS_GLOBAL.
      */
     JS_ASSERT(obj->getProto());
 
     {
         AutoObjectRooter tvr(cx, obj);
-        if (!InitArrayObject(cx, obj, length, vector, holey))
+        if (!InitArrayObject(cx, obj, length, vector))
             obj = NULL;
     }
 
     /* Set/clear newborn root, in case we lost it.  */
     cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
     return obj;
 }
 
 JSObject *
 js_NewSlowArrayObject(JSContext *cx)
 {
     JSObject *obj = NewObject(cx, &js_SlowArrayClass, NULL, NULL);
     if (obj)
-        obj->setSlowArrayLength(0);
+        obj->setArrayLength(0);
     return obj;
 }
 
 #ifdef DEBUG_ARRAYS
 JSBool
 js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, Value *argv, Value *rval)
 {
     uintN i;
@@ -3173,18 +3091,17 @@ js_ArrayInfo(JSContext *cx, JSObject *ob
             fprintf(stderr, "%s: not array\n", bytes);
             cx->free(bytes);
             continue;
         }
         fprintf(stderr, "%s: %s (len %lu", bytes,
                 array->isDenseArray()) ? "dense" : "sparse",
                 array->getArrayLength());
         if (array->isDenseArray()) {
-            fprintf(stderr, ", count %lu, capacity %lu",
-                    array->getDenseArrayCount(),
+            fprintf(stderr, ", capacity %lu",
                     array->getDenseArrayCapacity());
         }
         fputs(")\n", stderr);
         cx->free(bytes);
     }
     return JS_TRUE;
 }
 #endif
@@ -3242,37 +3159,16 @@ js_CoerceArrayToCanvasImageData(JSObject
         } else {
             return JS_FALSE;
         }
     }
 
     return JS_TRUE;
 }
 
-JS_FRIEND_API(JSObject *)
-js_NewArrayObjectWithCapacity(JSContext *cx, uint32_t capacity, jsval **vector)
-{
-    JSObject *obj = js_NewArrayObject(cx, capacity, NULL);
-    if (!obj)
-        return NULL;
-
-    AutoObjectRooter tvr(cx, obj);
-    if (!obj->ensureDenseArrayElements(cx, capacity, JS_FALSE))
-        obj = NULL;
-
-    /* Set/clear newborn root, in case we lost it.  */
-    cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
-    if (!obj)
-        return NULL;
-
-    obj->setDenseArrayCount(capacity);
-    *vector = Jsvalify(obj->getDenseArrayElements());
-    return obj;
-}
-
 JS_FRIEND_API(JSBool)
 js_IsDensePrimitiveArray(JSObject *obj)
 {
     if (!obj || !obj->isDenseArray())
         return JS_FALSE;
 
     jsuint length = obj->getArrayLength();
     for (jsuint i = 0; i < length; i++) {
@@ -3306,44 +3202,34 @@ js_CloneDensePrimitiveArray(JSContext *c
      * clone and original array will have the same capacity.
      */
     jsuint jsvalCount = JS_MIN(obj->getDenseArrayCapacity(), length);
 
     js::AutoValueVector vector(cx);
     if (!vector.reserve(jsvalCount))
         return JS_FALSE;
 
-    jsuint holeCount = 0;
-
     for (jsuint i = 0; i < jsvalCount; i++) {
         const Value &val = obj->dslots[i];
 
         if (val.isString()) {
             // Strings must be made immutable before being copied to a clone.
             if (!js_MakeStringImmutable(cx, val.toString()))
                 return JS_FALSE;
-        } else if (val.isMagic(JS_ARRAY_HOLE)) {
-            holeCount++;
         } else if (val.isObject()) {
             /*
              * This wasn't an array of primitives. Return JS_TRUE but a null
              * clone to signal that no exception was encountered.
              */
             *clone = NULL;
             return JS_TRUE;
         }
 
         vector.append(val);
     }
 
-    jsval *buffer;
-    *clone = js_NewArrayObjectWithCapacity(cx, jsvalCount, &buffer);
+    *clone = js_NewArrayObject(cx, jsvalCount, vector.begin());
     if (!*clone)
         return JS_FALSE;
-
-    AutoObjectRooter cloneRoot(cx, *clone);
-
-    memcpy(buffer, vector.begin(), jsvalCount * sizeof (jsval));
-    (*clone)->setDenseArrayLength(length);
-    (*clone)->setDenseArrayCount(length - holeCount);
+    (*clone)->setArrayLength(length);
 
     return JS_TRUE;
 }
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -142,17 +142,17 @@ js_InitContextBusyArrayTable(JSContext *
 /*
  * Creates a new array with the given length and proto (NB: NULL is not
  * translated to Array.prototype), with len slots preallocated.
  */
 extern JSObject * JS_FASTCALL
 js_NewArrayWithSlots(JSContext* cx, JSObject* proto, uint32 len);
 
 extern JSObject *
-js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector, bool holey = false);
+js_NewArrayObject(JSContext *cx, jsuint length, const js::Value *vector);
 
 /* Create an array object that starts out already made slow/sparse. */
 extern JSObject *
 js_NewSlowArrayObject(JSContext *cx);
 
 extern JSBool
 js_GetLengthProperty(JSContext *cx, JSObject *obj, jsuint *lengthp);
 
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -572,17 +572,16 @@ js_dmod(jsdouble a, jsdouble b);
 
 #endif /* !JS_TRACER */
 
 /* Defined in jsarray.cpp. */
 JS_DECLARE_CALLINFO(js_Array_dense_setelem)
 JS_DECLARE_CALLINFO(js_Array_dense_setelem_int)
 JS_DECLARE_CALLINFO(js_Array_dense_setelem_double)
 JS_DECLARE_CALLINFO(js_NewEmptyArray)
-JS_DECLARE_CALLINFO(js_NewEmptyArrayWithLength)
 JS_DECLARE_CALLINFO(js_NewArrayWithSlots)
 JS_DECLARE_CALLINFO(js_ArrayCompPush_tn)
 
 /* Defined in jsbuiltins.cpp. */
 JS_DECLARE_CALLINFO(js_UnboxDouble)
 JS_DECLARE_CALLINFO(js_UnboxInt32)
 JS_DECLARE_CALLINFO(js_dmod)
 JS_DECLARE_CALLINFO(js_imod)
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -742,17 +742,16 @@ js_PurgeThreads(JSContext *cx)
              * The following is potentially suboptimal as it also zeros the
              * caches in data, but the code simplicity wins here.
              */
             thread->data.gcFreeLists.purge();
             DestroyThread(thread);
             e.removeFront();
         } else {
             thread->data.purge(cx);
-            thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT;
         }
     }
 #else
     cx->runtime->threadData.purge(cx);
 #endif
 }
 
 /*
@@ -1883,85 +1882,78 @@ js_GetErrorMessage(void *userRef, const 
     if ((errorNumber > 0) && (errorNumber < JSErr_Limit))
         return &js_ErrorFormatString[errorNumber];
     return NULL;
 }
 
 JSBool
 js_InvokeOperationCallback(JSContext *cx)
 {
-    JS_ASSERT(cx->interruptFlags & JSContext::INTERRUPT_OPERATION_CALLBACK);
+    JS_ASSERT_REQUEST_DEPTH(cx);
+    JS_ASSERT(JS_THREAD_DATA(cx)->interruptFlags & JSThreadData::INTERRUPT_OPERATION_CALLBACK);
 
     /*
      * Reset the callback flag first, then yield. If another thread is racing
      * us here we will accumulate another callback request which will be
      * serviced at the next opportunity.
      */
-    JS_ATOMIC_CLEAR_MASK((jsword*)&cx->interruptFlags,
-                         JSContext::INTERRUPT_OPERATION_CALLBACK);
+    JS_ATOMIC_CLEAR_MASK((jsword*)&JS_THREAD_DATA(cx)->interruptFlags,
+                         JSThreadData::INTERRUPT_OPERATION_CALLBACK);
 
     /*
-     * Unless we are going to run the GC, we automatically yield the current
-     * context every time the operation callback is hit since we might be
-     * called as a result of an impending GC, which would deadlock if we do
-     * not yield. Operation callbacks are supposed to happen rarely (seconds,
-     * not milliseconds) so it is acceptable to yield at every callback.
+     * Ideally this should never be hit. The embedding should call JS_MaybeGC()
+     * to schedule preemptive GCs.
      */
     JSRuntime *rt = cx->runtime;
-    if (rt->gcIsNeeded) {
-        js_GC(cx, GC_NORMAL);
-
-        /*
-         * On trace we can exceed the GC quota, see comments in NewGCArena. So
-         * we check the quota and report OOM here when we are off trace.
-         */
-        bool delayedOutOfMemory;
-        JS_LOCK_GC(rt);
-        delayedOutOfMemory = (rt->gcBytes > rt->gcMaxBytes);
-        JS_UNLOCK_GC(rt);
-        if (delayedOutOfMemory) {
+    if (rt->gcIsNeeded || rt->overQuota()) {
+        JS_GC(cx);
+        /* If we are still over quota after the GC, report an error. */
+        if (rt->overQuota()) {
             js_ReportOutOfMemory(cx);
             return false;
         }
     }
+
+    /*
+     * Automatically yield the current context every time the operation callback
+     * is hit since we might be called as a result of an impending GC, which
+     * would deadlock if we do not yield. Operation callbacks are supposed to
+     * happen rarely (seconds, not milliseconds) so it is acceptable to yield at
+     * every callback.
+     */
 #ifdef JS_THREADSAFE
-    else {
-        JS_YieldRequest(cx);
-    }
+    JS_YieldRequest(cx);
 #endif
 
-    JSOperationCallback cb = cx->operationCallback;
-
     /*
      * Important: Additional callbacks can occur inside the callback handler
      * if it re-enters the JS engine. The embedding must ensure that the
      * callback is disconnected before attempting such re-entry.
      */
-
+    JSOperationCallback cb = cx->operationCallback;
     return !cb || cb(cx);
 }
 
 JSBool
 js_HandleExecutionInterrupt(JSContext *cx)
 {
     JSBool result = JS_TRUE;
-    if (cx->interruptFlags & JSContext::INTERRUPT_OPERATION_CALLBACK)
+    if (JS_THREAD_DATA(cx)->interruptFlags & JSThreadData::INTERRUPT_OPERATION_CALLBACK)
         result = js_InvokeOperationCallback(cx) && result;
     return result;
 }
 
 void
 js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
 {
 #ifdef JS_THREADSAFE
     Conditionally<AutoLockGC> lockIf(!gcLocked, rt);
 #endif
-    JSContext *iter = NULL;
-    while (JSContext *acx = js_ContextIterator(rt, JS_FALSE, &iter))
-        JS_TriggerOperationCallback(acx);
+    for (ThreadDataIter i(rt); !i.empty(); i.popFront())
+        i.threadData()->triggerOperationCallback();
 }
 
 JSStackFrame *
 js_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
 {
     if (!fp)
         fp = js_GetTopStackFrame(cx);
     while (fp) {
@@ -2169,57 +2161,16 @@ JSContext::containingSegment(const JSSta
             if (f == target)
                 return css;
         }
     }
 
     return NULL;
 }
 
-void
-JSContext::checkMallocGCPressure(void *p)
-{
-    if (!p) {
-        js_ReportOutOfMemory(this);
-        return;
-    }
-
-#ifdef JS_THREADSAFE
-    JS_ASSERT(thread);
-    JS_ASSERT(thread->gcThreadMallocBytes <= 0);
-    ptrdiff_t n = JS_GC_THREAD_MALLOC_LIMIT - thread->gcThreadMallocBytes;
-    thread->gcThreadMallocBytes = JS_GC_THREAD_MALLOC_LIMIT;
-
-    AutoLockGC lock(runtime);
-    runtime->gcMallocBytes -= n;
-
-    /*
-     * Trigger the GC on memory pressure but only if we are inside a request
-     * and not inside a GC.
-     */
-    if (runtime->isGCMallocLimitReached() && requestDepth != 0)
-#endif
-    {
-        if (!runtime->gcRunning) {
-            JS_ASSERT(runtime->isGCMallocLimitReached());
-            runtime->gcMallocBytes = -1;
-
-            /*
-             * Empty the GC free lists to trigger a last-ditch GC when any GC
-             * thing is allocated later on this thread. This makes unnecessary
-             * to check for the memory pressure on the fast path of the GC
-             * allocator. We cannot touch the free lists on other threads as
-             * their manipulation is not thread-safe.
-             */
-            JS_THREAD_DATA(this)->gcFreeLists.purge();
-            js_TriggerGC(this, true);
-        }
-    }
-}
-
 bool
 JSContext::isConstructing()
 {
 #ifdef JS_TRACER
     if (JS_ON_TRACE(this)) {
         JS_ASSERT(bailExit);
         return *bailExit->pc == JSOP_NEW;
     }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -984,29 +984,28 @@ struct JSFunctionMeter {
 #define NATIVE_ITER_CACHE_SIZE  JS_BIT(NATIVE_ITER_CACHE_LOG2)
 
 struct JSPendingProxyOperation {
     JSPendingProxyOperation *next;
     JSObject *object;
 };
 
 struct JSThreadData {
+    /*
+     * If this flag is set, we were asked to call back the operation callback
+     * as soon as possible.
+     */
+    volatile int32      interruptFlags;
+
     JSGCFreeLists       gcFreeLists;
 
     /* Keeper of the contiguous stack used by all contexts in this thread. */
     js::StackSpace      stackSpace;
 
     /*
-     * Flag indicating that we are waiving any soft limits on the GC heap
-     * because we want allocations to be infallible (except when we hit
-     * a hard quota).
-     */
-    bool                waiveGCQuota;
-
-    /*
      * The GSN cache is per thread since even multi-cx-per-thread embeddings
      * do not interleave js_GetSrcNote calls.
      */
     JSGSNCache          gsnCache;
 
     /* Property cache for faster call/get/set invocation. */
     js::PropertyCache   propertyCache;
 
@@ -1050,16 +1049,28 @@ struct JSThreadData {
     JSPendingProxyOperation *pendingProxyOperation;
 
     js::ConservativeGCThreadData conservativeGC;
 
     bool init();
     void finish();
     void mark(JSTracer *trc);
     void purge(JSContext *cx);
+
+    static const jsword INTERRUPT_OPERATION_CALLBACK = 0x1;
+
+    void triggerOperationCallback() {
+        /*
+         * Use JS_ATOMIC_SET in the hope that it will make sure the write will
+         * become immediately visible to other processors polling the flag.
+         * Note that we only care about visibility here, not read/write
+         * ordering.
+         */
+        JS_ATOMIC_SET_MASK((jsword *) (&interruptFlags), INTERRUPT_OPERATION_CALLBACK);
+    }
 };
 
 #ifdef JS_THREADSAFE
 
 /*
  * Structure uniquely representing a thread.  It holds thread-private data
  * that can be accessed without a global lock.
  */
@@ -1074,22 +1085,16 @@ struct JSThread {
 
     /* Opaque thread-id, from NSPR's PR_GetCurrentThread(). */
     void                *id;
 
     /* Indicates that the thread is waiting in ClaimTitle from jslock.cpp. */
     JSTitle             *titleToShare;
 
     /*
-     * Thread-local version of JSRuntime.gcMallocBytes to avoid taking
-     * locks on each JS_malloc.
-     */
-    ptrdiff_t           gcThreadMallocBytes;
-
-    /*
      * This thread is inside js_GC, either waiting until it can start GC, or
      * waiting for GC to finish on another thread. This thread holds no locks;
      * other threads may steal titles from it.
      *
      * Protected by rt->gcLock.
      */
     bool                gcWaiting;
 
@@ -1097,23 +1102,16 @@ struct JSThread {
      * The context running the requests.
      */
     JSContext           *requestContext;
 
     /* Factored out of JSThread for !JS_THREADSAFE embedding in JSRuntime. */
     JSThreadData        data;
 };
 
-/*
- * Only when JSThread::gcThreadMallocBytes exhausts the following limit we
- * update JSRuntime::gcMallocBytes.
- * .
- */
-const size_t JS_GC_THREAD_MALLOC_LIMIT = 1 << 19;
-
 #define JS_THREAD_DATA(cx)      (&(cx)->thread->data)
 
 extern JSThread *
 js_CurrentThread(JSRuntime *rt);
 
 /*
  * The function takes the GC lock and does not release in successful return.
  * On error (out of memory) the function releases the lock but delegates
@@ -1226,16 +1224,17 @@ struct JSCompartment {
     bool wrap(JSContext *cx, js::AutoIdVector &props);
     bool wrapException(JSContext *cx);
 
     void sweep(JSContext *cx);
 };
 
 struct JSGCTracer : public JSTracer {
     uint32 color;
+    js::Vector<JSObject *, 0, js::SystemAllocPolicy> arraysToSlowify;
 };
 
 struct JSRuntime {
     /* Default compartment. */
     JSCompartment       *defaultCompartment;
 
     /* List of compartments (protected by the GC lock). */
     js::Vector<JSCompartment *, 0, js::SystemAllocPolicy> compartments;
@@ -1311,17 +1310,17 @@ struct JSRuntime {
 #endif
 
     JSGCCallback        gcCallback;
 
     /*
      * Malloc counter to measure memory pressure for GC scheduling. It runs
      * from gcMaxMallocBytes down to zero.
      */
-    ptrdiff_t           gcMallocBytes;
+    volatile ptrdiff_t  gcMallocBytes;
 
     /* See comments before DelayMarkingChildren is jsgc.cpp. */
     JSGCArena           *gcUnmarkedArenaStackTop;
 #ifdef DEBUG
     size_t              gcMarkLaterCount;
 #endif
 
 #ifdef JS_THREADSAFE
@@ -1593,26 +1592,45 @@ struct JSRuntime {
     JSRuntime();
     ~JSRuntime();
 
     bool init(uint32 maxbytes);
 
     void setGCTriggerFactor(uint32 factor);
     void setGCLastBytes(size_t lastBytes);
 
-    void* malloc(size_t bytes) { return ::js_malloc(bytes); }
-
-    void* calloc(size_t bytes) { return ::js_calloc(bytes); }
-
-    void* realloc(void* p, size_t bytes) { return ::js_realloc(p, bytes); }
+    bool gcQuotaReached() {
+        return gcBytes >= gcMaxBytes;
+    }
+
+    void updateMallocCounter(size_t bytes) {
+        gcMallocBytes -= bytes; /* We tolerate races and lost counts here. */
+    }
+
+    bool mallocQuotaReached() {
+        return gcMallocBytes <= 0;
+    }
+
+    bool overQuota() {
+        return gcQuotaReached() || mallocQuotaReached();
+    }
+
+    void* malloc(size_t bytes) { updateMallocCounter(bytes); return ::js_malloc(bytes); }
+
+    void* calloc(size_t bytes) { updateMallocCounter(bytes); return ::js_calloc(bytes); }
+
+    void* realloc(void* p, size_t bytes) {
+        void* q = ::js_realloc(p, bytes);
+        if (p != q)
+            updateMallocCounter(bytes);
+        return q;
+    }
 
     void free(void* p) { ::js_free(p); }
 
-    bool isGCMallocLimitReached() const { return gcMallocBytes <= 0; }
-
     void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); }
 
     void setGCMaxMallocBytes(size_t value) {
         /*
          * For compatibility treat any value that exceeds PTRDIFF_T_MAX to
          * mean that value.
          */
         gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
@@ -1697,28 +1715,23 @@ struct JSRegExpStatics {
 
     JSRegExpStatics(JSContext *cx) : cx(cx), parens(cx) {}
 
     bool copy(const JSRegExpStatics& other);
     void clearRoots();
     void clear();
 };
 
+extern JS_FRIEND_API(void)
+js_ReportOutOfMemory(JSContext *cx);
+
 struct JSContext
 {
     explicit JSContext(JSRuntime *rt);
 
-    /*
-     * If this flag is non-zero, we were asked to interrupt execution as soon
-     * as possible. The bits below describe the reason.
-     */
-    volatile jsword     interruptFlags;
-
-    static const jsword INTERRUPT_OPERATION_CALLBACK = 0x1;
-
     /* JSRuntime contextList linkage. */
     JSCList             link;
 
     /* Runtime version control identifier. */
     uint16              version;
 
     /* Per-context options. */
     uint32              options;            /* see jsapi.h for JSOPTION_* */
@@ -1977,86 +1990,50 @@ struct JSContext
 
 #ifdef JS_THREADSAFE
     /*
      * The sweep task for this context.
      */
     js::BackgroundSweepTask *gcSweepTask;
 #endif
 
-    ptrdiff_t &getMallocCounter() {
-#ifdef JS_THREADSAFE
-        return thread->gcThreadMallocBytes;
-#else
-        return runtime->gcMallocBytes;
-#endif
+    inline void triggerGC() {
+        if (!runtime->gcIsNeeded) {
+            runtime->gcIsNeeded = true;
+            JS_TriggerAllOperationCallbacks(runtime);
+        }
     }
 
-    /*
-     * Call this after allocating memory held by GC things, to update memory
-     * pressure counters or report the OOM error if necessary.
-     */
-    inline void updateMallocCounter(void *p, size_t nbytes) {
-        JS_ASSERT(ptrdiff_t(nbytes) >= 0);
-        ptrdiff_t &counter = getMallocCounter();
-        counter -= ptrdiff_t(nbytes);
-        if (!p || counter <= 0)
-            checkMallocGCPressure(p);
-    }
-
-    /*
-     * Call this after successfully allocating memory held by GC things, to
-     * update memory pressure counters.
-     */
-    inline void updateMallocCounter(size_t nbytes) {
-        JS_ASSERT(ptrdiff_t(nbytes) >= 0);
-        ptrdiff_t &counter = getMallocCounter();
-        counter -= ptrdiff_t(nbytes);
-        if (counter <= 0) {
-            /*
-             * Use 1 as an arbitrary non-null pointer indicating successful
-             * allocation.
-             */
-            checkMallocGCPressure(reinterpret_cast<void *>(jsuword(1)));
+    inline void *reportIfOutOfMemory(void *p) {
+        if (p) {
+            if (runtime->mallocQuotaReached())
+                triggerGC();
+            return p;
         }
+        js_ReportOutOfMemory(this);
+        return NULL;
     }
 
     inline void* malloc(size_t bytes) {
         JS_ASSERT(bytes != 0);
-        void *p = runtime->malloc(bytes);
-        updateMallocCounter(p, bytes);
-        return p;
+        return reportIfOutOfMemory(runtime->malloc(bytes));
     }
 
     inline void* mallocNoReport(size_t bytes) {
         JS_ASSERT(bytes != 0);
-        void *p = runtime->malloc(bytes);
-        if (!p)
-            return NULL;
-        updateMallocCounter(bytes);
-        return p;
+        return runtime->malloc(bytes);
     }
 
     inline void* calloc(size_t bytes) {
         JS_ASSERT(bytes != 0);
-        void *p = runtime->calloc(bytes);
-        updateMallocCounter(p, bytes);
-        return p;
+        return reportIfOutOfMemory(runtime->calloc(bytes));
     }
 
     inline void* realloc(void* p, size_t bytes) {
-        void *orig = p;
-        p = runtime->realloc(p, bytes);
-
-        /*
-         * For compatibility we do not account for realloc that increases
-         * previously allocated memory.
-         */
-        updateMallocCounter(p, orig ? 0 : bytes);
-        return p;
+        return reportIfOutOfMemory(runtime->realloc(p, bytes));
     }
 
     inline void free(void* p) {
 #ifdef JS_THREADSAFE
         if (gcSweepTask) {
             gcSweepTask->freeLater(p);
             return;
         }
@@ -2113,26 +2090,16 @@ struct JSContext
 #ifdef DEBUG
     void assertValidStackDepth(uintN depth) {
         JS_ASSERT(0 <= regs->sp - fp->base());
         JS_ASSERT(depth <= uintptr_t(regs->sp - fp->base()));
     }
 #else
     void assertValidStackDepth(uintN /*depth*/) {}
 #endif
-
-private:
-
-    /*
-     * The allocation code calls the function to indicate either OOM failure
-     * when p is null or that a memory pressure counter has reached some
-     * threshold when p is not null. The function takes the pointer and not
-     * a boolean flag to minimize the amount of code in its inlined callers.
-     */
-    JS_FRIEND_API(void) checkMallocGCPressure(void *p);
 };
 
 JS_ALWAYS_INLINE JSObject *
 JSStackFrame::varobj(js::CallStackSegment *css) const
 {
     JS_ASSERT(css->contains(this));
     return fun ? callobj : css->getInitialVarObj();
 }
@@ -2231,21 +2198,27 @@ FrameAtomBase(JSContext *cx, JSStackFram
 namespace js {
 
 class AutoGCRooter {
   public:
     AutoGCRooter(JSContext *cx, ptrdiff_t tag)
       : down(cx->autoGCRooters), tag(tag), context(cx)
     {
         JS_ASSERT(this != cx->autoGCRooters);
+#ifdef JS_THREADSAFE
+        JS_ASSERT(cx->requestDepth != 0);
+#endif
         cx->autoGCRooters = this;
     }
 
     ~AutoGCRooter() {
         JS_ASSERT(this == context->autoGCRooters);
+#ifdef JS_THREADSAFE
+        JS_ASSERT(context->requestDepth != 0);
+#endif
         context->autoGCRooters = down;
     }
 
     /* Implemented in jsgc.cpp. */
     inline void trace(JSTracer *trc);
 
 #ifdef __GNUC__
 # pragma GCC visibility push(default)
@@ -2875,19 +2848,16 @@ js_ReportErrorNumberVA(JSContext *cx, ui
 
 extern JSBool
 js_ExpandErrorArguments(JSContext *cx, JSErrorCallback callback,
                         void *userRef, const uintN errorNumber,
                         char **message, JSErrorReport *reportp,
                         bool charArgs, va_list ap);
 #endif
 
-extern void
-js_ReportOutOfMemory(JSContext *cx);
-
 /*
  * Report that cx->scriptStackQuota is exhausted.
  */
 void
 js_ReportOutOfScriptQuota(JSContext *cx);
 
 extern void
 js_ReportOverRecursed(JSContext *cx);
@@ -2954,24 +2924,30 @@ extern JSErrorFormatString js_ErrorForma
  * grows in the expected direction.
  */
 #if JS_STACK_GROWTH_DIRECTION > 0
 # define JS_CHECK_STACK_SIZE(cx, lval)  ((jsuword)&(lval) < (cx)->stackLimit)
 #else
 # define JS_CHECK_STACK_SIZE(cx, lval)  ((jsuword)&(lval) > (cx)->stackLimit)
 #endif
 
+#ifdef JS_THREADSAFE
+# define JS_ASSERT_REQUEST_DEPTH(cx)  JS_ASSERT((cx)->requestDepth >= 1)
+#else
+# define JS_ASSERT_REQUEST_DEPTH(cx)  ((void) 0)
+#endif
+
 /*
  * If the operation callback flag was set, call the operation callback.
  * This macro can run the full GC. Return true if it is OK to continue and
  * false otherwise.
  */
-#define JS_CHECK_OPERATION_LIMIT(cx) \
-    (!((cx)->interruptFlags & JSContext::INTERRUPT_OPERATION_CALLBACK) || \
-     js_InvokeOperationCallback(cx))
+#define JS_CHECK_OPERATION_LIMIT(cx)                                          \
+    (JS_ASSERT_REQUEST_DEPTH(cx),                                             \
+     (!(JS_THREAD_DATA(cx)->interruptFlags & JSThreadData::INTERRUPT_OPERATION_CALLBACK) || js_InvokeOperationCallback(cx)))
 
 /*
  * Invoke the operation callback and return false if the current execution
  * is to be terminated.
  */
 extern JSBool
 js_InvokeOperationCallback(JSContext *cx);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -636,39 +636,29 @@ ReleaseGCChunk(JSRuntime *rt, jsuword ch
     METER(rt->gcStats.nchunks--);
     rt->gcChunkAllocator->free(p);
 }
 
 static JSGCArena *
 NewGCArena(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
-    if (!JS_THREAD_DATA(cx)->waiveGCQuota && rt->gcBytes >= rt->gcMaxBytes) {
-        /*
-         * FIXME bug 524051 We cannot run a last-ditch GC on trace for now, so
-         * just pretend we are out of memory which will throw us off trace and
-         * we will re-try this code path from the interpreter.
-         */
-        if (!JS_ON_TRACE(cx))
-            return NULL;
-        js_TriggerGC(cx, true);
-    }
 
     size_t nchunks = rt->gcChunks.length();
-
     JSGCChunkInfo *ci;
     for (;; ++rt->gcChunkCursor) {
         if (rt->gcChunkCursor == nchunks) {
             ci = NULL;
             break;
         }
         ci = rt->gcChunks[rt->gcChunkCursor];
         if (ci->numFreeArenas != 0)
             break;
     }
+
     if (!ci) {
         if (!rt->gcChunks.reserve(nchunks + 1))
             return NULL;
         void *chunkptr = GetGCChunk(rt);
         if (!chunkptr)
             return NULL;
         ci = JSGCChunkInfo::fromChunk(reinterpret_cast<jsuword>(chunkptr));
         ci->init(rt);
@@ -1718,107 +1708,55 @@ JSGCFreeLists::purge()
 void
 JSGCFreeLists::moveTo(JSGCFreeLists *another)
 {
     *another = *this;
     PodArrayZero(finalizables);
     JS_ASSERT(isEmpty());
 }
 
-static inline bool
-IsGCThresholdReached(JSRuntime *rt)
-{
-#ifdef JS_GC_ZEAL
-    if (rt->gcZeal >= 1)
-        return true;
-#endif
-
-    /*
-     * Since the initial value of the gcLastBytes parameter is not equal to
-     * zero (see the js_InitGC function) the return value is false when
-     * the gcBytes value is close to zero at the JS engine start.
-     */
-    return rt->isGCMallocLimitReached() || rt->gcBytes >= rt->gcTriggerBytes;
-}
-
-static void
-LastDitchGC(JSContext *cx)
-{
-    JS_ASSERT(!JS_ON_TRACE(cx));
-
-    /* The last ditch GC preserves weak roots and all atoms. */
-    AutoPreserveWeakRoots save(cx);
-    AutoKeepAtoms keep(cx->runtime);
-
-    /*
-     * Keep rt->gcLock across the call into the GC so we don't starve and
-     * lose to racing threads who deplete the heap just after the GC has
-     * replenished it (or has synchronized with a racing GC that collected a
-     * bunch of garbage).  This unfair scheduling can happen on certain
-     * operating systems. For the gory details, see bug 162779.
-     */
-    js_GC(cx, GC_LOCK_HELD);
-}
-
 static JSGCThing *
 RefillFinalizableFreeList(JSContext *cx, unsigned thingKind)
 {
     JS_ASSERT(!JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind]);
     JSRuntime *rt = cx->runtime;
     JSGCArenaList *arenaList;
     JSGCArena *a;
 
     {
         AutoLockGC lock(rt);
         JS_ASSERT(!rt->gcRunning);
         if (rt->gcRunning) {
             METER(rt->gcStats.finalfail++);
             return NULL;
         }
 
-        bool canGC = !JS_ON_TRACE(cx) && !JS_THREAD_DATA(cx)->waiveGCQuota;
-        bool doGC = canGC && IsGCThresholdReached(rt);
         arenaList = &rt->gcArenaList[thingKind];
-        for (;;) {
-            if (doGC) {
-                LastDitchGC(cx);
-                METER(cx->runtime->gcStats.arenaStats[thingKind].retry++);
-                canGC = false;
-
-                /*
-                 * The JSGC_END callback can legitimately allocate new GC
-                 * things and populate the free list. If that happens, just
-                 * return that list head.
-                 */
-                JSGCThing *freeList = JS_THREAD_DATA(cx)->gcFreeLists.finalizables[thingKind];
-                if (freeList)
-                    return freeList;
+        while ((a = arenaList->cursor) != NULL) {
+            JSGCArenaInfo *ainfo = a->getInfo();
+            arenaList->cursor = ainfo->prev;
+            JSGCThing *freeList = ainfo->freeList;
+            if (freeList) {
+                ainfo->freeList = NULL;
+                return freeList;
             }
-
-            while ((a = arenaList->cursor) != NULL) {
-                JSGCArenaInfo *ainfo = a->getInfo();
-                arenaList->cursor = ainfo->prev;
-                JSGCThing *freeList = ainfo->freeList;
-                if (freeList) {
-                    ainfo->freeList = NULL;
-                    return freeList;
-                }
-            }
-
-            a = NewGCArena(cx);
-            if (a)
-                break;
-            if (!canGC) {
-                METER(cx->runtime->gcStats.arenaStats[thingKind].fail++);
-                return NULL;
-            }
-            doGC = true;
         }
 
         /*
+         * If we have to allocate a new arena, check whether are bumping
+         * against our GC heap quota. If so, request a GC to happen soon.
+         */
+        if (rt->gcQuotaReached())
+            cx->triggerGC();
+
+        a = NewGCArena(cx);
+        if (!a)
+            return NULL;
+
+        /*
          * Do only minimal initialization of the arena inside the GC lock. We
          * can do the rest outside the lock because no other threads will see
          * the arena until the GC is run.
          */
         JSGCArenaInfo *ainfo = a->getInfo();
         ainfo->list = arenaList;
         ainfo->prev = arenaList->head;
         ainfo->freeList = NULL;
@@ -2523,36 +2461,16 @@ js_TraceRuntime(JSTracer *trc)
     if (rt->state != JSRTS_LANDING)
         ConservativeGCStackMarker(trc).markRoots();
 
     if (rt->gcExtraRootsTraceOp)
         rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
 }
 
 void
-js_TriggerGC(JSContext *cx, JSBool gcLocked)
-{
-    JSRuntime *rt = cx->runtime;
-
-#ifdef JS_THREADSAFE
-    JS_ASSERT(cx->requestDepth > 0);
-#endif
-    JS_ASSERT(!rt->gcRunning);
-    if (rt->gcIsNeeded)
-        return;
-
-    /*
-     * Trigger the GC when it is safe to call an operation callback on any
-     * thread.
-     */
-    rt->gcIsNeeded = JS_TRUE;
-    js_TriggerAllOperationCallbacks(rt, gcLocked);
-}
-
-void
 js_DestroyScriptsToGC(JSContext *cx, JSThreadData *data)
 {
     JSScript **listp, *script;
 
     for (size_t i = 0; i != JS_ARRAY_LENGTH(data->scriptsToGC); ++i) {
         listp = &data->scriptsToGC[i];
         while ((script = *listp) != NULL) {
             *listp = script->u.nextToGC;
@@ -3157,16 +3075,26 @@ GC(JSContext *cx  GCTIMER_PARAM)
      * Sweep script filenames after sweeping functions in the generic loop
      * above. In this way when a scripted function's finalizer destroys the
      * script and calls rt->destroyScriptHook, the hook can still access the
      * script's filename. See bug 323267.
      */
     js_SweepScriptFilenames(rt);
 
     /*
+     * Slowify arrays we have accumulated.
+     */
+    while (!trc.arraysToSlowify.empty()) {
+        JSObject *obj = trc.arraysToSlowify.back();
+        trc.arraysToSlowify.popBack();
+        if (IsMarkedGCThing(obj))
+            obj->makeDenseArraySlow(cx);
+    }
+
+    /*
      * Destroy arenas after we finished the sweeping so finalizers can safely
      * use js_IsAboutToBeFinalized().
      */
     FreeGCChunks(rt);
     TIMESTAMP(sweepDestroyEnd);
 
 #ifdef JS_THREADSAFE
     if (cx->gcSweepTask) {
@@ -3312,22 +3240,26 @@ BeginGCSession(JSContext *cx)
     JS_ASSERT(!rt->gcRunning);
 
 #ifdef JS_THREADSAFE
     /* No other thread is in GC, so indicate that we're now in GC. */
     JS_ASSERT(!rt->gcThread);
     rt->gcThread = cx->thread;
 
     /*
-     * Notify all operation callbacks, which will give them a chance to yield
-     * their current request. Contexts that are not currently executing will
-     * perform their callback at some later point, which then will be
-     * unnecessary, but harmless.
+     * Notify operation callbacks on other threads, which will give them a
+     * chance to yield their requests. Threads without requests perform their
+     * callback at some later point, which then will be unnecessary, but
+     * harmless.
      */
-    js_NudgeOtherContexts(cx);
+    for (JSThread::Map::Range r = rt->threads.all(); !r.empty(); r.popFront()) {
+        JSThread *thread = r.front().value;
+        if (thread != cx->thread)
+            thread->data.triggerOperationCallback();
+    }
 
     /*
      * Discount the request on the current thread from contributing to
      * rt->requestCount before we wait for all other requests to finish.
      * JS_NOTIFY_REQUEST_DONE, which will wake us up, is only called on
      * rt->requestCount transitions to 0.
      */
     JS_ASSERT_IF(cx->requestDepth != 0, cx->thread->requestContext);
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -163,26 +163,16 @@ js_TraceStackFrame(JSTracer *trc, JSStac
 
 extern JS_REQUIRES_STACK void
 js_TraceRuntime(JSTracer *trc);
 
 extern JS_REQUIRES_STACK JS_FRIEND_API(void)
 js_TraceContext(JSTracer *trc, JSContext *acx);
 
 /*
- * Schedule the GC call at a later safe point.
- */
-#ifndef JS_THREADSAFE
-# define js_TriggerGC(cx, gcLocked)    js_TriggerGC (cx)
-#endif
-
-extern void
-js_TriggerGC(JSContext *cx, JSBool gcLocked);
-
-/*
  * Kinds of js_GC invocation.
  */
 typedef enum JSGCInvocationKind {
     /* Normal invocation. */
     GC_NORMAL           = 0,
 
     /*
      * Called from js_DestroyContext for last JSContext in a JSRuntime, when
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1067,17 +1067,17 @@ CheckRedeclaration(JSContext *cx, JSObje
         return JS_FALSE;
     return !!JS_ReportErrorFlagsAndNumber(cx, report,
                                           js_GetErrorMessage, NULL,
                                           JSMSG_REDECLARED_VAR,
                                           type, name);
 }
 
 JSBool
-js_HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
+HasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
 {
     Class *clasp = obj->getClass();
     if (clasp->hasInstance)
         return clasp->hasInstance(cx, obj, v, bp);
     js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                         JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL);
     return JS_FALSE;
 }
@@ -2342,17 +2342,17 @@ Interpret(JSContext *cx, JSStackFrame *e
 #endif /* !JS_TRACER */
 
     /*
      * Prepare to call a user-supplied branch handler, and abort the script
      * if it returns false.
      */
 #define CHECK_BRANCH()                                                        \
     JS_BEGIN_MACRO                                                            \
-        if (cx->interruptFlags && !js_HandleExecutionInterrupt(cx))           \
+        if (JS_THREAD_DATA(cx)->interruptFlags && !js_HandleExecutionInterrupt(cx)) \
             goto error;                                                       \
     JS_END_MACRO
 
 #ifndef TRACE_RECORDER
 #define TRACE_RECORDER(cx) (false)
 #endif
 
 #ifdef JS_METHODJIT
@@ -4518,18 +4518,17 @@ BEGIN_CASE(JSOP_GETELEM)
     const Value *copyFrom;
     Value rval;
     jsid id;
     if (rref.isInt32()) {
         int32_t i = rref.toInt32();
         if (obj->isDenseArray()) {
             jsuint idx = jsuint(i);
 
-            if (idx < obj->getArrayLength() &&
-                idx < obj->getDenseArrayCapacity()) {
+            if (idx < obj->getDenseArrayCapacity()) {
                 copyFrom = obj->addressOfDenseArrayElement(idx);
                 if (!copyFrom->isMagic())
                     goto end_getelem;
 
                 /* Reload retval from the stack in the rare hole case. */
                 copyFrom = &regs.sp[-1];
             }
         } else if (obj->isArguments()
@@ -4610,18 +4609,17 @@ BEGIN_CASE(JSOP_SETELEM)
         if (obj->isDenseArray() && JSID_IS_INT(id)) {
             jsuint length = obj->getDenseArrayCapacity();
             jsint i = JSID_TO_INT(id);
             if ((jsuint)i < length) {
                 if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
                     if (js_PrototypeHasIndexedProperties(cx, obj))
                         break;
                     if ((jsuint)i >= obj->getArrayLength())
-                        obj->setDenseArrayLength(i + 1);
-                    obj->incDenseArrayCountBy(1);
+                        obj->setArrayLength(i + 1);
                 }
                 obj->setDenseArrayElement(i, regs.sp[-1]);
                 goto end_setelem;
             }
         }
     } while (0);
     if (!obj->setProperty(cx, id, &regs.sp[-1]))
         goto error;
@@ -6030,17 +6028,17 @@ BEGIN_CASE(JSOP_SETTER)
 BEGIN_CASE(JSOP_HOLE)
     PUSH_HOLE();
 END_CASE(JSOP_HOLE)
 
 BEGIN_CASE(JSOP_NEWARRAY)
 {
     len = GET_UINT16(regs.pc);
     cx->assertValidStackDepth(len);
-    JSObject *obj = js_NewArrayObject(cx, len, regs.sp - len, JS_TRUE);
+    JSObject *obj = js_NewArrayObject(cx, len, regs.sp - len);
     if (!obj)
         goto error;
     regs.sp -= len - 1;
     regs.sp[-1].setObject(*obj);
 }
 END_CASE(JSOP_NEWARRAY)
 
 BEGIN_CASE(JSOP_NEWINIT)
@@ -6427,17 +6425,17 @@ BEGIN_CASE(JSOP_INSTANCEOF)
     const Value &rref = regs.sp[-1];
     if (rref.isPrimitive()) {
         js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS, -1, rref, NULL);
         goto error;
     }
     JSObject *obj = &rref.toObject();
     const Value &lref = regs.sp[-2];
     JSBool cond = JS_FALSE;
-    if (!js_HasInstance(cx, obj, &lref, &cond))
+    if (!HasInstance(cx, obj, &lref, &cond))
         goto error;
     regs.sp--;
     regs.sp[-1].setBoolean(cond);
 }
 END_CASE(JSOP_INSTANCEOF)
 
 #if JS_HAS_DEBUGGER_KEYWORD
 BEGIN_CASE(JSOP_DEBUGGER)
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -396,17 +396,17 @@ InstanceOf(JSContext *cx, JSObject *obj,
 {
     if (obj && obj->getClass() == clasp)
         return true;
     extern bool InstanceOfSlow(JSContext *, JSObject *, Class *, Value *);
     return InstanceOfSlow(cx, obj, clasp, argv);
 }
 
 extern JSBool
-js_HasInstance(JSContext *cx, JSObject *obj, const js::Value *v, JSBool *bp);
+HasInstance(JSContext *cx, JSObject *obj, const js::Value *v, JSBool *bp);
 
 inline void *
 GetInstancePrivate(JSContext *cx, JSObject *obj, Class *clasp, Value *argv)
 {
     if (!InstanceOf(cx, obj, clasp, argv))
         return NULL;
     return obj->getPrivate();
 }
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -201,27 +201,28 @@ template <class EnumPolicy>
 static inline bool
 Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
           bool enumerable, bool sharedPermanent, uintN flags, IdSet& ht,
           typename EnumPolicy::ResultVector &props)
 {
     JS_ASSERT(JSID_IS_INT(id) || JSID_IS_ATOM(id));
 
     IdSet::AddPtr p = ht.lookupForAdd(id);
-    JS_ASSERT_IF(obj == pobj, !p);
+    JS_ASSERT_IF(obj == pobj && !obj->isProxy(), !p);
 
     /* If we've already seen this, we definitely won't add it. */
     if (JS_UNLIKELY(!!p))
         return true;
 
     /*
      * It's not necessary to add properties to the hash table at the end of the
-     * prototype chain.
+     * prototype chain -- but a proxy might return duplicated properties, so
+     * always add for them.
      */
-    if (pobj->getProto() && !ht.add(p, id))
+    if ((pobj->getProto() || pobj->isProxy()) && !ht.add(p, id))
         return false;
 
     if (JS_UNLIKELY(flags & JSITER_OWNONLY)) {
         /*
          * Shared-permanent hack: If this property is shared permanent
          * and pobj and obj have the same class, then treat it as an own
          * property of obj, even if pobj != obj. (But see bug 575997.)
          *
@@ -272,17 +273,17 @@ static bool
 EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, uintN flags,
                               IdSet &ht, typename EnumPolicy::ResultVector &props)
 {
     if (!Enumerate<EnumPolicy>(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false, true,
                                flags, ht, props)) {
         return false;
     }
 
-    if (pobj->getDenseArrayCount() > 0) {
+    if (pobj->getArrayLength() > 0) {
         size_t capacity = pobj->getDenseArrayCapacity();
         Value *vp = pobj->dslots;
         for (size_t i = 0; i < capacity; ++i, ++vp) {
             if (!vp->isMagic(JS_ARRAY_HOLE)) {
                 /* Dense arrays never get so large that i would not fit into an integer id. */
                 if (!Enumerate<EnumPolicy>(cx, obj, pobj, INT_TO_JSID(i), true, false, flags, ht, props))
                     return false;
             }
--- a/js/src/jslock.cpp
+++ b/js/src/jslock.cpp
@@ -525,53 +525,16 @@ FinishSharingTitle(JSContext *cx, JSTitl
         }
     }
 
     title->ownercx = NULL;  /* NB: set last, after lock init */
     JS_RUNTIME_METER(cx->runtime, sharedTitles);
 }
 
 /*
- * Notify all contexts that are currently in a request, which will give them a
- * chance to yield their current request.
- */
-void
-js_NudgeOtherContexts(JSContext *cx)
-{
-    JSRuntime *rt = cx->runtime;
-    JSContext *acx = NULL;
-
-    while ((acx = js_NextActiveContext(rt, acx)) != NULL) {
-        if (cx != acx)
-            JS_TriggerOperationCallback(acx);
-    }
-}
-
-/*
- * Notify all contexts that are currently in a request and execute on this
- * specific thread.
- */
-static void
-NudgeThread(JSRuntime *rt, JSThread *thread)
-{
-    JS_ASSERT(thread);
-
-    /*
-     * We cannot walk here over thread->contextList as that is manipulated
-     * outside the GC lock and must be accessed only from the the thread that
-     * owns JSThread.
-     */
-    JSContext *acx = NULL;
-    while ((acx = js_NextActiveContext(rt, acx)) != NULL) {
-        if (acx->thread == thread)
-            JS_TriggerOperationCallback(acx);
-    }
-}
-
-/*
  * Given a title with apparently non-null ownercx different from cx, try to
  * set ownercx to cx, claiming exclusive (single-threaded) ownership of title.
  * If we claim ownership, return true.  Otherwise, we wait for ownercx to be
  * set to null (indicating that title is multi-threaded); or if waiting would
  * deadlock, we set ownercx to null ourselves via ShareTitle.  In any case,
  * once ownercx is null we return false.
  */
 static JSBool
@@ -653,17 +616,17 @@ ClaimTitle(JSTitle *title, JSContext *cx
 
         /*
          * We know that some other thread's context owns title, which is now
          * linked onto rt->titleSharingTodo, awaiting the end of that other
          * thread's request. So it is safe to wait on rt->titleSharingDone.
          * But before waiting, we force the operation callback for that other
          * thread so it can quickly suspend.
          */
-        NudgeThread(rt, ownercx->thread);
+        JS_THREAD_DATA(ownercx)->triggerOperationCallback();
 
         JS_ASSERT(!cx->thread->titleToShare);
         cx->thread->titleToShare = title;
 #ifdef DEBUG
         PRStatus stat =
 #endif
             PR_WaitCondVar(rt->titleSharingDone, PR_INTERVAL_NO_TIMEOUT);
         JS_ASSERT(stat != PR_FAILURE);
--- a/js/src/jslock.h
+++ b/js/src/jslock.h
@@ -210,19 +210,16 @@ extern void js_InitLock(JSThinLock *);
 extern void js_FinishLock(JSThinLock *);
 
 /*
  * This function must be called with the GC lock held.
  */
 extern void
 js_ShareWaitingTitles(JSContext *cx);
 
-extern void
-js_NudgeOtherContexts(JSContext *cx);
-
 #ifdef DEBUG
 
 #define JS_IS_RUNTIME_LOCKED(rt)        js_IsRuntimeLocked(rt)
 #define JS_IS_OBJ_LOCKED(cx,obj)        js_IsObjLocked(cx,obj)
 #define JS_IS_TITLE_LOCKED(cx,title)    js_IsTitleLocked(cx,title)
 
 extern JSBool js_IsRuntimeLocked(JSRuntime *rt);
 extern JSBool js_IsObjLocked(JSContext *cx, JSObject *obj);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1858,17 +1858,17 @@ obj_keys(JSContext *cx, uintN argc, Valu
             JSString *str = js_IntToString(cx, JSID_TO_INT(id));
             if (!str)
                 return JS_FALSE;
             vals[i].setString(str);
         }
     }
 
     JS_ASSERT(props.length() <= UINT32_MAX);
-    JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin(), false);
+    JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin());
     if (!aobj)
         return JS_FALSE;
     vp->setObject(*aobj);
 
     return JS_TRUE;
 }
 
 static JSBool
@@ -2337,17 +2337,17 @@ DefinePropertyOnArray(JSContext *cx, JSO
          */
         if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
             return JS_FALSE;
         if (!*rval)
             return Reject(cx, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
 
         if (index >= oldLen) {
             JS_ASSERT(index != UINT32_MAX);
-            obj->setSlowArrayLength(index + 1);
+            obj->setArrayLength(index + 1);
         }
 
         *rval = true;
         return JS_TRUE;
     }
 
     return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
 }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -469,55 +469,38 @@ struct JSObject {
 
     /*
      * Array-specific getters and setters (for both dense and slow arrays).
      */
 
     // Used by dense and slow arrays.
     static const uint32 JSSLOT_ARRAY_LENGTH = JSSLOT_PRIVATE;
 
-  private:
-    // Used only by dense arrays.
-    static const uint32 JSSLOT_DENSE_ARRAY_COUNT     = JSSLOT_PRIVATE + 1;
-    static const uint32 JSSLOT_DENSE_ARRAY_MINLENCAP = JSSLOT_PRIVATE + 2;
-
     // This assertion must remain true;  see comment in js_MakeArraySlow().
     // (Nb: This method is never called, it just contains a static assertion.
     // The static assertion isn't inline because that doesn't work on Mac.)
     inline void staticAssertArrayLengthIsInPrivateSlot();
 
-    inline uint32 uncheckedGetArrayLength() const;
-    inline uint32 uncheckedGetDenseArrayCapacity() const;
-
   public:
     static const uint32 DENSE_ARRAY_FIXED_RESERVED_SLOTS = 3;
 
     inline uint32 getArrayLength() const;
-    inline void setDenseArrayLength(uint32 length);
-    inline void setSlowArrayLength(uint32 length);
-
-    inline uint32 getDenseArrayCount() const;
-    inline void setDenseArrayCount(uint32 count);
-    inline void incDenseArrayCountBy(uint32 posDelta);
-    inline void decDenseArrayCountBy(uint32 negDelta);
+    inline void setArrayLength(uint32 length);
 
     inline uint32 getDenseArrayCapacity() const;
     inline void setDenseArrayCapacity(uint32 capacity); // XXX: bug 558263 will remove this
 
-    inline bool isDenseArrayMinLenCapOk(bool strictAboutLength = true) const;
-
     inline const js::Value &getDenseArrayElement(uint32 i) const;
     inline js::Value *addressOfDenseArrayElement(uint32 i);
     inline void setDenseArrayElement(uint32 i, const js::Value &v);
 
     inline js::Value *getDenseArrayElements() const;   // returns pointer to the Array's elements array
-    bool resizeDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap,
-                               bool initializeAllSlots = true);
-    bool ensureDenseArrayElements(JSContext *cx, uint32 newcap,
-                               bool initializeAllSlots = true);
+    bool growDenseArrayElements(JSContext *cx, uint32 oldcap, uint32 newcap);
+    bool ensureDenseArrayElements(JSContext *cx, uint32 newcap);
+    bool shrinkDenseArrayElements(JSContext *cx, uint32 newcap);
     inline void freeDenseArrayElements(JSContext *cx);
 
     inline void voidDenseOnlyArraySlots();  // used when converting a dense array to a slow array
 
     JSBool makeDenseArraySlow(JSContext *cx);
 
     /*
      * Arguments-specific getters and setters.
@@ -581,16 +564,17 @@ struct JSObject {
   private:
     static const uint32 JSSLOT_REGEXP_LAST_INDEX = JSSLOT_PRIVATE + 1;
 
   public:
     static const uint32 REGEXP_FIXED_RESERVED_SLOTS = 1;
 
     inline const js::Value &getRegExpLastIndex() const;
     inline void setRegExpLastIndex(const js::Value &v);
+    inline void setRegExpLastIndex(jsdouble d);
     inline void zeroRegExpLastIndex();
 
     /*
      * Iterator-specific getters and setters.
      */
 
     inline NativeIterator *getNativeIterator() const;
     inline void setNativeIterator(NativeIterator *);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -127,116 +127,44 @@ JSObject::setPrimitiveThis(const js::Val
 }
 
 inline void
 JSObject::staticAssertArrayLengthIsInPrivateSlot()
 {
     JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE);
 }
 
-inline bool
-JSObject::isDenseArrayMinLenCapOk(bool strictAboutLength) const
-{
-    JS_ASSERT(isDenseArray());
-
-    // This function can be called while the LENGTH and MINLENCAP slots are
-    // still set to JSVAL_VOID and there are no dslots (ie. the capacity is
-    // zero).  If 'strictAboutLength' is false we allow this.
-    if (!strictAboutLength &&
-        fslots[JSSLOT_ARRAY_LENGTH].isUndefined() &&
-        uncheckedGetDenseArrayCapacity() == 0) {
-        return true;
-    }
-
-    uint32 length = uncheckedGetArrayLength();
-    uint32 capacity = uncheckedGetDenseArrayCapacity();
-    uint32 minLenCap = fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].toPrivateUint32();
-    return minLenCap == JS_MIN(length, capacity);
-}
-
-inline uint32
-JSObject::uncheckedGetArrayLength() const
-{
-    return fslots[JSSLOT_ARRAY_LENGTH].toPrivateUint32();
-}
-
 inline uint32
 JSObject::getArrayLength() const
 {
     JS_ASSERT(isArray());
-    JS_ASSERT_IF(isDenseArray(), isDenseArrayMinLenCapOk());
-    return uncheckedGetArrayLength();
-}
-
-inline void 
-JSObject::setDenseArrayLength(uint32 length)
-{
-    JS_ASSERT(isDenseArray());
-    fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length);
-    uint32 capacity = uncheckedGetDenseArrayCapacity();
-    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setPrivateUint32(JS_MIN(length, capacity));
+    return fslots[JSSLOT_ARRAY_LENGTH].toPrivateUint32();
 }
 
 inline void 
-JSObject::setSlowArrayLength(uint32 length)
-{
-    JS_ASSERT(isSlowArray());
-    fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length);
-}
-
-inline uint32 
-JSObject::getDenseArrayCount() const
-{
-    JS_ASSERT(isDenseArray());
-    return fslots[JSSLOT_DENSE_ARRAY_COUNT].toPrivateUint32();
-}
-
-inline void 
-JSObject::setDenseArrayCount(uint32 count)
+JSObject::setArrayLength(uint32 length)
 {
-    JS_ASSERT(isDenseArray());
-    fslots[JSSLOT_DENSE_ARRAY_COUNT].setPrivateUint32(count);
-}
-
-inline void 
-JSObject::incDenseArrayCountBy(uint32 posDelta)
-{
-    JS_ASSERT(isDenseArray());
-    fslots[JSSLOT_DENSE_ARRAY_COUNT].getPrivateUint32Ref() += posDelta;
-}
-
-inline void 
-JSObject::decDenseArrayCountBy(uint32 negDelta)
-{
-    JS_ASSERT(isDenseArray());
-    fslots[JSSLOT_DENSE_ARRAY_COUNT].getPrivateUint32Ref() -= negDelta;
-}
-
-inline uint32
-JSObject::uncheckedGetDenseArrayCapacity() const
-{
-    return dslots ? dslots[-1].toPrivateUint32() : 0;
+    JS_ASSERT(isArray());
+    fslots[JSSLOT_ARRAY_LENGTH].setPrivateUint32(length);
 }
 
 inline uint32
 JSObject::getDenseArrayCapacity() const
 {
     JS_ASSERT(isDenseArray());
-    JS_ASSERT(isDenseArrayMinLenCapOk(/* strictAboutLength = */false));
-    return uncheckedGetDenseArrayCapacity();
+    JS_ASSERT(dslots);
+    return dslots[-1].toPrivateUint32();
 }
 
 inline void
 JSObject::setDenseArrayCapacity(uint32 capacity)
 {
     JS_ASSERT(isDenseArray());
     JS_ASSERT(dslots);
     dslots[-1].setPrivateUint32(capacity);
-    uint32 length = uncheckedGetArrayLength();
-    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setPrivateUint32(JS_MIN(length, capacity));
 }
 
 inline const js::Value &
 JSObject::getDenseArrayElement(uint32 i) const
 {
     JS_ASSERT(isDenseArray());
     JS_ASSERT(i < getDenseArrayCapacity());
     return dslots[i];
@@ -268,26 +196,22 @@ JSObject::getDenseArrayElements() const
 inline void
 JSObject::freeDenseArrayElements(JSContext *cx)
 {
     JS_ASSERT(isDenseArray());
     if (dslots) {
         cx->free(dslots - 1);
         dslots = NULL;
     }
-    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setPrivateUint32(0);
-    JS_ASSERT(isDenseArrayMinLenCapOk(/* strictAboutLength = */false));
 }
 
 inline void 
 JSObject::voidDenseOnlyArraySlots()
 {
     JS_ASSERT(isDenseArray());
-    fslots[JSSLOT_DENSE_ARRAY_COUNT].setUndefined();
-    fslots[JSSLOT_DENSE_ARRAY_MINLENCAP].setUndefined();
 }
 
 inline void
 JSObject::setArgsLength(uint32 argc)
 {
     JS_ASSERT(isArguments());
     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
     fslots[JSSLOT_ARGS_LENGTH].setInt32(argc << 1);
@@ -379,37 +303,16 @@ JSObject::getDateUTCTime() const
 
 inline void 
 JSObject::setDateUTCTime(const js::Value &time)
 {
     JS_ASSERT(isDate());
     fslots[JSSLOT_DATE_UTC_TIME] = time;
 }
 
-inline const js::Value &
-JSObject::getRegExpLastIndex() const
-{
-    JS_ASSERT(isRegExp());
-    return fslots[JSSLOT_REGEXP_LAST_INDEX];
-}
-
-inline void
-JSObject::setRegExpLastIndex(const js::Value &v)
-{
-    JS_ASSERT(isRegExp());
-    fslots[JSSLOT_REGEXP_LAST_INDEX] = v;
-}
-
-inline void 
-JSObject::zeroRegExpLastIndex()
-{
-    JS_ASSERT(isRegExp());
-    fslots[JSSLOT_REGEXP_LAST_INDEX].setInt32(0);
-}
-
 inline NativeIterator *
 JSObject::getNativeIterator() const
 {
     return (NativeIterator *) getPrivate();
 }
 
 inline void
 JSObject::setNativeIterator(NativeIterator *ni)
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -44,22 +44,28 @@
  */
 #include "jstypes.h"
 #include "jscompat.h"
 #include "jsval.h"
 
 JS_BEGIN_EXTERN_C
 
 /* Scalar typedefs. */
-typedef uint16    jschar;
 typedef int32     jsint;
 typedef uint32    jsuint;
 typedef float64   jsdouble;
 typedef int32     jsrefcount;   /* PRInt32 if JS_THREADSAFE, see jslock.h */
 
+#ifdef WIN32
+typedef wchar_t   jschar;
+#else
+typedef uint16    jschar;
+#endif
+
+
 /*
  * Run-time version enumeration.  See jsversion.h for compile-time counterparts
  * to these values that may be selected by the JS_VERSION macro, and tested by
  * #if expressions.
  */
 typedef enum JSVersion {
     JSVERSION_1_0     = 100,
     JSVERSION_1_1     = 110,
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -5081,23 +5081,16 @@ js_ExecuteRegExp(JSContext *cx, JSRegExp
 
 out:
     JS_ARENA_RELEASE(&cx->regexpPool, mark);
     return ok;
 }
 
 /************************************************************************/
 
-static void
-SetRegExpLastIndex(JSContext *cx, JSObject *obj, jsdouble lastIndex)
-{
-    JS_ASSERT(obj->isRegExp());
-    obj->setRegExpLastIndex(NumberValue(lastIndex));
-}
-
 #define DEFINE_GETTER(name, code)                                              \
     static JSBool                                                              \
     name(JSContext *cx, JSObject *obj, jsid id, Value *vp)                     \
     {                                                                          \
         while (obj->getClass() != &js_RegExpClass) {                           \
             obj = obj->getProto();                                             \
             if (!obj)                                                          \
                 return true;                                                   \
@@ -5120,21 +5113,17 @@ DEFINE_GETTER(sticky_getter,     vp->set
 static JSBool
 lastIndex_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     while (obj->getClass() != &js_RegExpClass) {
         obj = obj->getProto();
         if (!obj)
             return true;
     }
-    jsdouble lastIndex;
-    if (!ValueToNumber(cx, *vp, &lastIndex))
-        return false;
-    lastIndex = js_DoubleToInteger(lastIndex);
-    SetRegExpLastIndex(cx, obj, lastIndex);
+    obj->setRegExpLastIndex(*vp);
     return true;
 }
 
 static const struct LazyProp {
     const char *name;
     uint16 atomOffset;
     PropertyOp getter;
 } lazyRegExpProps[] = {
@@ -5648,17 +5637,26 @@ regexp_exec_sub(JSContext *cx, JSObject 
         JS_UNLOCK_OBJ(cx, obj);
         return JS_TRUE;
     }
 
     /* NB: we must reach out: after this paragraph, in order to drop re. */
     HOLD_REGEXP(cx, re);
     sticky = (re->flags & JSREG_STICKY) != 0;
     if (re->flags & (JSREG_GLOB | JSREG_STICKY)) {
-        lastIndex = obj->getRegExpLastIndex().toNumber();
+        const Value &v = obj->getRegExpLastIndex();
+        if (v.isInt32()) {
+            lastIndex = v.toInt32();
+        } else {
+            if (v.isDouble())
+                lastIndex = v.toDouble();
+            else if (!ValueToNumber(cx, v, &lastIndex))
+                return JS_FALSE;
+            lastIndex = js_DoubleToInteger(lastIndex);
+        }
     } else {
         lastIndex = 0;
     }
     JS_UNLOCK_OBJ(cx, obj);
 
     /* Now that obj is unlocked, it's safe to (potentially) grab the GC lock. */
     if (argc == 0) {
         str = cx->regExpStatics.input;
@@ -5692,17 +5690,17 @@ regexp_exec_sub(JSContext *cx, JSObject 
     } else {
         i = (size_t) lastIndex;
         ok = js_ExecuteRegExp(cx, re, str, &i, test, rval);
         if (ok &&
             ((re->flags & JSREG_GLOB) || (!rval->isNull() && sticky))) {
             if (rval->isNull())
                 obj->zeroRegExpLastIndex();
             else
-                SetRegExpLastIndex(cx, obj, i);
+                obj->setRegExpLastIndex(i);
         }
     }
 
 out:
     DROP_REGEXP(cx, re);
     return ok;
 }
 
--- a/js/src/jsregexp.h
+++ b/js/src/jsregexp.h
@@ -45,16 +45,44 @@
 #include <stddef.h>
 #include "jspubtd.h"
 #include "jsstr.h"
 
 #ifdef JS_THREADSAFE
 #include "jsdhash.h"
 #endif
 
+inline const js::Value &
+JSObject::getRegExpLastIndex() const
+{
+    JS_ASSERT(isRegExp());
+    return fslots[JSSLOT_REGEXP_LAST_INDEX];
+}
+
+inline void
+JSObject::setRegExpLastIndex(const js::Value &v)
+{
+    JS_ASSERT(isRegExp());
+    fslots[JSSLOT_REGEXP_LAST_INDEX] = v;
+}
+
+inline void
+JSObject::setRegExpLastIndex(jsdouble d)
+{
+    JS_ASSERT(isRegExp());
+    fslots[JSSLOT_REGEXP_LAST_INDEX] = js::NumberValue(d);
+}
+
+inline void
+JSObject::zeroRegExpLastIndex()
+{
+    JS_ASSERT(isRegExp());
+    fslots[JSSLOT_REGEXP_LAST_INDEX].setInt32(0);
+}
+
 namespace js { class AutoStringRooter; }
 
 extern JS_FRIEND_API(void)
 js_SaveAndClearRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
                              js::AutoStringRooter *tvr);
 
 extern JS_FRIEND_API(void)
 js_RestoreRegExpStatics(JSContext *cx, JSRegExpStatics *statics,
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -81,17 +81,17 @@ js_GenerateShape(JSContext *cx, bool gcL
         /*
          * FIXME bug 440834: The shape id space has overflowed. Currently we
          * cope badly with this and schedule the GC on the every call. But
          * first we make sure that increments from other threads would not
          * have a chance to wrap around shapeGen to zero.
          */
         rt->shapeGen = SHAPE_OVERFLOW_BIT;
         shape = SHAPE_OVERFLOW_BIT;
-        js_TriggerGC(cx, gcLocked);
+        cx->triggerGC();
     }
     return shape;
 }
 
 JSScope *
 js_GetMutableScope(JSContext *cx, JSObject *obj)
 {
     JSScope *scope = obj->scope();
@@ -195,17 +195,17 @@ JSScope::createTable(JSContext *cx, bool
 
     table = (JSScopeProperty **) js_calloc(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
     if (!table) {
         if (report)
             JS_ReportOutOfMemory(cx);
         METER(tableAllocFails);
         return false;
     }
-    cx->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
+    cx->runtime->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
 
     hashShift = JS_DHASH_BITS - sizeLog2;
     for (sprop = lastProp; sprop; sprop = sprop->parent) {
         spp = search(sprop->id, true);
         SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
     }
     return true;
 }
@@ -511,17 +511,17 @@ JSScope::changeTable(JSContext *cx, int 
 
     /* Now that we have newtable allocated, update members. */
     hashShift = JS_DHASH_BITS - newlog2;
     removedCount = 0;
     oldtable = table;
     table = newtable;
 
     /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */
-    cx->updateMallocCounter(nbytes);
+    cx->runtime->updateMallocCounter(nbytes);
 
     /* Copy only live entries, leaving removed and free ones behind. */
     for (oldspp = oldtable; oldsize != 0; oldspp++) {
         sprop = SPROP_FETCH(oldspp);
         if (sprop) {
             spp = search(sprop->id, true);
             JS_ASSERT(SPROP_IS_FREE(*spp));
             *spp = sprop;
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -747,25 +747,28 @@ extern const bool js_alnum[];
 #define JS_ISXMLNSSTART(c)      ((JS_CCODE(c) & 0x00000100) || (c) == '_')
 #define JS_ISXMLNS(c)           ((JS_CCODE(c) & 0x00000080) || (c) == '.' ||  \
                                  (c) == '-' || (c) == '_')
 #define JS_ISXMLNAMESTART(c)    (JS_ISXMLNSSTART(c) || (c) == ':')
 #define JS_ISXMLNAME(c)         (JS_ISXMLNS(c) || (c) == ':')
 
 #define JS_ISDIGIT(c)   (JS_CTYPE(c) == JSCT_DECIMAL_DIGIT_NUMBER)
 
+const jschar BYTE_ORDER_MARK = 0xFEFF;
+const jschar NO_BREAK_SPACE  = 0x00A0;
+
 static inline bool
 JS_ISSPACE(jschar c)
 {
     unsigned w = c;
 
     if (w < 256)
-        return (w <= ' ' && (w == ' ' || (9 <= w && w <= 0xD))) || w == 0xA0;
+        return (w <= ' ' && (w == ' ' || (9 <= w && w <= 0xD))) || w == NO_BREAK_SPACE;
 
-    return (JS_CCODE(w) & 0x00070000) == 0x00040000;
+    return w == BYTE_ORDER_MARK || (JS_CCODE(w) & 0x00070000) == 0x00040000;
 }
 
 #define JS_ISPRINT(c)   ((c) < 128 && isprint(c))
 
 #define JS_ISUPPER(c)   (JS_CTYPE(c) == JSCT_UPPERCASE_LETTER)
 #define JS_ISLOWER(c)   (JS_CTYPE(c) == JSCT_LOWERCASE_LETTER)
 
 #define JS_TOUPPER(c)   ((jschar) ((JS_CCODE(c) & 0x00100000)                 \
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2338,22 +2338,23 @@ TraceRecorder::TraceRecorder(JSContext* 
 
     /* Finish handling RECURSIVE_SLURP_FAIL_EXIT in startRecorder. */
     if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT)
         return;
 
     if (fragment == fragment->root) {
         /*
          * We poll the operation callback request flag. It is updated asynchronously whenever
-         * the callback is to be invoked.
+         * the callback is to be invoked. We can use INS_CONSTPTR here as JIT-ed code is per
+         * thread and cannot outlive the corresponding JSThreaData.
          */
         // XXX: this load is volatile.  If bug 545406 (loop-invariant code
         // hoisting) is implemented this fact will need to be made explicit.
-        LIns* x =
-            lir->insLoad(LIR_ldi, cx_ins, offsetof(JSContext, interruptFlags), ACC_LOAD_ANY);
+        LIns* flagptr = INS_CONSTPTR((void *) &JS_THREAD_DATA(cx)->interruptFlags);
+        LIns* x = lir->insLoad(LIR_ldi, flagptr, 0, ACC_LOAD_ANY);
         guard(true, lir->insEqI_0(x), snapshot(TIMEOUT_EXIT));
     }
 
     /*
      * If we are attached to a tree call guard, make sure the guard the inner
      * tree exited from is what we expect it to be.
      */
     if (anchor && anchor->exitType == NESTED_EXIT) {
@@ -2956,17 +2957,16 @@ public:
         mCx(cx),
         mTypeMap(typeMap),
         mGlobal(global)
     {}
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE void
     visitGlobalSlot(Value *vp, unsigned n, unsigned slot) {
         debug_only_printf(LC_TMTracer, "global%d=", n);
-        JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota);
         NativeToValue(mCx, *vp, *mTypeMap++, &mGlobal[slot]);
     }
 };
 
 class FlushNativeStackFrameVisitor : public SlotVisitorBase
 {
     JSContext *mCx;
     const JSValueType *mInitTypeMap;
@@ -2990,33 +2990,31 @@ public:
 
     const JSValueType* getTypeMap()
     {
         return mTypeMap;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitStackSlots(Value *vp, size_t count, JSStackFrame* fp) {
-        JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota);
         for (size_t i = 0; i < count; ++i) {
             if (vp == mStop)
                 return false;
             debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i));
             if (unsigned(mTypeMap - mInitTypeMap) >= mIgnoreSlots)
                 NativeToValue(mCx, *vp, *mTypeMap, mStack);
             vp++;
             mTypeMap++;
             mStack++;
         }
         return true;
     }
 
     JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
     visitFrameObjPtr(JSObject **p, JSStackFrame* fp) {
-        JS_ASSERT(JS_THREAD_DATA(mCx)->waiveGCQuota);
         if ((Value *)p == mStop)
             return false;
         debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0);
         if (unsigned(mTypeMap - mInitTypeMap) >= mIgnoreSlots) {
             *p = *(JSObject **)mStack;
 #ifdef DEBUG
             JSValueType type = *mTypeMap;
             if (type == JSVAL_TYPE_NULL) {
@@ -6700,41 +6698,23 @@ ExecuteTree(JSContext* cx, TreeFragment*
     LeaveTree(tm, state, lr);
 
     *lrp = state.innermost;
     bool ok = !(state.builtinStatus & BUILTIN_ERROR);
     JS_ASSERT_IF(cx->throwing, !ok);
     return ok;
 }
 
-class Guardian {
-    bool *flagp;
-public:
-    Guardian(bool *flagp) {
-        this->flagp = flagp;
-        JS_ASSERT(!*flagp);
-        *flagp = true;
-    }
-
-    ~Guardian() {
-        JS_ASSERT(*flagp);
-        *flagp = false;
-    }
-};
-
 static JS_FORCES_STACK void
 LeaveTree(TraceMonitor *tm, TracerState& state, VMSideExit* lr)
 {
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
     JSContext* cx = state.cx;
 
-    /* Temporary waive the soft GC quota to make sure LeaveTree() doesn't fail. */
-    Guardian waiver(&JS_THREAD_DATA(cx)->waiveGCQuota);
-
     FrameInfo** callstack = state.callstackBase;
     double* stack = state.stackBase;
 
     /*
      * Except if we find that this is a nested bailout, the guard the call
      * returned is the one we have to use to adjust pc and sp.
      */
     VMSideExit* innermost = lr;
@@ -7111,17 +7091,17 @@ MonitorLoopEdge(JSContext* cx, uintN& in
     SlotList* globalSlots = NULL;
 
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) {
         Backoff(cx, cx->regs->pc);
         return MONITOR_NOT_RECORDING;
     }
 
     /* Do not enter the JIT code with a pending operation callback. */
-    if (cx->interruptFlags) {
+    if (JS_THREAD_DATA(cx)->interruptFlags) {
 #ifdef MOZ_TRACEVIS
         tvso.r = R_CALLBACK_PENDING;
 #endif
         return MONITOR_NOT_RECORDING;
     }
 
     jsbytecode* pc = cx->regs->pc;
     uint32 argc = cx->fp->argc;
@@ -11018,32 +10998,29 @@ TraceRecorder::newArray(JSObject* ctor, 
     if (argc == 0) {
         // arr_ins = js_NewEmptyArray(cx, Array.prototype)
         LIns *args[] = { proto_ins, cx_ins };
         arr_ins = lir->insCall(&js_NewEmptyArray_ci, args);
         guard(false, lir->insEqP_0(arr_ins), OOM_EXIT);
     } else if (argc == 1 && argv[0].isNumber()) {
         // arr_ins = js_NewEmptyArray(cx, Array.prototype, length)
         LIns *args[] = { d2i(get(argv)), proto_ins, cx_ins }; // FIXME: is this 64-bit safe?
-        arr_ins = lir->insCall(&js_NewEmptyArrayWithLength_ci, args);
+        arr_ins = lir->insCall(&js_NewArrayWithSlots_ci, args);
         guard(false, lir->insEqP_0(arr_ins), OOM_EXIT);
     } else {
         // arr_ins = js_NewArrayWithSlots(cx, Array.prototype, argc)
         LIns *args[] = { INS_CONST(argc), proto_ins, cx_ins };
         arr_ins = lir->insCall(&js_NewArrayWithSlots_ci, args);
         guard(false, lir->insEqP_0(arr_ins), OOM_EXIT);
 
         // arr->dslots[i] = box_jsval(vp[i]);  for i in 0..argc
         LIns *dslots_ins = NULL;
         for (uint32 i = 0; i < argc && !outOfMemory(); i++) {
             stobj_set_dslot(arr_ins, i, dslots_ins, argv[i], get(&argv[i]));
         }
-
-        if (argc > 0)
-            set_array_fslot(arr_ins, JSObject::JSSLOT_DENSE_ARRAY_COUNT, argc);
     }
 
     set(rval, arr_ins);
     pendingSpecializedNative = IGNORE_NATIVE_CALL_COMPLETE_CALLBACK;
     return RECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK void
@@ -13652,47 +13629,45 @@ TraceRecorder::denseArrayElement(Value& 
     JS_ASSERT(oval.isObject() && ival.isInt32());
 
     JSObject* obj = &oval.toObject();
     LIns* obj_ins = get(&oval);
     jsint idx = ival.toInt32();
     LIns* idx_ins = makeNumberInt32(get(&ival));
 
     VMSideExit* exit = snapshot(BRANCH_EXIT);
-    /* check that the index is within bounds */
+
+    /*
+     * Arrays have both a length and a capacity, but we only need to check
+     * |index < capacity|;  in the case where |length < index < capacity|
+     * the entries [length..capacity-1] will have already been marked as
+     * holes by resizeDenseArrayElements() so we can read them and get
+     * the correct value.
+     */
     LIns* dslots_ins =
         addName(lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots), ACC_OTHER), "dslots");
+    LIns* capacity_ins =
+        addName(lir->insLoad(LIR_ldi, dslots_ins, -int(sizeof(jsval)), ACC_OTHER), "capacity");
+
     jsuint capacity = obj->getDenseArrayCapacity();
-    bool within = (jsuint(idx) < obj->getArrayLength() && jsuint(idx) < capacity);
+    bool within = (jsuint(idx) < capacity);
     if (!within) {
-        /* If not idx < min(length, capacity), stay on trace (and read value as undefined). */
-        JS_ASSERT(obj->isDenseArrayMinLenCapOk());
-        LIns* minLenCap =
-            addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_MINLENCAP), "minLenCap");
-        LIns* br = lir->insBranch(LIR_jf,
-                                  lir->ins2(LIR_ltui, idx_ins, minLenCap),
-                                  NULL);
-
-        lir->insGuard(LIR_x, NULL, createGuardRecord(exit));
-        LIns* label = lir->ins0(LIR_label);
-        br->setTarget(label);
+        /* If not idx < capacity, stay on trace (and read value as undefined). */
+        guard(true, lir->ins2(LIR_geui, idx_ins, capacity_ins), exit);
 
         CHECK_STATUS(guardPrototypeHasNoIndexedProperties(obj, obj_ins, MISMATCH_EXIT));
 
         // Return undefined and indicate that we didn't actually read this (addr_ins).
         v_ins = INS_UNDEFINED();
         addr_ins = NULL;
         return RECORD_CONTINUE;
     }
 
-    /* Guard array min(length, capacity). */
-    JS_ASSERT(obj->isDenseArrayMinLenCapOk());
-    LIns* minLenCap =
-        addName(stobj_get_fslot_uint32(obj_ins, JSObject::JSSLOT_DENSE_ARRAY_MINLENCAP), "minLenCap");
-    guard(true, lir->ins2(LIR_ltui, idx_ins, minLenCap), exit);
+    /* Guard that index is within capacity. */
+    guard(true, lir->ins2(LIR_ltui, idx_ins, capacity_ins), exit);
 
     /* Load the value and guard on its type to unbox it. */
     vp = &obj->dslots[jsuint(idx)];
 	JS_ASSERT(sizeof(Value) == 8); // The |3| in the following statement requires this.
     addr_ins = lir->ins2(LIR_addp, dslots_ins,
                          lir->ins2ImmI(LIR_lshp, lir->insUI2P(idx_ins), 3));
     v_ins = unbox_value(*vp, addr_ins, 0, exit, true);
 
@@ -14666,40 +14641,41 @@ TraceRecorder::record_JSOP_IN()
      * comparison. This way the value of the condition doesn't have to be
      * calculated and saved on the stack in most cases.
      */
     set(&lval, x);
     return ARECORD_CONTINUE;
 }
 
 static JSBool FASTCALL
-HasInstance(JSContext* cx, JSObject* ctor, ValueArgType arg)
+HasInstanceOnTrace(JSContext* cx, JSObject* ctor, ValueArgType arg)
 {
     const Value &argref = ValueArgToConstRef(arg);
     JSBool result = JS_FALSE;
-    if (!js_HasInstance(cx, ctor, &argref, &result))
+    if (!HasInstance(cx, ctor, &argref, &result))
         SetBuiltinError(cx);
     return result;
 }
-JS_DEFINE_CALLINFO_3(static, BOOL_FAIL, HasInstance, CONTEXT, OBJECT, VALUE, 0, ACC_STORE_ANY)
+JS_DEFINE_CALLINFO_3(static, BOOL_FAIL, HasInstanceOnTrace, CONTEXT, OBJECT, VALUE, 0,
+                     ACC_STORE_ANY)
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_INSTANCEOF()
 {
     // If the rhs isn't an object, we are headed for a TypeError.
     Value& ctor = stackval(-1);
     if (ctor.isPrimitive())
         RETURN_STOP_A("non-object on rhs of instanceof");
 
     Value& val = stackval(-2);
     LIns* val_ins = box_value_for_native_call(val, get(&val));
 
     enterDeepBailCall();
     LIns* args[] = {val_ins, get(&ctor), cx_ins};
-    stack(-2, lir->insCall(&HasInstance_ci, args));
+    stack(-2, lir->insCall(&HasInstanceOnTrace_ci, args));
     LIns* status_ins = lir->insLoad(LIR_ldi,
                                     lirbuf->state,
                                     offsetof(TracerState, builtinStatus), ACC_OTHER);
     pendingGuardCondition = lir->insEqI_0(status_ins);
     leaveDeepBailCall();
 
     return ARECORD_CONTINUE;
 }
@@ -15716,19 +15692,16 @@ TraceRecorder::record_JSOP_NEWARRAY()
     uint32 count = 0;
     for (uint32 i = 0; i < len; i++) {
         Value& v = stackval(int(i) - int(len));
         if (!v.isMagic())
             count++;
         stobj_set_dslot(v_ins, i, dslots_ins, v, get(&v));
     }
 
-    if (count > 0)
-        set_array_fslot(v_ins, JSObject::JSSLOT_DENSE_ARRAY_COUNT, count);
-
     stack(-int(len), v_ins);
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_HOLE()
 {
     stack(0, INS_CONST(JS_ARRAY_HOLE));
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -1273,17 +1273,23 @@ mjit::Compiler::generateMethod()
           }
           END_CASE(JSOP_LAMBDA_FC)
 
           BEGIN_CASE(JSOP_TRACE)
           {
             if (analysis[PC].nincoming > 0) {
                 RegisterID cxreg = frame.allocReg();
                 masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), cxreg);
-                Address flag(cxreg, offsetof(JSContext, interruptFlags));
+#ifdef JS_THREADSAFE
+                masm.loadPtr(Address(cxreg, offsetof(JSContext, thread)), cxreg);
+                Address flag(cxreg, offsetof(JSThread, data.interruptFlags));
+#else
+                masm.loadPtr(Address(cxreg, offsetof(JSContext, runtime)), cxreg);
+                Address flag(cxreg, offsetof(JSRuntime, threadData.interruptFlags));
+#endif
                 Jump jump = masm.branchTest32(Assembler::NonZero, flag);
                 frame.freeReg(cxreg);
                 stubcc.linkExit(jump, Uses(0));
                 stubcc.leave();
                 stubcc.call(stubs::Interrupt);
                 stubcc.rejoin(Changes(0));
             }
           }
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -610,37 +610,34 @@ stubs::SetElem(VMFrame &f)
 
     obj = ValueToObject(cx, &objval);
     if (!obj)
         THROW();
 
     if (!FetchElementId(f, obj, idval, id, &regs.sp[-2]))
         THROW();
 
-    if (obj->isDenseArray() && JSID_IS_INT(id)) {
-        jsuint length = obj->getDenseArrayCapacity();
-        jsint i = JSID_TO_INT(id);
-        
-        if ((jsuint)i < length) {
-            if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
-                if (js_PrototypeHasIndexedProperties(cx, obj))
-                    goto mid_setelem;
-                if ((jsuint)i >= obj->getArrayLength())
-                    obj->setDenseArrayLength(i + 1);
-                obj->incDenseArrayCountBy(1);
+    do {
+        if (obj->isDenseArray() && JSID_IS_INT(id)) {
+            jsuint length = obj->getDenseArrayCapacity();
+            jsint i = JSID_TO_INT(id);
+            if ((jsuint)i < length) {
+                if (obj->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE)) {
+                    if (js_PrototypeHasIndexedProperties(cx, obj))
+                        break;
+                    if ((jsuint)i >= obj->getArrayLength())
+                        obj->setArrayLength(i + 1);
+                }
+                obj->setDenseArrayElement(i, regs.sp[-1]);
+                goto end_setelem;
             }
-            obj->setDenseArrayElement(i, regs.sp[-1]);
-            goto end_setelem;
         }
-    }
-
-  mid_setelem:
+    } while (0);
     if (!obj->setProperty(cx, id, &regs.sp[-1]))
         THROW();
-
   end_setelem:
     /* :FIXME: Moving the assigned object into the lowest stack slot
      * is a temporary hack. What we actually want is an implementation
      * of popAfterSet() that allows popping more than one value;
      * this logic can then be handled in Compiler.cpp. */
     regs.sp[-3] = retval;
 }
 
@@ -1300,17 +1297,17 @@ stubs::Mod(VMFrame &f)
             regs.sp[-2].setDouble(d1);
         }
     }
 }
 
 JSObject *JS_FASTCALL
 stubs::NewArray(VMFrame &f, uint32 len)
 {
-    JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len, JS_TRUE);
+    JSObject *obj = js_NewArrayObject(f.cx, len, f.regs.sp - len);
     if (!obj)
         THROWV(NULL);
     return obj;
 }
 
 void JS_FASTCALL
 stubs::Interrupt(VMFrame &f)
 {
@@ -2262,17 +2259,17 @@ stubs::InstanceOf(VMFrame &f)
     if (rref.isPrimitive()) {
         js_ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                             -1, rref, NULL);
         THROWV(JS_FALSE);
     }
     JSObject *obj = &rref.toObject();
     const Value &lref = regs.sp[-2];
     JSBool cond = JS_FALSE;
-    if (!js_HasInstance(cx, obj, &lref, &cond))
+    if (!HasInstance(cx, obj, &lref, &cond))
         THROWV(JS_FALSE);
     f.regs.sp[-2].setBoolean(cond);
     return cond;
 }
 
 void JS_FASTCALL
 stubs::FastInstanceOf(VMFrame &f)
 {
--- a/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js
+++ b/js/src/tests/ecma/GlobalObject/15.1.2.2-1.js
@@ -213,16 +213,18 @@ for ( HEX_STRING = "0x0", HEX_VALUE = 0,
 for ( var space = " ", HEX_STRING = "0x0", HEX_VALUE = 0, POWER = 0;
       POWER < 15;
       POWER++, HEX_STRING = HEX_STRING +"f", space += " ")
 {
   new TestCase( SECTION, "parseInt("+space+HEX_STRING+space+", void 0)",    HEX_VALUE,  parseInt(space+HEX_STRING+space, void 0) );
   HEX_VALUE += Math.pow(16,POWER)*15;
 }
 
+new TestCase(SECTION, "parseInt(BOM + '123', 10)", 123, parseInt("\uFEFF" + "123", 10));
+
 // a few tests with negative numbers
 for ( HEX_STRING = "-0x0", HEX_VALUE = 0, POWER = 0; POWER < 15; POWER++, HEX_STRING = HEX_STRING +"f" ) {
   new TestCase( SECTION, "parseInt("+HEX_STRING+")",    HEX_VALUE,  parseInt(HEX_STRING) );
   HEX_VALUE -= Math.pow(16,POWER)*15;
 }
 
 // we should stop parsing when we get to a value that is not a numeric literal for the type we expect
 
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/RegExp/15.10.7.5-01.js
@@ -0,0 +1,72 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var gTestfile = '15.10.7.5-01.js';
+var BUGNUMBER = 465199;
+var summary = "RegExp lastIndex property set should not coerce type to number";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var called = false;
+var o = { valueOf: function() { called = true; return 1; } };
+var r = /a/g;
+var desc, m;
+
+assertEq(r.lastIndex, 0);
+
+desc = Object.getOwnPropertyDescriptor(r, "lastIndex");
+assertEq(desc.enumerable, false);
+assertEq(desc.configurable, false);
+assertEq(desc.value, 0);
+assertEq(desc.writable, true);
+
+r.lastIndex = o;
+
+assertEq(r.lastIndex, o);
+
+desc = Object.getOwnPropertyDescriptor(r, "lastIndex");
+assertEq(desc.enumerable, false);
+assertEq(desc.configurable, false);
+assertEq(desc.value, o);
+assertEq(desc.writable, true);
+
+assertEq(called, false);
+assertEq(r.exec("aaaa").length, 1);
+assertEq(called, true);
+assertEq(r.lastIndex, 2);
+
+desc = Object.getOwnPropertyDescriptor(r, "lastIndex");
+assertEq(desc.enumerable, false);
+assertEq(desc.configurable, false);
+assertEq(desc.value, 2);
+assertEq(desc.writable, true);
+
+
+r.lastIndex = -0.2;
+assertEq(r.lastIndex, -0.2);
+
+m = r.exec("aaaa");
+assertEq(Array.isArray(m), true);
+assertEq(m.length, 1);
+assertEq(m[0], "a");
+assertEq(r.lastIndex, 1);
+
+m = r.exec("aaaa");
+assertEq(Array.isArray(m), true);
+assertEq(m.length, 1);
+assertEq(m[0], "a");
+assertEq(r.lastIndex, 2);
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("All tests passed!");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/RegExp/browser.js
@@ -0,0 +1,1 @@
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/RegExp/jstests.list
@@ -0,0 +1,2 @@
+url-prefix ../../jsreftest.html?test=ecma_5/RegExp/
+script 15.10.7.5-01.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/RegExp/shell.js
@@ -0,0 +1,1 @@
+gTestsubsuite='RegExp';
--- a/js/src/tests/ecma_5/jstests.list
+++ b/js/src/tests/ecma_5/jstests.list
@@ -1,9 +1,10 @@
 include Array/jstests.list
 include Date/jstests.list
 include Expressions/jstests.list
 include JSON/jstests.list
 include Object/jstests.list
+include RegExp/jstests.list
 include Types/jstests.list
 include extensions/jstests.list
 include misc/jstests.list
 include strict/jstests.list
--- a/js/src/tests/js1_8_5/extensions/jstests.list
+++ b/js/src/tests/js1_8_5/extensions/jstests.list
@@ -5,8 +5,9 @@ skip-if(!xulRuntime.shell) script worker
 skip-if(!xulRuntime.shell) script worker-error-propagation.js
 skip-if(!xulRuntime.shell) script worker-fib.js
 skip-if(!xulRuntime.shell) script worker-init.js
 skip-if(!xulRuntime.shell) script worker-simple.js
 skip-if(!xulRuntime.shell) script worker-terminate.js
 skip-if(!xulRuntime.shell) script worker-timeout.js
 script scripted-proxies.js
 script array-length-protochange.js
+script proxy-enumerateOwn-duplicates.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/extensions/proxy-enumerateOwn-duplicates.js
@@ -0,0 +1,32 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/licenses/publicdomain/
+
+var gTestfile = 'proxy-enumerateOwn-duplicates.js';
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 580200;
+var summary =
+  'Assertion failure enumerating own properties of proxy returning ' +
+  'duplicated own property name';
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+var x = Proxy.create({ enumerateOwn: function() { return ["0","0"]; } }, [1,2]);
+var ax = Object.getOwnPropertyNames(x);
+assertEq(ax.length, 1, "array: " + ax);
+assertEq(ax[0], "0");
+
+var p = Proxy.create({ enumerateOwn: function() { return ["1","1"]; } }, null);
+var ap = Object.getOwnPropertyNames(p);
+assertEq(ap.length, 1, "array: " + ap);
+assertEq(ap[0], "1");
+
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("All tests passed!");
--- a/modules/plugin/base/src/nsNPAPIPlugin.cpp
+++ b/modules/plugin/base/src/nsNPAPIPlugin.cpp
@@ -1555,16 +1555,18 @@ bool NP_CALLBACK
   NPPAutoPusher nppPusher(npp);
 
   nsIDocument *doc = GetDocumentFromNPP(npp);
   NS_ENSURE_TRUE(doc, false);
 
   JSContext *cx = GetJSContextFromDoc(doc);
   NS_ENSURE_TRUE(cx, false);
 
+  JSAutoRequest req(cx);
+
   nsCOMPtr<nsIScriptContext> scx = GetScriptContextFromJSContext(cx);
   NS_ENSURE_TRUE(scx, false);
 
   JSObject *obj =
     nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj);
 
   if (!obj) {
     return false;