Bug 737365 - stop using the cx during finalization, part 1.
authorIgor Bukanov <igor@mir2.org>
Mon, 19 Mar 2012 15:34:55 +0100
changeset 90964 d5057ff02ffb9786f62ca69ebc22cf11aa86f612
parent 90963 523d5c5c53eb9f18f460bdedad4c824cc1f6b0fe
child 90965 74053b148a3c8883ad1a375107c94359606f6e1e
push id7945
push userigor@mir2.org
push dateWed, 04 Apr 2012 10:36:17 +0000
treeherdermozilla-inbound@d8c5316f513a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs737365
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 737365 - stop using the cx during finalization, part 1. This part changes the signatures for various finalization API to take not JSContext* but rather either JSFreeOp structure or its library-private counterpart FreeOp. These structures wrap parameters that are passed to the finalizers removing most of explicit dependencies on JSContext in the finalization code.
content/xbl/src/nsXBLBinding.cpp
content/xbl/src/nsXBLDocumentInfo.cpp
content/xul/document/src/nsXULPrototypeDocument.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfo.h
dom/base/nsGlobalWindow.cpp
dom/bindings/Codegen.py
dom/plugins/base/nsJSNPRuntime.cpp
dom/workers/DOMBindingBase.cpp
dom/workers/DOMBindingBase.h
dom/workers/EventListenerManager.cpp
dom/workers/EventListenerManager.h
dom/workers/EventTarget.cpp
dom/workers/EventTarget.h
dom/workers/Events.cpp
dom/workers/Exceptions.cpp
dom/workers/File.cpp
dom/workers/FileReaderSync.cpp
dom/workers/Location.cpp
dom/workers/Navigator.cpp
dom/workers/Worker.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerPrivate.h
dom/workers/WorkerScope.cpp
dom/workers/XMLHttpRequest.cpp
dom/workers/XMLHttpRequest.h
dom/workers/XMLHttpRequestEventTarget.cpp
dom/workers/XMLHttpRequestEventTarget.h
dom/workers/XMLHttpRequestUpload.cpp
dom/workers/XMLHttpRequestUpload.h
js/ipc/ObjectWrapperChild.cpp
js/ipc/ObjectWrapperParent.cpp
js/ipc/ObjectWrapperParent.h
js/src/builtin/MapObject.cpp
js/src/builtin/MapObject.h
js/src/builtin/TestingFunctions.cpp
js/src/ctypes/CTypes.cpp
js/src/ctypes/Library.cpp
js/src/jsapi-tests/testIntern.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsatom.cpp
js/src/jsclass.h
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsexn.cpp
js/src/jsfriendapi.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinfer.h
js/src/jsiter.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jspropertytree.cpp
js/src/jspropertytree.h
js/src/jsproxy.cpp
js/src/jsproxy.h
js/src/jsprvtd.h
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsweakmap.cpp
js/src/jsxml.cpp
js/src/jsxml.h
js/src/perf/jsperf.cpp
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/vm/ArgumentsObject.cpp
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/GlobalObject.cpp
js/src/vm/RegExpStatics.cpp
js/src/vm/String-inl.h
js/src/vm/String.h
js/xpconnect/idl/nsIXPCScriptable.idl
js/xpconnect/idl/nsIXPConnect.idl
js/xpconnect/public/xpc_map_end.h
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCQuickStubs.cpp
js/xpconnect/src/XPCThreadContext.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/XPCWrappedNativeProto.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/dombindings.cpp
js/xpconnect/src/dombindings.h
js/xpconnect/src/xpcprivate.h
--- a/content/xbl/src/nsXBLBinding.cpp
+++ b/content/xbl/src/nsXBLBinding.cpp
@@ -97,17 +97,17 @@
 
 // Helper classes
 
 /***********************************************************************/
 //
 // The JS class for XBLBinding
 //
 static void
-XBLFinalize(JSContext *cx, JSObject *obj)
+XBLFinalize(JSFreeOp *fop, JSObject *obj)
 {
   nsXBLDocumentInfo* docInfo =
     static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
   NS_RELEASE(docInfo);
   
   nsXBLJSClass* c = static_cast<nsXBLJSClass*>(::JS_GetClass(obj));
   c->Drop();
 }
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -167,17 +167,17 @@ nsXBLDocGlobalObject_checkAccess(JSConte
     translated = nsIXPCSecurityManager::ACCESS_GET_PROPERTY;
   }
 
   return nsXBLDocGlobalObject::
     doCheckAccess(cx, obj, id, translated);
 }
 
 static void
-nsXBLDocGlobalObject_finalize(JSContext *cx, JSObject *obj)
+nsXBLDocGlobalObject_finalize(JSFreeOp *fop, JSObject *obj)
 {
   nsISupports *nativeThis = (nsISupports*)JS_GetPrivate(obj);
 
   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeThis));
 
   if (sgo)
     sgo->OnFinalize(obj);
 
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -115,17 +115,17 @@ protected:
 };
 
 nsIPrincipal* nsXULPrototypeDocument::gSystemPrincipal;
 nsXULPDGlobalObject* nsXULPrototypeDocument::gSystemGlobal;
 PRUint32 nsXULPrototypeDocument::gRefCnt;
 
 
 void
-nsXULPDGlobalObject_finalize(JSContext *cx, JSObject *obj)
+nsXULPDGlobalObject_finalize(JSFreeOp *fop, JSObject *obj)
 {
     nsISupports *nativeThis = (nsISupports*)JS_GetPrivate(obj);
 
     nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(nativeThis));
 
     if (sgo) {
         sgo->OnFinalize(obj);
     }
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -4794,17 +4794,17 @@ nsDOMClassInfo::Convert(nsIXPConnectWrap
                         bool *_retval)
 {
   NS_WARNING("nsDOMClassInfo::Convert Don't call me!");
 
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
-nsDOMClassInfo::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+nsDOMClassInfo::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop,
                          JSObject *obj)
 {
   NS_WARNING("nsDOMClassInfo::Finalize Don't call me!");
 
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
@@ -7398,17 +7398,17 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
       *objp = obj;
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+nsWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop,
                      JSObject *obj)
 {
   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
   NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
 
   sgo->OnFinalize(obj);
 
   return NS_OK;
@@ -8935,17 +8935,17 @@ nsHTMLDocumentSH::DocumentAllNewResolve(
 
   return ok;
 }
 
 // Finalize hook used by document related JS objects, but also by
 // sGlobalScopePolluterClass!
 
 void
-nsHTMLDocumentSH::ReleaseDocument(JSContext *cx, JSObject *obj)
+nsHTMLDocumentSH::ReleaseDocument(JSFreeOp *fop, JSObject *obj)
 {
   nsIHTMLDocument *doc = (nsIHTMLDocument *)::JS_GetPrivate(obj);
 
   NS_IF_RELEASE(doc);
 }
 
 JSBool
 nsHTMLDocumentSH::CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp)
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -411,17 +411,17 @@ public:
 #endif
   NS_IMETHOD GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                          JSObject *obj, jsid id, jsval *vp, bool *_retval);
   NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj, bool *_retval);
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, PRUint32 flags,
                         JSObject **objp, bool *_retval);
-  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
+  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop,
                       JSObject *obj);
   NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
                          JSObject * obj, JSObject * *_retval);
 
   static JSBool GlobalScopePolluterNewResolve(JSContext *cx, JSObject *obj,
                                               jsid id, unsigned flags,
                                               JSObject **objp);
   static JSBool GlobalScopePolluterGetProperty(JSContext *cx, JSObject *obj,
@@ -793,17 +793,17 @@ protected:
                                        nsDocument *doc,
                                        nsContentList **nodeList);
 
 public:
   static JSBool DocumentAllGetProperty(JSContext *cx, JSObject *obj, jsid id,
                                        jsval *vp);
   static JSBool DocumentAllNewResolve(JSContext *cx, JSObject *obj, jsid id,
                                       unsigned flags, JSObject **objp);
-  static void ReleaseDocument(JSContext *cx, JSObject *obj);
+  static void ReleaseDocument(JSFreeOp *fop, JSObject *obj);
   static JSBool CallToGetPropMapper(JSContext *cx, unsigned argc, jsval *vp);
   static JSBool DocumentAllHelperGetProperty(JSContext *cx, JSObject *obj,
                                              jsid id, jsval *vp);
   static JSBool DocumentAllHelperNewResolve(JSContext *cx, JSObject *obj,
                                             jsid id, unsigned flags,
                                             JSObject **objp);
   static JSBool DocumentAllTagsNewResolve(JSContext *cx, JSObject *obj,
                                           jsid id, unsigned flags,
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -803,32 +803,32 @@ class nsOuterWindowProxy : public js::Wr
 {
 public:
   nsOuterWindowProxy() : js::Wrapper(0) {}
 
   virtual bool isOuterWindow() {
     return true;
   }
   JSString *obj_toString(JSContext *cx, JSObject *wrapper);
-  void finalize(JSContext *cx, JSObject *proxy);
+  void finalize(JSFreeOp *fop, JSObject *proxy);
 
   static nsOuterWindowProxy singleton;
 };
 
 
 JSString *
 nsOuterWindowProxy::obj_toString(JSContext *cx, JSObject *proxy)
 {
     JS_ASSERT(js::IsProxy(proxy));
 
     return JS_NewStringCopyZ(cx, "[object Window]");
 }
 
 void
-nsOuterWindowProxy::finalize(JSContext *cx, JSObject *proxy)
+nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy)
 {
   nsISupports *global =
     static_cast<nsISupports*>(js::GetProxyExtra(proxy, 0).toPrivate());
   if (global) {
     nsWrapperCache *cache;
     CallQueryInterface(global, &cache);
     cache->ClearWrapper();
   }
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -465,17 +465,17 @@ class CGAddPropertyHook(CGAbstractClassH
   self->SetPreservingWrapper(true);
   return true;"""
 
 class CGClassFinalizeHook(CGAbstractClassHook):
     """
     A hook for finalize, used to release our native object.
     """
     def __init__(self, descriptor):
-        args = [Argument('JSContext*', 'cx'), Argument('JSObject*', 'obj')]
+        args = [Argument('JSFreeOp*', 'fop'), Argument('JSObject*', 'obj')]
         CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME,
                                      'void', args)
 
     def generate_code(self):
         if self.descriptor.customFinalize:
             return """  if (self) {
     self->%s(%s);
   }""" % (self.name, self.args[0].name)
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -161,17 +161,17 @@ NPObjWrapper_newEnumerate(JSContext *cx,
 static JSBool
 NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
                         JSObject **objp);
 
 static JSBool
 NPObjWrapper_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
 
 static void
-NPObjWrapper_Finalize(JSContext *cx, JSObject *obj);
+NPObjWrapper_Finalize(JSFreeOp *fop, JSObject *obj);
 
 static JSBool
 NPObjWrapper_Call(JSContext *cx, unsigned argc, jsval *vp);
 
 static JSBool
 NPObjWrapper_Construct(JSContext *cx, unsigned argc, jsval *vp);
 
 static JSBool
@@ -196,17 +196,17 @@ typedef struct NPObjectMemberPrivate {
     NPIdentifier methodName;
     NPP   npp;
 } NPObjectMemberPrivate;
 
 static JSBool
 NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
 
 static void
-NPObjectMember_Finalize(JSContext *cx, JSObject *obj);
+NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
 
 static JSBool
 NPObjectMember_Call(JSContext *cx, unsigned argc, jsval *vp);
 
 static void
 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
 
 static JSClass sNPObjectMemberClass =
@@ -1707,17 +1707,17 @@ NPObjWrapper_Convert(JSContext *cx, JSOb
                        ? "primitive type"
                        : hint == JSTYPE_NUMBER
                        ? "number"
                        : "string");
   return false;
 }
 
 static void
-NPObjWrapper_Finalize(JSContext *cx, JSObject *obj)
+NPObjWrapper_Finalize(JSFreeOp *fop, JSObject *obj)
 {
   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
   if (npobj) {
     if (sNPObjWrappers.ops) {
       PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_REMOVE);
     }
   }
 
@@ -2209,17 +2209,17 @@ NPObjectMember_Convert(JSContext *cx, JS
     return JS_TRUE;
   default:
     NS_ERROR("illegal operation on JSObject prototype object");
     return JS_FALSE;
   }
 }
 
 static void
-NPObjectMember_Finalize(JSContext *cx, JSObject *obj)
+NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj)
 {
   NPObjectMemberPrivate *memberPrivate;
 
   memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
   if (!memberPrivate)
     return;
 
   PR_Free(memberPrivate);
--- a/dom/workers/DOMBindingBase.cpp
+++ b/dom/workers/DOMBindingBase.cpp
@@ -34,17 +34,17 @@ DOMBindingBase::_Trace(JSTracer* aTrc)
 {
   JSObject* obj = GetJSObject();
   if (obj) {
     JS_CALL_OBJECT_TRACER(aTrc, obj, "cached wrapper");
   }
 }
 
 void
-DOMBindingBase::_Finalize(JSContext* aCx)
+DOMBindingBase::_Finalize(JSFreeOp* aFop)
 {
   ClearWrapper();
   NS_RELEASE_THIS();
 }
 
 JSContext*
 DOMBindingBase::GetJSContextFromContextStack() const
 {
--- a/dom/workers/DOMBindingBase.h
+++ b/dom/workers/DOMBindingBase.h
@@ -35,17 +35,17 @@ class DOMBindingBase : public nsWrapperC
 protected:
   DOMBindingBase(JSContext* aCx);
   virtual ~DOMBindingBase();
 
   virtual void
   _Trace(JSTracer* aTrc);
 
   virtual void
-  _Finalize(JSContext* aCx);
+  _Finalize(JSFreeOp* aFop);
 
   JSContext*
   GetJSContextFromContextStack() const;
 
 public:
   NS_INLINE_DECL_REFCOUNTING(DOMBindingBase)
 
   JSContext*
--- a/dom/workers/EventListenerManager.cpp
+++ b/dom/workers/EventListenerManager.cpp
@@ -79,21 +79,21 @@ struct ListenerData : PRCList
   }
 
     PR_REMOVE_LINK(aListenerData);
     JS_free(aCx, aListenerData);
   }
 };
 
 inline void
-DestroyList(JSContext* aCx, PRCList* aListHead)
+DestroyList(JSFreeOp* aFop, PRCList* aListHead)
 {
   for (PRCList* elem = PR_NEXT_LINK(aListHead); elem != aListHead; ) {
     PRCList* nextElem = PR_NEXT_LINK(elem);
-    JS_free(aCx, elem);
+    JS_freeop(aFop, elem);
     elem = nextElem;
   }
 }
 
 inline ListenerCollection*
 GetCollectionForType(const PRCList* aHead, const jsid& aTypeId)
 {
   for (PRCList* elem = PR_NEXT_LINK(aHead);
@@ -169,27 +169,27 @@ EventListenerManager::TraceInternal(JSTr
       JS_CALL_OBJECT_TRACER(aTrc,
                             static_cast<ListenerData*>(listenerElem)->mListener,
                             "EventListenerManager listener object");
     }
   }
 }
 
 void
-EventListenerManager::FinalizeInternal(JSContext* aCx)
+EventListenerManager::FinalizeInternal(JSFreeOp* aFop)
 {
   MOZ_ASSERT(!PR_CLIST_IS_EMPTY(&mCollectionHead));
 
   for (PRCList* elem = PR_NEXT_LINK(&mCollectionHead);
        elem != &mCollectionHead;
        elem = PR_NEXT_LINK(elem)) {
-    DestroyList(aCx, &static_cast<ListenerCollection*>(elem)->mListenerHead);
+    DestroyList(aFop, &static_cast<ListenerCollection*>(elem)->mListenerHead);
   }
 
-  DestroyList(aCx, &mCollectionHead);
+  DestroyList(aFop, &mCollectionHead);
 
 #ifdef DEBUG
   PR_INIT_CLIST(&mCollectionHead);
 #endif
 }
 
 void
 EventListenerManager::Add(JSContext* aCx, const jsid& aType,
--- a/dom/workers/EventListenerManager.h
+++ b/dom/workers/EventListenerManager.h
@@ -34,20 +34,20 @@ public:
   _Trace(JSTracer* aTrc) const
   {
     if (!PR_CLIST_IS_EMPTY(&mCollectionHead)) {
       TraceInternal(aTrc);
     }
   }
 
   void
-  _Finalize(JSContext* aCx)
+  _Finalize(JSFreeOp* aFop)
   {
     if (!PR_CLIST_IS_EMPTY(&mCollectionHead)) {
-      FinalizeInternal(aCx);
+      FinalizeInternal(aFop);
     }
   }
 
   enum Phase
   {
     All = 0,
     Capturing,
     Onfoo,
@@ -105,17 +105,17 @@ public:
     return HasListeners() && HasListenersForTypeInternal(aCx, aType);
   }
 
 private:
   void
   TraceInternal(JSTracer* aTrc) const;
 
   void
-  FinalizeInternal(JSContext* aCx);
+  FinalizeInternal(JSFreeOp* aFop);
 
   void
   Add(JSContext* aCx, const jsid& aType, JSObject* aListener, Phase aPhase,
       bool aWantsUntrusted, nsresult& aRv);
 
   void
   Remove(JSContext* aCx, const jsid& aType, JSObject* aListener, Phase aPhase,
          bool aClearEmpty);
--- a/dom/workers/EventTarget.cpp
+++ b/dom/workers/EventTarget.cpp
@@ -10,20 +10,20 @@ USING_WORKERS_NAMESPACE
 void
 EventTarget::_Trace(JSTracer* aTrc)
 {
   mListenerManager._Trace(aTrc);
   DOMBindingBase::_Trace(aTrc);
 }
 
 void
-EventTarget::_Finalize(JSContext* aCx)
+EventTarget::_Finalize(JSFreeOp* aFop)
 {
-  mListenerManager._Finalize(aCx);
-  DOMBindingBase::_Finalize(aCx);
+  mListenerManager._Finalize(aFop);
+  DOMBindingBase::_Finalize(aFop);
 }
 
 JSObject*
 EventTarget::GetEventListener(const nsAString& aType, nsresult& aRv) const
 {
   JSContext* cx = GetJSContext();
 
   JSString* type =
@@ -91,9 +91,9 @@ EventTarget::RemoveEventListener(const n
     JS_NewUCStringCopyN(cx, aType.BeginReading(), aType.Length());
   if (!type || !(type = JS_InternJSString(cx, type))) {
     aRv = NS_ERROR_OUT_OF_MEMORY;
     return;
   }
 
   mListenerManager.RemoveEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
                                        aListener, aCapturing);
-}
\ No newline at end of file
+}
--- a/dom/workers/EventTarget.h
+++ b/dom/workers/EventTarget.h
@@ -29,17 +29,17 @@ protected:
   virtual ~EventTarget()
   { }
 
 public:
   virtual void
   _Trace(JSTracer* aTrc) MOZ_OVERRIDE;
 
   virtual void
-  _Finalize(JSContext* aCx) MOZ_OVERRIDE;
+  _Finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 
   void
   AddEventListener(const nsAString& aType, JSObject* aListener,
                    bool aCapture, Nullable<bool> aWantsUntrusted, nsresult& aRv);
 
   void
   RemoveEventListener(const nsAString& aType, JSObject* aListener,
                       bool aCapture, nsresult& aRv);
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -241,17 +241,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     delete GetJSPrivateSafeish<Event>(aObj);
   }
 
   static JSBool
   GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
   {
@@ -524,22 +524,22 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     MessageEvent* priv = GetJSPrivateSafeish<MessageEvent>(aObj);
     if (priv) {
-      JS_free(aCx, priv->mData);
+      JS_freeop(aFop, priv->mData);
 #ifdef DEBUG
       priv->mData = NULL;
 #endif
       delete priv;
     }
   }
 
   static JSBool
@@ -739,17 +739,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     delete GetJSPrivateSafeish<ErrorEvent>(aObj);
   }
 
   static JSBool
   GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
   {
@@ -919,17 +919,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     delete GetJSPrivateSafeish<ProgressEvent>(aObj);
   }
 
   static JSBool
   GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
   {
--- a/dom/workers/Exceptions.cpp
+++ b/dom/workers/Exceptions.cpp
@@ -104,17 +104,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     delete GetJSPrivateSafeish<DOMException>(aObj);
   }
 
   static JSBool
   ToString(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
@@ -311,17 +311,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     delete GetJSPrivateSafeish<FileException>(aObj);
   }
 
   static JSBool
   GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
   {
--- a/dom/workers/File.cpp
+++ b/dom/workers/File.cpp
@@ -136,17 +136,17 @@ private:
       return false;
     }
 
     JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
     return true;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
 
     nsIDOMBlob* blob = GetPrivate(aObj);
     NS_IF_RELEASE(blob);
   }
 
   static JSBool
@@ -325,17 +325,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
 
     nsIDOMFile* file = GetPrivate(aObj);
     NS_IF_RELEASE(file);
   }
 
   static JSBool
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -151,17 +151,17 @@ private:
 
     SetJSPrivateSafeish(obj, fileReader);
 
     JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj));
     return true;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     FileReaderSyncPrivate* fileReader =
       GetJSPrivateSafeish<FileReaderSyncPrivate>(aObj);
     NS_IF_RELEASE(fileReader);
   }
 
   static JSBool
--- a/dom/workers/Location.cpp
+++ b/dom/workers/Location.cpp
@@ -127,17 +127,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     delete static_cast<Location*>(JS_GetPrivate(aObj));
   }
 
   static JSBool
   ToString(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -138,17 +138,17 @@ private:
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     delete static_cast<Navigator*>(JS_GetPrivate(aObj));
   }
 
   static JSBool
   GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
   {
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -241,22 +241,22 @@ private:
 
   static JSBool
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     return ConstructInternal(aCx, aArgc, aVp, false, Class());
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
     WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj, Class());
     if (worker) {
-      worker->_Finalize(aCx);
+      worker->_Finalize(aFop);
     }
   }
 
   static void
   Trace(JSTracer* aTrc, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
     WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj, Class());
@@ -399,22 +399,22 @@ private:
 
   static JSBool
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     return ConstructInternal(aCx, aArgc, aVp, true, Class());
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
     WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj, Class());
     if (worker) {
-      worker->_Finalize(aCx);
+      worker->_Finalize(aFop);
     }
   }
 
   static void
   Trace(JSTracer* aTrc, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
     WorkerPrivate* worker = UnwrapDOMObject<WorkerPrivate>(aObj, Class());
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2038,43 +2038,43 @@ WorkerPrivateParent<Derived>::_Trace(JST
   // This should only happen on the parent thread but we can't assert that
   // because it can also happen on the cycle collector thread when this is a
   // top-level worker.
   EventTarget::_Trace(aTrc);
 }
 
 template <class Derived>
 void
-WorkerPrivateParent<Derived>::_Finalize(JSContext* aCx)
+WorkerPrivateParent<Derived>::_Finalize(JSFreeOp* aFop)
 {
   AssertIsOnParentThread();
 
   MOZ_ASSERT(mJSObject);
   MOZ_ASSERT(!mJSObjectRooted);
 
   // Clear the JS object.
   mJSObject = nsnull;
 
-  if (!TerminatePrivate(aCx, true)) {
+  if (!TerminatePrivate(aFop->context, true)) {
     NS_WARNING("Failed to terminate!");
   }
 
   // Before calling through to the base class we need to grab another reference
   // if we're on the main thread. Otherwise the base class' _Finalize method
   // will call Release, and some of our members cannot be released during
   // finalization. Of course, if those members are already gone then we can skip
   // this mess...
   WorkerPrivateParent<Derived>* extraSelfRef = NULL;
 
   if (!mParent && !mMainThreadObjectsForgotten) {
     AssertIsOnMainThread();
     NS_ADDREF(extraSelfRef = this);
   }
 
-  EventTarget::_Finalize(aCx);
+  EventTarget::_Finalize(aFop);
 
   if (extraSelfRef) {
     nsCOMPtr<nsIRunnable> runnable =
       NS_NewNonOwningRunnableMethod(extraSelfRef,
                                     &WorkerPrivateParent<Derived>::Release);
     if (NS_FAILED(NS_DispatchToCurrentThread(runnable))) {
       NS_WARNING("Failed to proxy release, this will leak!");
     }
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -282,17 +282,17 @@ public:
 
   bool
   Resume(JSContext* aCx);
 
   virtual void
   _Trace(JSTracer* aTrc) MOZ_OVERRIDE;
 
   virtual void
-  _Finalize(JSContext* aCx) MOZ_OVERRIDE;
+  _Finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 
   void
   Finish(JSContext* aCx)
   {
     RootJSObject(aCx, false);
   }
 
   bool
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -154,19 +154,19 @@ protected:
     for (int32 i = 0; i < SLOT_COUNT; i++) {
       JS_CALL_VALUE_TRACER(aTrc, mSlots[i], "WorkerGlobalScope instance slot");
     }
     mWorker->TraceInternal(aTrc);
     EventTarget::_Trace(aTrc);
   }
 
   virtual void
-  _Finalize(JSContext* aCx) MOZ_OVERRIDE
+  _Finalize(JSFreeOp* aFop) MOZ_OVERRIDE
   {
-    EventTarget::_Finalize(aCx);
+    EventTarget::_Finalize(aFop);
   }
 
 private:
   static JSBool
   _GetEventListener(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp)
   {
     JS_ASSERT(JSID_IS_INT(aIdval));
     JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
@@ -821,24 +821,24 @@ private:
       return false;
     }
 
     *aObjp = resolved ? aObj : NULL;
     return true;
   }
 
   static void
-  Finalize(JSContext* aCx, JSObject* aObj)
+  Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
     DedicatedWorkerGlobalScope* scope =
       UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj, Class());
     if (scope) {
       DestroyProtoOrIfaceCache(aObj);
-      scope->_Finalize(aCx);
+      scope->_Finalize(aFop);
     }
   }
 
   static void
   Trace(JSTracer* aTrc, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
     DedicatedWorkerGlobalScope* scope =
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -1465,20 +1465,20 @@ XMLHttpRequest::_Trace(JSTracer* aTrc)
   if (mUpload) {
     JS_CALL_OBJECT_TRACER(aTrc, mUpload->GetJSObject(), "mUpload");
   }
   JS_CALL_VALUE_TRACER(aTrc, mStateData.mResponse, "mResponse");
   XMLHttpRequestEventTarget::_Trace(aTrc);
 }
 
 void
-XMLHttpRequest::_Finalize(JSContext* aCx)
+XMLHttpRequest::_Finalize(JSFreeOp* aFop)
 {
   ReleaseProxy(XHRIsGoingAway);
-  XMLHttpRequestEventTarget::_Finalize(aCx);
+  XMLHttpRequestEventTarget::_Finalize(aFop);
 }
 
 // static
 XMLHttpRequest*
 XMLHttpRequest::_Constructor(JSContext* aCx, JSObject* aGlobal, nsresult& aRv)
 {
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
   MOZ_ASSERT(workerPrivate);
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -63,17 +63,17 @@ protected:
   XMLHttpRequest(JSContext* aCx, WorkerPrivate* aWorkerPrivate);
   virtual ~XMLHttpRequest();
 
 public:
   virtual void
   _Trace(JSTracer* aTrc) MOZ_OVERRIDE;
 
   virtual void
-  _Finalize(JSContext* aCx) MOZ_OVERRIDE;
+  _Finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 
   static XMLHttpRequest*
   _Constructor(JSContext* aCx, JSObject* aGlobal, nsresult& aRv);
 
   void
   Unpin();
 
   bool
--- a/dom/workers/XMLHttpRequestEventTarget.cpp
+++ b/dom/workers/XMLHttpRequestEventTarget.cpp
@@ -9,12 +9,12 @@ USING_WORKERS_NAMESPACE
 
 void
 XMLHttpRequestEventTarget::_Trace(JSTracer* aTrc)
 {
   EventTarget::_Trace(aTrc);
 }
 
 void
-XMLHttpRequestEventTarget::_Finalize(JSContext* aCx)
+XMLHttpRequestEventTarget::_Finalize(JSFreeOp* aFop)
 {
-  EventTarget::_Finalize(aCx);
+  EventTarget::_Finalize(aFop);
 }
--- a/dom/workers/XMLHttpRequestEventTarget.h
+++ b/dom/workers/XMLHttpRequestEventTarget.h
@@ -20,17 +20,17 @@ protected:
   virtual ~XMLHttpRequestEventTarget()
   { }
 
 public:
   virtual void
   _Trace(JSTracer* aTrc) MOZ_OVERRIDE;
 
   virtual void
-  _Finalize(JSContext* aCx) MOZ_OVERRIDE;
+  _Finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 
 #define IMPL_GETTER_AND_SETTER(_type)                                          \
   JSObject*                                                                    \
   GetOn##_type(nsresult& aRv)                                                  \
   {                                                                            \
     return GetEventListener(NS_LITERAL_STRING(#_type), aRv);                   \
   }                                                                            \
                                                                                \
--- a/dom/workers/XMLHttpRequestUpload.cpp
+++ b/dom/workers/XMLHttpRequestUpload.cpp
@@ -24,12 +24,12 @@ XMLHttpRequestUpload::_Trace(JSTracer* a
 {
   if (mXHR) {
     JS_CALL_OBJECT_TRACER(aTrc, mXHR->GetJSObject(), "mXHR");
   }
   XMLHttpRequestEventTarget::_Trace(aTrc);
 }
 
 void
-XMLHttpRequestUpload::_Finalize(JSContext* aCx)
+XMLHttpRequestUpload::_Finalize(JSFreeOp* aFop)
 {
-  XMLHttpRequestEventTarget::_Finalize(aCx);
+  XMLHttpRequestEventTarget::_Finalize(aFop);
 }
--- a/dom/workers/XMLHttpRequestUpload.h
+++ b/dom/workers/XMLHttpRequestUpload.h
@@ -27,14 +27,14 @@ protected:
 public:
   static XMLHttpRequestUpload*
   Create(JSContext* aCx, XMLHttpRequest* aXHR);
 
   virtual void
   _Trace(JSTracer* aTrc) MOZ_OVERRIDE;
 
   virtual void
-  _Finalize(JSContext* aCx) MOZ_OVERRIDE;
+  _Finalize(JSFreeOp* aFop) MOZ_OVERRIDE;
 };
 
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_xmlhttprequestupload_h__
--- a/js/ipc/ObjectWrapperChild.cpp
+++ b/js/ipc/ObjectWrapperChild.cpp
@@ -406,27 +406,33 @@ ObjectWrapperChild::AnswerDelProperty(co
 
     return jsval_to_JSVariant(cx, aco.Ok() ? val : JSVAL_VOID, vp);
 }
 
 static const PRUint32 sNextIdIndexSlot = 0;
 static const PRUint32 sNumNewEnumerateStateSlots = 1;
 
 static void
-CPOW_NewEnumerateState_Finalize(JSContext* cx, JSObject* state)
+CPOW_NewEnumerateState_FreeIds(JSObject* state)
 {
     nsTArray<nsString>* strIds =
         static_cast<nsTArray<nsString>*>(JS_GetPrivate(state));
 
     if (strIds) {
         delete strIds;
         JS_SetPrivate(state, NULL);
     }
 }
 
+static void
+CPOW_NewEnumerateState_Finalize(JSFreeOp* fop, JSObject* state)
+{
+    CPOW_NewEnumerateState_FreeIds(state);
+}
+
 // Similar to IteratorClass in XPCWrapper.cpp
 static const JSClass sCPOW_NewEnumerateState_JSClass = {
     "CPOW NewEnumerate State",
     JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(sNumNewEnumerateStateSlots),
     JS_PropertyStub,  JS_PropertyStub,
     JS_PropertyStub,  JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub,
@@ -526,17 +532,17 @@ ObjectWrapperChild::RecvNewEnumerateDest
     JSObject* state;
 
     JSContext* cx = Manager()->GetContext();
     AutoContextPusher acp(cx);
 
     if (!JSObject_from_JSVariant(cx, in_state, &state))
         return false;
 
-    CPOW_NewEnumerateState_Finalize(cx, state);
+    CPOW_NewEnumerateState_FreeIds(state);
 
     return true;
 }
 
 bool
 ObjectWrapperChild::AnswerNewResolve(const nsString& id, const int& flags,
                                      OperationStatus* status, PObjectWrapperChild** obj2)
 {
--- a/js/ipc/ObjectWrapperParent.cpp
+++ b/js/ipc/ObjectWrapperParent.cpp
@@ -622,17 +622,17 @@ ObjectWrapperParent::CPOW_Convert(JSCont
         return with_error(cx, JS_FALSE, "Unwrapping failed in CPOW_Convert");
 
     *vp = OBJECT_TO_JSVAL(obj);
 
     return JS_TRUE;
 }
 
 /*static*/ void
-ObjectWrapperParent::CPOW_Finalize(JSContext* cx, JSObject* obj)
+ObjectWrapperParent::CPOW_Finalize(js::FreeOp* fop, JSObject* obj)
 {
     CPOW_LOG(("Calling CPOW_Finalize..."));
     
     ObjectWrapperParent* self = Unwrap(obj);
     if (self) {
         self->mObj = NULL;
         unused << ObjectWrapperParent::Send__delete__(self);
     }
--- a/js/ipc/ObjectWrapperParent.h
+++ b/js/ipc/ObjectWrapperParent.h
@@ -114,17 +114,17 @@ private:
     static JSBool
     CPOW_NewResolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
                     JSObject **objp);
 
     static JSBool
     CPOW_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
 
     static void
-    CPOW_Finalize(JSContext* cx, JSObject* obj);
+    CPOW_Finalize(js::FreeOp* fop, JSObject* obj);
 
     static JSBool
     CPOW_Call(JSContext* cx, unsigned argc, jsval* vp);
 
     static JSBool
     CPOW_Construct(JSContext *cx, unsigned argc, jsval *vp);
     
     static JSBool
--- a/js/src/builtin/MapObject.cpp
+++ b/js/src/builtin/MapObject.cpp
@@ -191,21 +191,21 @@ MapObject::mark(JSTracer *trc, JSObject 
             gc::MarkValue(trc, &tmp, "key");
             JS_ASSERT(tmp.get() == key.get());
             gc::MarkValue(trc, &r.front().value, "value");
         }
     }
 }
 
 void
-MapObject::finalize(JSContext *cx, JSObject *obj)
+MapObject::finalize(FreeOp *fop, JSObject *obj)
 {
     MapObject *mapobj = static_cast<MapObject *>(obj);
     if (ValueMap *map = mapobj->getData())
-        cx->delete_(map);
+        fop->delete_(map);
 }
 
 class AddToMap {
   private:
     ValueMap *map;
 
   public:
     AddToMap(ValueMap *map) : map(map) {}
@@ -392,21 +392,21 @@ SetObject::mark(JSTracer *trc, JSObject 
             HeapValue tmp(key);
             gc::MarkValue(trc, &tmp, "key");
             JS_ASSERT(tmp.get() == key.get());
         }
     }
 }
 
 void
-SetObject::finalize(JSContext *cx, JSObject *obj)
+SetObject::finalize(FreeOp *fop, JSObject *obj)
 {
     SetObject *setobj = static_cast<SetObject *>(obj);
     if (ValueSet *set = setobj->getData())
-        cx->delete_(set);
+        fop->delete_(set);
 }
 
 class AddToSet {
   private:
     ValueSet *set;
 
   public:
     AddToSet(ValueSet *set) : set(set) {}
--- a/js/src/builtin/MapObject.h
+++ b/js/src/builtin/MapObject.h
@@ -82,17 +82,17 @@ class MapObject : public JSObject {
   public:
     static JSObject *initClass(JSContext *cx, JSObject *obj);
     static Class class_;
   private:
     typedef ValueMap Data;
     static JSFunctionSpec methods[];
     ValueMap *getData() { return static_cast<ValueMap *>(getPrivate()); }
     static void mark(JSTracer *trc, JSObject *obj);
-    static void finalize(JSContext *cx, JSObject *obj);
+    static void finalize(FreeOp *fop, JSObject *obj);
     static JSBool construct(JSContext *cx, unsigned argc, Value *vp);
     static JSBool size(JSContext *cx, unsigned argc, Value *vp);
     static JSBool get(JSContext *cx, unsigned argc, Value *vp);
     static JSBool has(JSContext *cx, unsigned argc, Value *vp);
     static JSBool set(JSContext *cx, unsigned argc, Value *vp);
     static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
 };
 
@@ -100,17 +100,17 @@ class SetObject : public JSObject {
   public:
     static JSObject *initClass(JSContext *cx, JSObject *obj);
     static Class class_;
   private:
     typedef ValueSet Data;
     static JSFunctionSpec methods[];
     ValueSet *getData() { return static_cast<ValueSet *>(getPrivate()); }
     static void mark(JSTracer *trc, JSObject *obj);
-    static void finalize(JSContext *cx, JSObject *obj);
+    static void finalize(FreeOp *fop, JSObject *obj);
     static JSBool construct(JSContext *cx, unsigned argc, Value *vp);
     static JSBool size(JSContext *cx, unsigned argc, Value *vp);
     static JSBool has(JSContext *cx, unsigned argc, Value *vp);
     static JSBool add(JSContext *cx, unsigned argc, Value *vp);
     static JSBool delete_(JSContext *cx, unsigned argc, Value *vp);
 };
 
 } /* namespace js */
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -385,17 +385,17 @@ CountHeap(JSContext *cx, unsigned argc, 
     }
 
     return JS_NewNumberValue(cx, (double) counter, vp);
 }
 
 static unsigned finalizeCount = 0;
 
 static void
-finalize_counter_finalize(JSContext *cx, JSObject *obj)
+finalize_counter_finalize(JSFreeOp *fop, JSObject *obj)
 {
     JS_ATOMIC_INCREMENT(&finalizeCount);
 }
 
 static JSClass FinalizeCounterClass = {
     "FinalizeCounter", JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,       /* addProperty */
     JS_PropertyStub,       /* delProperty */
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -73,18 +73,18 @@ namespace ctypes {
 
 static JSBool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp);
 
 namespace CType {
   static JSBool ConstructData(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ConstructBasic(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp);
 
   static void Trace(JSTracer* trc, JSObject* obj);
-  static void Finalize(JSContext* cx, JSObject* obj);
-  static void FinalizeProtoClass(JSContext* cx, JSObject* obj);
+  static void Finalize(JSFreeOp *fop, JSObject* obj);
+  static void FinalizeProtoClass(JSFreeOp *fop, JSObject* obj);
 
   static JSBool PrototypeGetter(JSContext* cx, JSObject* obj, jsid idval,
     jsval* vp);
   static JSBool NameGetter(JSContext* cx, JSObject* obj, jsid idval,
     jsval* vp);
   static JSBool SizeGetter(JSContext* cx, JSObject* obj, jsid idval,
     jsval* vp);
   static JSBool PtrGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
@@ -163,25 +163,25 @@ namespace FunctionType {
     jsval* vp);
   static JSBool ABIGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp);
   static JSBool IsVariadicGetter(JSContext* cx, JSObject* obj, jsid idval,
     jsval* vp);
 }
 
 namespace CClosure {
   static void Trace(JSTracer* trc, JSObject* obj);
-  static void Finalize(JSContext* cx, JSObject* obj);
+  static void Finalize(JSFreeOp *fop, JSObject* obj);
 
   // libffi callback
   static void ClosureStub(ffi_cif* cif, void* result, void** args,
     void* userData);
 }
 
 namespace CData {
-  static void Finalize(JSContext* cx, JSObject* obj);
+  static void Finalize(JSFreeOp *fop, JSObject* obj);
 
   static JSBool ValueGetter(JSContext* cx, JSObject* obj, jsid idval,
                             jsval* vp);
   static JSBool ValueSetter(JSContext* cx, JSObject* obj, jsid idval,
                             JSBool strict, jsval* vp);
   static JSBool Address(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ReadString(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
@@ -203,17 +203,17 @@ namespace Int64Base {
   uint64_t GetInt(JSObject* obj);
 
   JSBool ToString(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp,
     bool isUnsigned);
 
   JSBool ToSource(JSContext* cx, JSObject* obj, unsigned argc, jsval* vp,
     bool isUnsigned);
 
-  static void Finalize(JSContext* cx, JSObject* obj);
+  static void Finalize(JSFreeOp *fop, JSObject* obj);
 }
 
 namespace Int64 {
   static JSBool Construct(JSContext* cx, unsigned argc, jsval* vp);
 
   static JSBool ToString(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool ToSource(JSContext* cx, unsigned argc, jsval* vp);
 
@@ -2753,62 +2753,62 @@ CType::DefineBuiltin(JSContext* cx,
   if (!JS_DefineProperty(cx, parent, propName, OBJECT_TO_JSVAL(typeObj),
          NULL, NULL, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return NULL;
 
   return typeObj;
 }
 
 void
-CType::Finalize(JSContext* cx, JSObject* obj)
+CType::Finalize(JSFreeOp *fop, JSObject* obj)
 {
   // Make sure our TypeCode slot is legit. If it's not, bail.
   jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE);
   if (JSVAL_IS_VOID(slot))
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(JSVAL_TO_INT(slot))) {
   case TYPE_function: {
     // Free the FunctionInfo.
     slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
     if (!JSVAL_IS_VOID(slot))
-      cx->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
+      FreeOp::get(fop)->delete_(static_cast<FunctionInfo*>(JSVAL_TO_PRIVATE(slot)));
     break;
   }
 
   case TYPE_struct: {
     // Free the FieldInfoHash table.
     slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
     if (!JSVAL_IS_VOID(slot)) {
       void* info = JSVAL_TO_PRIVATE(slot);
-      cx->delete_(static_cast<FieldInfoHash*>(info));
+      FreeOp::get(fop)->delete_(static_cast<FieldInfoHash*>(info));
     }
   }
 
     // Fall through.
   case TYPE_array: {
     // Free the ffi_type info.
     slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
     if (!JSVAL_IS_VOID(slot)) {
       ffi_type* ffiType = static_cast<ffi_type*>(JSVAL_TO_PRIVATE(slot));
-      cx->array_delete(ffiType->elements);
-      cx->delete_(ffiType);
+      FreeOp::get(fop)->array_delete(ffiType->elements);
+      FreeOp::get(fop)->delete_(ffiType);
     }
 
     break;
   }
   default:
     // Nothing to do here.
     break;
   }
 }
 
 void
-CType::FinalizeProtoClass(JSContext* cx, JSObject* obj)
+CType::FinalizeProtoClass(JSFreeOp *fop, JSObject* obj)
 {
   // Finalize the CTypeProto class. The only important bit here is our
   // SLOT_CLOSURECX -- it contains the JSContext that was (lazily) instantiated
   // for use with FunctionType closures. And if we're here, in this finalizer,
   // we're guaranteed to not need it anymore. Note that this slot will only
   // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype.
   jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSURECX);
   if (JSVAL_IS_VOID(slot))
@@ -5489,25 +5489,25 @@ CClosure::Trace(JSTracer* trc, JSObject*
   // 'closureObj', since that's us.)
   JS_CALL_OBJECT_TRACER(trc, cinfo->typeObj, "typeObj");
   JS_CALL_OBJECT_TRACER(trc, cinfo->jsfnObj, "jsfnObj");
   if (cinfo->thisObj)
     JS_CALL_OBJECT_TRACER(trc, cinfo->thisObj, "thisObj");
 }
 
 void
-CClosure::Finalize(JSContext* cx, JSObject* obj)
+CClosure::Finalize(JSFreeOp *fop, JSObject* obj)
 {
   // Make sure our ClosureInfo slot is legit. If it's not, bail.
   jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
   if (JSVAL_IS_VOID(slot))
     return;
 
   ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
-  cx->delete_(cinfo);
+  FreeOp::get(fop)->delete_(cinfo);
 }
 
 void
 CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
 {
   JS_ASSERT(cif);
   JS_ASSERT(result);
   JS_ASSERT(args);
@@ -5735,33 +5735,33 @@ CData::Create(JSContext* cx,
 
   *buffer = data;
   JS_SetReservedSlot(dataObj, SLOT_DATA, PRIVATE_TO_JSVAL(buffer));
 
   return dataObj;
 }
 
 void
-CData::Finalize(JSContext* cx, JSObject* obj)
+CData::Finalize(JSFreeOp *fop, JSObject* obj)
 {
   // Delete our buffer, and the data it contains if we own it.
   jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS);
   if (JSVAL_IS_VOID(slot))
     return;
 
   JSBool owns = JSVAL_TO_BOOLEAN(slot);
 
   slot = JS_GetReservedSlot(obj, SLOT_DATA);
   if (JSVAL_IS_VOID(slot))
     return;
   char** buffer = static_cast<char**>(JSVAL_TO_PRIVATE(slot));
 
   if (owns)
-    cx->array_delete(*buffer);
-  cx->delete_(buffer);
+    FreeOp::get(fop)->array_delete(*buffer);
+  FreeOp::get(fop)->delete_(buffer);
 }
 
 JSObject*
 CData::GetCType(JSObject* dataObj)
 {
   JS_ASSERT(CData::IsCData(dataObj));
 
   jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE);
@@ -6119,23 +6119,23 @@ Int64Base::Construct(JSContext* cx,
 
   if (!JS_FreezeObject(cx, result))
     return NULL;
 
   return result;
 }
 
 void
-Int64Base::Finalize(JSContext* cx, JSObject* obj)
+Int64Base::Finalize(JSFreeOp *fop, JSObject* obj)
 {
   jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
   if (JSVAL_IS_VOID(slot))
     return;
 
-  cx->delete_(static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot)));
+  FreeOp::get(fop)->delete_(static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot)));
 }
 
 uint64_t
 Int64Base::GetInt(JSObject* obj) {
   JS_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
 
   jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
   return *static_cast<uint64_t*>(JSVAL_TO_PRIVATE(slot));
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -48,17 +48,17 @@ namespace js {
 namespace ctypes {
 
 /*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
 
 namespace Library
 {
-  static void Finalize(JSContext* cx, JSObject* obj);
+  static void Finalize(JSFreeOp *fop, JSObject* obj);
 
   static JSBool Close(JSContext* cx, unsigned argc, jsval* vp);
   static JSBool Declare(JSContext* cx, unsigned argc, jsval* vp);
 }
 
 /*******************************************************************************
 ** JSObject implementation
 *******************************************************************************/
@@ -200,25 +200,30 @@ PRLibrary*
 Library::GetLibrary(JSObject* obj)
 {
   JS_ASSERT(IsLibrary(obj));
 
   jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
   return static_cast<PRLibrary*>(JSVAL_TO_PRIVATE(slot));
 }
 
-void
-Library::Finalize(JSContext* cx, JSObject* obj)
+static void
+UnloadLibrary(JSObject* obj)
 {
-  // unload the library
-  PRLibrary* library = GetLibrary(obj);
+  PRLibrary* library = Library::GetLibrary(obj);
   if (library)
     PR_UnloadLibrary(library);
 }
 
+void
+Library::Finalize(JSFreeOp *fop, JSObject* obj)
+{
+  UnloadLibrary(obj);
+}
+
 JSBool
 Library::Open(JSContext* cx, unsigned argc, jsval *vp)
 {
   JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
   if (!ctypesObj || !IsCTypesGlobal(ctypesObj)) {
     JS_ReportError(cx, "not a ctypes object");
     return JS_FALSE;
   }
@@ -246,17 +251,17 @@ Library::Close(JSContext* cx, unsigned a
   }
 
   if (argc != 0) {
     JS_ReportError(cx, "close doesn't take any arguments");
     return JS_FALSE;
   }
 
   // delete our internal objects
-  Finalize(cx, obj);
+  UnloadLibrary(obj);
   JS_SetReservedSlot(obj, SLOT_LIBRARY, PRIVATE_TO_JSVAL(NULL));
 
   JS_SET_RVAL(cx, vp, JSVAL_VOID);
   return JS_TRUE;
 }
 
 JSBool
 Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
--- a/js/src/jsapi-tests/testIntern.cpp
+++ b/js/src/jsapi-tests/testIntern.cpp
@@ -19,17 +19,17 @@ END_TEST(testAtomizedIsNotInterned)
 
 struct StringWrapper
 {
     JSString *str;
     bool     strOk;
 } sw;
 
 void
-FinalizeCallback(JSContext *cx, JSFinalizeStatus status)
+FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status)
 {
     if (status == JSFINALIZE_START)
         sw.strOk = !JS_IsAboutToBeFinalized(sw.str);
 }
 
 BEGIN_TEST(testInternAcrossGC)
 {
     sw.str = JS_InternString(cx, "wrapped chars that another test shouldn't be using");
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2225,16 +2225,22 @@ JS_realloc(JSContext *cx, void *p, size_
 
 JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p)
 {
     return cx->free_(p);
 }
 
 JS_PUBLIC_API(void)
+JS_freeop(JSFreeOp *fop, void *p)
+{
+    return FreeOp::get(fop)->free_(p);
+}
+
+JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes)
 {
     return cx->runtime->updateMallocCounter(cx, nbytes);
 }
 
 JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s)
 {
@@ -4185,17 +4191,17 @@ JS_DeleteProperty(JSContext *cx, JSObjec
 
 JS_PUBLIC_API(void)
 JS_ClearScope(JSContext *cx, JSObject *obj)
 {
     AssertNoGC(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
-    JSFinalizeOp clearOp = obj->getOps()->clear;
+    ClearOp clearOp = obj->getOps()->clear;
     if (clearOp)
         clearOp(cx, obj);
 
     if (obj->isNative())
         js_ClearNative(cx, obj);
 
     /* Clear cached class objects on the global object. */
     if (obj->isGlobal())
@@ -4225,26 +4231,26 @@ JS_Enumerate(JSContext *cx, JSObject *ob
  *     prop_iterator_class somehow...
  * + preserve the obj->enumerate API while optimizing the native object case
  * + native case here uses a Shape *, but that iterates in reverse!
  * + so we make non-native match, by reverse-iterating after JS_Enumerating
  */
 const uint32_t JSSLOT_ITER_INDEX = 0;
 
 static void
-prop_iter_finalize(JSContext *cx, JSObject *obj)
+prop_iter_finalize(FreeOp *fop, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
         return;
 
     if (obj->getSlot(JSSLOT_ITER_INDEX).toInt32() >= 0) {
         /* Non-native case: destroy the ida enumerated when obj was created. */
         JSIdArray *ida = (JSIdArray *) pdata;
-        JS_DestroyIdArray(cx, ida);
+        JS_DestroyIdArray(fop->context, ida);
     }
 }
 
 static void
 prop_iter_trace(JSTracer *trc, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     if (!pdata)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1339,23 +1339,44 @@ typedef JSBool
 (* JSConvertOp)(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
 
 /*
  * Delegate typeof to an object so it can cloak a primitive or another object.
  */
 typedef JSType
 (* JSTypeOfOp)(JSContext *cx, JSObject *obj);
 
+typedef struct JSFreeOp JSFreeOp;
+
+struct JSFreeOp {
+    JSContext   *context;
+#ifndef __cplusplus
+    JSRuntime   *runtime;
+#else
+  private:
+    JSRuntime   *runtime_;
+
+  protected:
+    JSFreeOp(JSRuntime *rt, JSContext *cx)
+      : context(cx), runtime_(rt) { }
+
+  public:
+    JSRuntime *runtime() const {
+        return runtime_;
+    }
+#endif
+};
+
 /*
  * Finalize obj, which the garbage collector has determined to be unreachable
  * from other live objects or from GC roots.  Obviously, finalizers must never
  * store a reference to obj.
  */
 typedef void
-(* JSFinalizeOp)(JSContext *cx, JSObject *obj);
+(* JSFinalizeOp)(JSFreeOp *fop, JSObject *obj);
 
 /*
  * Finalizes external strings created by JS_NewExternalString.
  */
 typedef struct JSStringFinalizer JSStringFinalizer;
 
 struct JSStringFinalizer {
     void (*finalize)(const JSStringFinalizer *fin, jschar *chars);
@@ -1450,17 +1471,17 @@ typedef void
 (* JSGCCallback)(JSRuntime *rt, JSGCStatus status);
 
 typedef enum JSFinalizeStatus {
     JSFINALIZE_START,
     JSFINALIZE_END
 } JSFinalizeStatus;
 
 typedef void
-(* JSFinalizeCallback)(JSContext *cx, JSFinalizeStatus status);
+(* JSFinalizeCallback)(JSFreeOp *fop, JSFinalizeStatus status);
 
 /*
  * Generic trace operation that calls JS_CallTracer on each traceable thing
  * stored in data.
  */
 typedef void
 (* JSTraceDataOp)(JSTracer *trc, void *data);
 
@@ -2949,19 +2970,30 @@ extern JS_PUBLIC_API(void)
 JS_FreeInCompartment(JSCompartment *comp, size_t nbytes);
 
 extern JS_PUBLIC_API(void *)
 JS_malloc(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(void *)
 JS_realloc(JSContext *cx, void *p, size_t nbytes);
 
+/*
+ * A wrapper for js_free(p) that may delay js_free(p) invocation as a
+ * performance optimization.
+ */
 extern JS_PUBLIC_API(void)
 JS_free(JSContext *cx, void *p);
 
+/*
+ * A wrapper for js_free(p) that may delay js_free(p) invocation as a
+ * performance optimization as specified by the given JSFreeOp instance.
+ */
+extern JS_PUBLIC_API(void)
+JS_freeop(JSFreeOp *fop, void *p);
+
 extern JS_PUBLIC_API(void)
 JS_updateMallocCounter(JSContext *cx, size_t nbytes);
 
 extern JS_PUBLIC_API(char *)
 JS_strdup(JSContext *cx, const char *s);
 
 extern JS_PUBLIC_API(JSBool)
 JS_NewNumberValue(JSContext *cx, double d, jsval *rval);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -213,18 +213,19 @@ js_FinishAtomState(JSRuntime *rt)
     if (!state->atoms.initialized()) {
         /*
          * We are called with uninitialized state when JS_NewRuntime fails and
          * calls JS_DestroyRuntime on a partially initialized runtime.
          */
         return;
     }
 
+    FreeOp fop(rt, false, false, NULL);
     for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
-        r.front().asPtr()->finalize(rt);
+        r.front().asPtr()->finalize(&fop);
 }
 
 bool
 js_InitCommonAtoms(JSContext *cx)
 {
     JSAtomState *state = &cx->runtime->atomState;
     JSAtom **atoms = state->commonAtomsStart();
     for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) {
--- a/js/src/jsclass.h
+++ b/js/src/jsclass.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=4 sw=4 et tw=79 ft=cpp:
  *
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
@@ -243,31 +243,33 @@ typedef JSType
  * caller will throw an appropriate error.
  */
 typedef JSBool
 (* FixOp)(JSContext *cx, JSObject *obj, bool *fixed, AutoIdVector *props);
 
 typedef JSObject *
 (* ObjectOp)(JSContext *cx, JSObject *obj);
 typedef void
-(* FinalizeOp)(JSContext *cx, JSObject *obj);
+(* FinalizeOp)(FreeOp *fop, JSObject *obj);
+typedef void
+(* ClearOp)(JSContext *cx, JSObject *obj);
 
 #define JS_CLASS_MEMBERS                                                      \
     const char          *name;                                                \
     uint32_t            flags;                                                \
                                                                               \
     /* Mandatory non-null function pointer members. */                        \
     JSPropertyOp        addProperty;                                          \
     JSPropertyOp        delProperty;                                          \
     JSPropertyOp        getProperty;                                          \
     JSStrictPropertyOp  setProperty;                                          \
     JSEnumerateOp       enumerate;                                            \
     JSResolveOp         resolve;                                              \
     JSConvertOp         convert;                                              \
-    JSFinalizeOp        finalize;                                             \
+    FinalizeOp          finalize;                                             \
                                                                               \
     /* Optionally non-null members start here. */                             \
     JSCheckAccessOp     checkAccess;                                          \
     JSNative            call;                                                 \
     JSNative            construct;                                            \
     JSHasInstanceOp     hasInstance;                                          \
     JSTraceOp           trace
 
@@ -327,17 +329,17 @@ struct ObjectOps
     DeletePropertyOp    deleteProperty;
     DeleteElementOp     deleteElement;
     DeleteSpecialOp     deleteSpecial;
 
     JSNewEnumerateOp    enumerate;
     TypeOfOp            typeOf;
     FixOp               fix;
     ObjectOp            thisObject;
-    FinalizeOp          clear;
+    ClearOp             clear;
 };
 
 #define JS_NULL_OBJECT_OPS                                                    \
     {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,   \
      NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,        \
      NULL,NULL,NULL,NULL,NULL,NULL}
 
 struct Class
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -739,17 +739,17 @@ OptionsSameVersionFlags(uint32_t self, u
  * example of the latter is enabling the top-level-anonymous-function-is-error
  * option: subsequent evals of the same, previously-valid script text may have
  * become invalid.
  */
 namespace VersionFlags {
 static const unsigned MASK         = 0x0FFF; /* see JSVersion in jspubtd.h */
 static const unsigned HAS_XML      = 0x1000; /* flag induced by XML option */
 static const unsigned FULL_MASK    = 0x3FFF;
-}
+} /* namespace VersionFlags */
 
 static inline JSVersion
 VersionNumber(JSVersion version)
 {
     return JSVersion(uint32_t(version) & VersionFlags::MASK);
 }
 
 static inline bool
@@ -802,16 +802,63 @@ VersionIsKnown(JSVersion version)
 {
     return VersionNumber(version) != JSVERSION_UNKNOWN;
 }
 
 typedef HashSet<JSObject *,
                 DefaultHasher<JSObject *>,
                 SystemAllocPolicy> BusyArraysSet;
 
+class FreeOp : public JSFreeOp {
+    bool        shouldFreeLater_;
+    bool        onBackgroundThread_;
+  public:
+
+    static FreeOp *get(JSFreeOp *fop) {
+        return static_cast<FreeOp *>(fop);
+    }
+
+    FreeOp(JSRuntime *rt, bool shouldFreeLater, bool onBackgroundThread, JSContext *cx)
+      : JSFreeOp(rt, cx),
+        shouldFreeLater_(shouldFreeLater),
+        onBackgroundThread_(onBackgroundThread)
+    {
+    }
+
+    bool shouldFreeLater() const {
+        return shouldFreeLater_;
+    }
+
+    bool onBackgroundThread() const {
+        return onBackgroundThread_;
+    }
+
+    void free_(void* p) {
+#ifdef JS_THREADSAFE
+        if (shouldFreeLater()) {
+            runtime()->gcHelperThread.freeLater(p);
+            return;
+        }
+#endif
+        runtime()->free_(p);
+    }
+
+    JS_DECLARE_DELETE_METHODS(free_, inline)
+
+    static void staticAsserts() {
+        /*
+         * Check that JSFreeOp is the first base class for FreeOp and we can
+         * reinterpret a pointer to JSFreeOp as a pointer to FreeOp without
+         * any offset adjustments. JSClass::freeOp <-> Class::freeOp depends
+         * on this.
+         */
+        JS_STATIC_ASSERT(offsetof(FreeOp, shouldFreeLater_) == sizeof(JSFreeOp));
+    }
+};
+
 } /* namespace js */
 
 struct JSContext : js::ContextFriendFields
 {
     explicit JSContext(JSRuntime *rt);
     JSContext *thisDuringConstruction() { return this; }
     ~JSContext();
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -485,48 +485,48 @@ JSCompartment::discardJitCode(JSContext 
          * are setting array holes or accessing getter properties.
          */
         script->resetUseCount();
     }
 #endif
 }
 
 void
-JSCompartment::sweep(JSContext *cx, bool releaseTypes)
+JSCompartment::sweep(FreeOp *fop, bool releaseTypes)
 {
     /* Remove dead wrappers from the table. */
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key) &&
                      !IsAboutToBeFinalized(e.front().value),
                      e.front().key.isString());
         if (IsAboutToBeFinalized(e.front().key) ||
             IsAboutToBeFinalized(e.front().value)) {
             e.removeFront();
         }
     }
 
     /* Remove dead references held weakly by the compartment. */
 
     regExps.sweep(rt);
 
-    sweepBaseShapeTable(cx);
-    sweepInitialShapeTable(cx);
-    sweepNewTypeObjectTable(cx, newTypeObjects);
-    sweepNewTypeObjectTable(cx, lazyTypeObjects);
+    sweepBaseShapeTable();
+    sweepInitialShapeTable();
+    sweepNewTypeObjectTable(newTypeObjects);
+    sweepNewTypeObjectTable(lazyTypeObjects);
 
     if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject))
         emptyTypeObject = NULL;
 
     newObjectCache.reset();
 
-    sweepBreakpoints(cx);
+    sweepBreakpoints(fop);
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
-        discardJitCode(cx);
+        discardJitCode(fop->context);
     }
 
     if (!activeAnalysis) {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
 
         /*
          * Clear the analysis pool, but don't release its data yet. While
          * sweeping types any live data will be allocated into the pool.
@@ -547,30 +547,30 @@ JSCompartment::sweep(JSContext *cx, bool
          * enabled in the compartment.
          */
         if (types.inferenceEnabled) {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
 
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->types) {
-                    types::TypeScript::Sweep(cx, script);
+                    types::TypeScript::Sweep(fop->context, script);
 
                     if (releaseTypes) {
                         script->types->destroy();
                         script->types = NULL;
                         script->typesPurged = true;
                     }
                 }
             }
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
-            types.sweep(cx);
+            types.sweep(fop->context);
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_CLEAR_SCRIPT_ANALYSIS);
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 script->clearAnalysis();
             }
@@ -761,19 +761,19 @@ JSCompartment::clearTraps(JSContext *cx)
     for (gc::CellIter i(this, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (script->hasAnyBreakpointsOrStepMode())
             script->clearTraps(cx);
     }
 }
 
 void
-JSCompartment::sweepBreakpoints(JSContext *cx)
+JSCompartment::sweepBreakpoints(FreeOp *fop)
 {
-    if (JS_CLIST_IS_EMPTY(&cx->runtime->debuggerList))
+    if (JS_CLIST_IS_EMPTY(&rt->debuggerList))
         return;
 
     for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
         JSScript *script = i.get<JSScript>();
         if (!script->hasAnyBreakpointsOrStepMode())
             continue;
         bool scriptGone = IsAboutToBeFinalized(script);
         for (unsigned i = 0; i < script->length; i++) {
@@ -781,17 +781,17 @@ JSCompartment::sweepBreakpoints(JSContex
             if (!site)
                 continue;
             // nextbp is necessary here to avoid possibly reading *bp after
             // destroying it.
             Breakpoint *nextbp;
             for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
                 nextbp = bp->nextInSite();
                 if (scriptGone || IsAboutToBeFinalized(bp->debugger->toJSObject()))
-                    bp->destroy(cx);
+                    bp->destroy(fop->context);
             }
         }
     }
 }
 
 size_t
 JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf)
 {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -309,26 +309,26 @@ struct JSCompartment
 
     /*
      * Shared scope property tree, and arena-pool for allocating its nodes.
      */
     js::PropertyTree             propertyTree;
 
     /* Set of all unowned base shapes in the compartment. */
     js::BaseShapeSet             baseShapes;
-    void sweepBaseShapeTable(JSContext *cx);
+    void sweepBaseShapeTable();
 
     /* Set of initial shapes in the compartment. */
     js::InitialShapeSet          initialShapes;
-    void sweepInitialShapeTable(JSContext *cx);
+    void sweepInitialShapeTable();
 
     /* Set of default 'new' or lazy types in the compartment. */
     js::types::TypeObjectSet     newTypeObjects;
     js::types::TypeObjectSet     lazyTypeObjects;
-    void sweepNewTypeObjectTable(JSContext *cx, js::types::TypeObjectSet &table);
+    void sweepNewTypeObjectTable(js::types::TypeObjectSet &table);
 
     js::types::TypeObject        *emptyTypeObject;
 
     /* Get the default 'new' type for objects with a NULL prototype. */
     inline js::types::TypeObject *getEmptyType(JSContext *cx);
 
     js::types::TypeObject *getLazyType(JSContext *cx, JSObject *proto);
 
@@ -379,17 +379,17 @@ struct JSCompartment
     bool wrapId(JSContext *cx, jsid *idp);
     bool wrap(JSContext *cx, js::PropertyOp *op);
     bool wrap(JSContext *cx, js::StrictPropertyOp *op);
     bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
     bool wrap(JSContext *cx, js::AutoIdVector &props);
 
     void markTypes(JSTracer *trc);
     void discardJitCode(JSContext *cx);
-    void sweep(JSContext *cx, bool releaseTypes);
+    void sweep(js::FreeOp *fop, bool releaseTypes);
     void purge();
 
     void setGCLastBytes(size_t lastBytes, size_t lastMallocBytes, js::JSGCInvocationKind gckind);
     void reduceGCTriggerBytes(size_t amount);
 
     void resetGCMallocBytes();
     void setGCMaxMallocBytes(size_t value);
     void updateMallocCounter(size_t nbytes) {
@@ -454,17 +454,17 @@ struct JSCompartment
     void removeDebuggee(JSContext *cx, js::GlobalObject *global,
                         js::GlobalObjectSet::Enum *debuggeesEnum = NULL);
     bool setDebugModeFromC(JSContext *cx, bool b);
 
     void clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler);
     void clearTraps(JSContext *cx);
 
   private:
-    void sweepBreakpoints(JSContext *cx);
+    void sweepBreakpoints(js::FreeOp *fop);
 
   public:
     js::WatchpointMap *watchpointMap;
 };
 
 #define JS_PROPERTY_TREE(cx)    ((cx)->compartment->propertyTree)
 
 namespace js {
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -81,17 +81,17 @@ using namespace js::types;
 /* Forward declarations for ErrorClass's initializer. */
 static JSBool
 Exception(JSContext *cx, unsigned argc, Value *vp);
 
 static void
 exn_trace(JSTracer *trc, JSObject *obj);
 
 static void
-exn_finalize(JSContext *cx, JSObject *obj);
+exn_finalize(FreeOp *fop, JSObject *obj);
 
 static JSBool
 exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
             JSObject **objp);
 
 Class js::ErrorClass = {
     js_Error_str,
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE |
@@ -483,26 +483,26 @@ SetExnPrivate(JSContext *cx, JSObject *e
     if (JSErrorReport *report = priv->errorReport) {
         if (JSPrincipals *prin = report->originPrincipals)
             JS_HoldPrincipals(prin);
     }
     exnObject->setPrivate(priv);
 }
 
 static void
-exn_finalize(JSContext *cx, JSObject *obj)
+exn_finalize(FreeOp *fop, JSObject *obj)
 {
     if (JSExnPrivate *priv = GetExnPrivate(obj)) {
         if (JSErrorReport *report = priv->errorReport) {
             /* HOLD called by SetExnPrivate. */
             if (JSPrincipals *prin = report->originPrincipals)
-                JS_DropPrincipals(cx->runtime, prin);
-            cx->free_(report);
+                JS_DropPrincipals(fop->runtime(), prin);
+            fop->free_(report);
         }
-        cx->free_(priv);
+        fop->free_(priv);
     }
 }
 
 static JSBool
 exn_resolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
             JSObject **objp)
 {
     JSExnPrivate *priv;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -302,17 +302,16 @@ struct WeakMapTracer {
 };
 
 extern JS_FRIEND_API(void)
 TraceWeakMaps(WeakMapTracer *trc);
 
 extern JS_FRIEND_API(bool)
 GCThingIsMarkedGray(void *thing);
 
-
 /*
  * Shadow declarations of JS internal structures, for access by inline access
  * functions below. Do not use these structures in any other way. When adding
  * new fields for access by inline methods, make sure to add static asserts to
  * the original header file to ensure that offsets are consistent.
  */
 namespace shadow {
 
@@ -758,17 +757,16 @@ class ObjectPtr
     /* Always call finalize before the destructor. */
     ~ObjectPtr() { JS_ASSERT(!value); }
 
     void finalize(JSRuntime *rt) {
         if (IsIncrementalBarrierNeeded(rt))
             IncrementalReferenceBarrier(value);
         value = NULL;
     }
-    void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); }
 
     void init(JSObject *obj) { value = obj; }
 
     JSObject *get() const { return value; }
 
     void writeBarrierPre(JSRuntime *rt) {
         IncrementalReferenceBarrier(value);
     }
@@ -782,16 +780,27 @@ class ObjectPtr
     JSObject &operator*() const { return *value; }
     JSObject *operator->() const { return value; }
     operator JSObject *() const { return value; }
 };
 
 extern JS_FRIEND_API(JSObject *)
 GetTestingFunctions(JSContext *cx);
 
+/*
+ * Helper to convert FreeOp to JSFreeOp when the definition of FreeOp is not
+ * available and the compiler does not know that FreeOp inherits from
+ * JSFreeOp.
+ */
+inline JSFreeOp *
+CastToJSFreeOp(FreeOp *fop)
+{
+    return reinterpret_cast<JSFreeOp *>(fop);
+}
+
 } /* namespace js */
 
 #endif
 
 /* Implemented in jsdate.cpp. */
 
 /*
  * Detect whether the internal date value is NaN.  (Because failure is
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -269,17 +269,17 @@ Arena::staticAsserts()
 {
     JS_STATIC_ASSERT(sizeof(Arena) == ArenaSize);
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(ThingSizes) == FINALIZE_LIMIT);
     JS_STATIC_ASSERT(JS_ARRAY_LENGTH(FirstThingOffsets) == FINALIZE_LIMIT);
 }
 
 template<typename T>
 inline bool
-Arena::finalize(JSContext *cx, AllocKind thingKind, size_t thingSize, bool background)
+Arena::finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize)
 {
     /* Enforce requirements on size of T. */
     JS_ASSERT(thingSize % Cell::CellSize == 0);
     JS_ASSERT(thingSize <= 255);
 
     JS_ASSERT(aheader.allocated());
     JS_ASSERT(thingKind == aheader.getAllocKind());
     JS_ASSERT(thingSize == aheader.getThingSize());
@@ -320,17 +320,17 @@ Arena::finalize(JSContext *cx, AllocKind
                     newListTail->first = newFreeSpanStart;
                     newListTail->last = thing - thingSize;
                     newListTail = newListTail->nextSpanUnchecked(thingSize);
                     newFreeSpanStart = 0;
                 }
             } else {
                 if (!newFreeSpanStart)
                     newFreeSpanStart = thing;
-                t->finalize(cx, background);
+                t->finalize(fop);
                 JS_POISON(t, JS_FREE_PATTERN, thingSize);
             }
         }
     }
 
     if (allClear) {
         JS_ASSERT(newListTail == &newListHead);
         JS_ASSERT(newFreeSpanStart == thingsStart(thingKind));
@@ -355,29 +355,29 @@ Arena::finalize(JSContext *cx, AllocKind
 #endif
     aheader.setFirstFreeSpan(&newListHead);
 
     return false;
 }
 
 template<typename T>
 inline void
-FinalizeTypedArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind, bool background)
+FinalizeTypedArenas(FreeOp *fop, ArenaLists::ArenaList *al, AllocKind thingKind)
 {
     /*
      * Release empty arenas and move non-full arenas with some free things into
      * a separated list that we append to al after the loop to ensure that any
      * arena before al->cursor is full.
      */
     JS_ASSERT_IF(!al->head, al->cursor == &al->head);
     ArenaLists::ArenaList available;
     ArenaHeader **ap = &al->head;
     size_t thingSize = Arena::thingSize(thingKind);
     while (ArenaHeader *aheader = *ap) {
-        bool allClear = aheader->getArena()->finalize<T>(cx, thingKind, thingSize, background);
+        bool allClear = aheader->getArena()->finalize<T>(fop, thingKind, thingSize);
         if (allClear) {
             *ap = aheader->next;
             aheader->chunk()->releaseArena(aheader);
         } else if (aheader->hasFreeThings()) {
             *ap = aheader->next;
             *available.cursor = aheader;
             available.cursor = &aheader->next;
         } else {
@@ -392,58 +392,58 @@ FinalizeTypedArenas(JSContext *cx, Arena
     JS_ASSERT_IF(!al->head, al->cursor == &al->head);
 }
 
 /*
  * Finalize the list. On return al->cursor points to the first non-empty arena
  * after the al->head.
  */
 static void
-FinalizeArenas(JSContext *cx, ArenaLists::ArenaList *al, AllocKind thingKind, bool background)
+FinalizeArenas(FreeOp *fop, ArenaLists::ArenaList *al, AllocKind thingKind)
 {
     switch(thingKind) {
       case FINALIZE_OBJECT0:
       case FINALIZE_OBJECT0_BACKGROUND:
       case FINALIZE_OBJECT2:
       case FINALIZE_OBJECT2_BACKGROUND:
       case FINALIZE_OBJECT4:
       case FINALIZE_OBJECT4_BACKGROUND:
       case FINALIZE_OBJECT8:
       case FINALIZE_OBJECT8_BACKGROUND:
       case FINALIZE_OBJECT12:
       case FINALIZE_OBJECT12_BACKGROUND:
       case FINALIZE_OBJECT16:
       case FINALIZE_OBJECT16_BACKGROUND:
-        FinalizeTypedArenas<JSObject>(cx, al, thingKind, background);
+        FinalizeTypedArenas<JSObject>(fop, al, thingKind);
         break;
       case FINALIZE_SCRIPT:
-	FinalizeTypedArenas<JSScript>(cx, al, thingKind, background);
+	FinalizeTypedArenas<JSScript>(fop, al, thingKind);
         break;
       case FINALIZE_SHAPE:
-	FinalizeTypedArenas<Shape>(cx, al, thingKind, background);
+	FinalizeTypedArenas<Shape>(fop, al, thingKind);
         break;
       case FINALIZE_BASE_SHAPE:
-        FinalizeTypedArenas<BaseShape>(cx, al, thingKind, background);
+        FinalizeTypedArenas<BaseShape>(fop, al, thingKind);
         break;
       case FINALIZE_TYPE_OBJECT:
-	FinalizeTypedArenas<types::TypeObject>(cx, al, thingKind, background);
+	FinalizeTypedArenas<types::TypeObject>(fop, al, thingKind);
         break;
 #if JS_HAS_XML_SUPPORT
       case FINALIZE_XML:
-	FinalizeTypedArenas<JSXML>(cx, al, thingKind, background);
+	FinalizeTypedArenas<JSXML>(fop, al, thingKind);
         break;
 #endif
       case FINALIZE_STRING:
-	FinalizeTypedArenas<JSString>(cx, al, thingKind, background);
+	FinalizeTypedArenas<JSString>(fop, al, thingKind);
         break;
       case FINALIZE_SHORT_STRING:
-	FinalizeTypedArenas<JSShortString>(cx, al, thingKind, background);
+	FinalizeTypedArenas<JSShortString>(fop, al, thingKind);
         break;
       case FINALIZE_EXTERNAL_STRING:
-	FinalizeTypedArenas<JSExternalString>(cx, al, thingKind, background);
+	FinalizeTypedArenas<JSExternalString>(fop, al, thingKind);
         break;
     }
 }
 
 static inline Chunk *
 AllocChunk() {
     return static_cast<Chunk *>(MapAlignedPages(ChunkSize, ChunkSize));
 }
@@ -1486,94 +1486,97 @@ ArenaLists::allocateFromArena(JSCompartm
     JS_ASSERT(!aheader->hasFreeThings());
     uintptr_t arenaAddr = aheader->arenaAddress();
     return freeLists[thingKind].allocateFromNewArena(arenaAddr,
                                                      Arena::firstThingOffset(thingKind),
                                                      Arena::thingSize(thingKind));
 }
 
 void
-ArenaLists::finalizeNow(JSContext *cx, AllocKind thingKind)
+ArenaLists::finalizeNow(FreeOp *fop, AllocKind thingKind)
 {
+    JS_ASSERT(!fop->onBackgroundThread());
 #ifdef JS_THREADSAFE
     JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
 #endif
-    FinalizeArenas(cx, &arenaLists[thingKind], thingKind, false);
+    FinalizeArenas(fop, &arenaLists[thingKind], thingKind);
 }
 
 inline void
-ArenaLists::finalizeLater(JSContext *cx, AllocKind thingKind)
+ArenaLists::finalizeLater(FreeOp *fop, AllocKind thingKind)
 {
     JS_ASSERT(thingKind == FINALIZE_OBJECT0_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT2_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT4_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT8_BACKGROUND  ||
               thingKind == FINALIZE_OBJECT12_BACKGROUND ||
               thingKind == FINALIZE_OBJECT16_BACKGROUND ||
               thingKind == FINALIZE_SHORT_STRING        ||
               thingKind == FINALIZE_STRING);
+    JS_ASSERT(!fop->onBackgroundThread());
 
 #ifdef JS_THREADSAFE
-    JS_ASSERT(!cx->runtime->gcHelperThread.sweeping());
+    JS_ASSERT(!fop->runtime()->gcHelperThread.sweeping());
 
     ArenaList *al = &arenaLists[thingKind];
     if (!al->head) {
         JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE);
         JS_ASSERT(al->cursor == &al->head);
         return;
     }
 
     /*
      * The state can be just-finished if we have not allocated any GC things
      * from the arena list after the previous background finalization.
      */
     JS_ASSERT(backgroundFinalizeState[thingKind] == BFS_DONE ||
               backgroundFinalizeState[thingKind] == BFS_JUST_FINISHED);
 
-    if (cx->gcBackgroundFree) {
+    if (fop->shouldFreeLater()) {
         /*
          * To ensure the finalization order even during the background GC we
          * must use infallibleAppend so arenas scheduled for background
          * finalization would not be finalized now if the append fails.
          */
-        cx->gcBackgroundFree->finalizeVector.infallibleAppend(al->head);
+        fop->runtime()->gcHelperThread.finalizeVector.infallibleAppend(al->head);
         al->clear();
         backgroundFinalizeState[thingKind] = BFS_RUN;
     } else {
-        FinalizeArenas(cx, al, thingKind, false);
+        FinalizeArenas(fop, al, thingKind);
         backgroundFinalizeState[thingKind] = BFS_DONE;
     }
 
 #else /* !JS_THREADSAFE */
 
-    finalizeNow(cx, thingKind);
+    finalizeNow(fop, thingKind);
 
 #endif
 }
 
 #ifdef JS_THREADSAFE
 /*static*/ void
-ArenaLists::backgroundFinalize(JSContext *cx, ArenaHeader *listHead)
+ArenaLists::backgroundFinalize(FreeOp *fop, ArenaHeader *listHead)
 {
+    JS_ASSERT(fop->onBackgroundThread());
     JS_ASSERT(listHead);
     AllocKind thingKind = listHead->getAllocKind();
     JSCompartment *comp = listHead->compartment;
     ArenaList finalized;
     finalized.head = listHead;
-    FinalizeArenas(cx, &finalized, thingKind, true);
+    FinalizeArenas(fop, &finalized, thingKind);
 
     /*
      * After we finish the finalization al->cursor must point to the end of
      * the head list as we emptied the list before the background finalization
      * and the allocation adds new arenas before the cursor.
      */
     ArenaLists *lists = &comp->arenas;
     ArenaList *al = &lists->arenaLists[thingKind];
 
-    AutoLockGC lock(cx->runtime);
+    AutoLockGC lock(fop->runtime());
     JS_ASSERT(lists->backgroundFinalizeState[thingKind] == BFS_RUN);
     JS_ASSERT(!*al->cursor);
 
     /*
      * We must set the state to BFS_JUST_FINISHED if we touch arenaList list,
      * even if we add to the list only fully allocated arenas without any free
      * things. It ensures that the allocation thread takes the GC lock and all
      * writes to the free list elements are propagated. As we always take the
@@ -1588,60 +1591,60 @@ ArenaLists::backgroundFinalize(JSContext
         lists->backgroundFinalizeState[thingKind] = BFS_JUST_FINISHED;
     } else {
         lists->backgroundFinalizeState[thingKind] = BFS_DONE;
     }
 }
 #endif /* JS_THREADSAFE */
 
 void
-ArenaLists::finalizeObjects(JSContext *cx)
+ArenaLists::finalizeObjects(FreeOp *fop)
 {
-    finalizeNow(cx, FINALIZE_OBJECT0);
-    finalizeNow(cx, FINALIZE_OBJECT2);
-    finalizeNow(cx, FINALIZE_OBJECT4);
-    finalizeNow(cx, FINALIZE_OBJECT8);
-    finalizeNow(cx, FINALIZE_OBJECT12);
-    finalizeNow(cx, FINALIZE_OBJECT16);
+    finalizeNow(fop, FINALIZE_OBJECT0);
+    finalizeNow(fop, FINALIZE_OBJECT2);
+    finalizeNow(fop, FINALIZE_OBJECT4);
+    finalizeNow(fop, FINALIZE_OBJECT8);
+    finalizeNow(fop, FINALIZE_OBJECT12);
+    finalizeNow(fop, FINALIZE_OBJECT16);
 
 #ifdef JS_THREADSAFE
-    finalizeLater(cx, FINALIZE_OBJECT0_BACKGROUND);
-    finalizeLater(cx, FINALIZE_OBJECT2_BACKGROUND);
-    finalizeLater(cx, FINALIZE_OBJECT4_BACKGROUND);
-    finalizeLater(cx, FINALIZE_OBJECT8_BACKGROUND);
-    finalizeLater(cx, FINALIZE_OBJECT12_BACKGROUND);
-    finalizeLater(cx, FINALIZE_OBJECT16_BACKGROUND);
+    finalizeLater(fop, FINALIZE_OBJECT0_BACKGROUND);
+    finalizeLater(fop, FINALIZE_OBJECT2_BACKGROUND);
+    finalizeLater(fop, FINALIZE_OBJECT4_BACKGROUND);
+    finalizeLater(fop, FINALIZE_OBJECT8_BACKGROUND);
+    finalizeLater(fop, FINALIZE_OBJECT12_BACKGROUND);
+    finalizeLater(fop, FINALIZE_OBJECT16_BACKGROUND);
 #endif
 
 #if JS_HAS_XML_SUPPORT
-    finalizeNow(cx, FINALIZE_XML);
+    finalizeNow(fop, FINALIZE_XML);
 #endif
 }
 
 void
-ArenaLists::finalizeStrings(JSContext *cx)
+ArenaLists::finalizeStrings(FreeOp *fop)
 {
-    finalizeLater(cx, FINALIZE_SHORT_STRING);
-    finalizeLater(cx, FINALIZE_STRING);
-
-    finalizeNow(cx, FINALIZE_EXTERNAL_STRING);
+    finalizeLater(fop, FINALIZE_SHORT_STRING);
+    finalizeLater(fop, FINALIZE_STRING);
+
+    finalizeNow(fop, FINALIZE_EXTERNAL_STRING);
 }
 
 void
-ArenaLists::finalizeShapes(JSContext *cx)
+ArenaLists::finalizeShapes(FreeOp *fop)
 {
-    finalizeNow(cx, FINALIZE_SHAPE);
-    finalizeNow(cx, FINALIZE_BASE_SHAPE);
-    finalizeNow(cx, FINALIZE_TYPE_OBJECT);
+    finalizeNow(fop, FINALIZE_SHAPE);
+    finalizeNow(fop, FINALIZE_BASE_SHAPE);
+    finalizeNow(fop, FINALIZE_TYPE_OBJECT);
 }
 
 void
-ArenaLists::finalizeScripts(JSContext *cx)
+ArenaLists::finalizeScripts(FreeOp *fop)
 {
-    finalizeNow(cx, FINALIZE_SCRIPT);
+    finalizeNow(fop, FINALIZE_SCRIPT);
 }
 
 static void
 RunLastDitchGC(JSContext *cx, gcreason::Reason reason, bool full)
 {
     JSRuntime *rt = cx->runtime;
 
     /* The last ditch GC preserves all atoms. */
@@ -2766,18 +2769,19 @@ GCHelperThread::doSweep()
     if (JSContext *cx = finalizationContext) {
         finalizationContext = NULL;
         AutoUnlockGC unlock(rt);
 
         /*
          * We must finalize in the insert order, see comments in
          * finalizeObjects.
          */
+        FreeOp fop(rt, false, true, cx);
         for (ArenaHeader **i = finalizeVector.begin(); i != finalizeVector.end(); ++i)
-            ArenaLists::backgroundFinalize(cx, *i);
+            ArenaLists::backgroundFinalize(&fop, *i);
         finalizeVector.resize(0);
 
         if (freeCursor) {
             void **array = freeCursorEnd - FREE_ARRAY_LENGTH;
             freeElementsAndArray(array, freeCursor);
             freeCursor = freeCursorEnd = NULL;
         } else {
             JS_ASSERT(!freeCursorEnd);
@@ -2822,19 +2826,19 @@ ReleaseObservedTypes(JSRuntime *rt)
         releaseTypes = true;
         rt->gcJitReleaseTime = now + JIT_SCRIPT_RELEASE_TYPES_INTERVAL;
     }
 
     return releaseTypes;
 }
 
 static void
-SweepCompartments(JSContext *cx, JSGCInvocationKind gckind)
+SweepCompartments(FreeOp *fop, JSGCInvocationKind gckind)
 {
-    JSRuntime *rt = cx->runtime;
+    JSRuntime *rt = fop->runtime();
     JSCompartmentCallback callback = rt->compartmentCallback;
 
     /* Skip the atomsCompartment. */
     JSCompartment **read = rt->compartments.begin() + 1;
     JSCompartment **end = rt->compartments.end();
     JSCompartment **write = read;
     JS_ASSERT(rt->compartments.length() >= 1);
     JS_ASSERT(*rt->compartments.begin() == rt->atomsCompartment);
@@ -2842,20 +2846,20 @@ SweepCompartments(JSContext *cx, JSGCInv
     while (read < end) {
         JSCompartment *compartment = *read++;
 
         if (!compartment->hold &&
             (compartment->arenas.arenaListsAreEmpty() || !rt->hasContexts()))
         {
             compartment->arenas.checkEmptyFreeLists();
             if (callback)
-                JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY));
+                JS_ALWAYS_TRUE(callback(fop->context, compartment, JSCOMPARTMENT_DESTROY));
             if (compartment->principals)
                 JS_DropPrincipals(rt, compartment->principals);
-            cx->delete_(compartment);
+            fop->delete_(compartment);
             continue;
         }
         *write++ = compartment;
     }
     rt->compartments.resize(write - rt->compartments.begin());
 }
 
 static void
@@ -3107,72 +3111,73 @@ SweepPhase(JSContext *cx, JSGCInvocation
     if (rt->hasContexts() && rt->gcHelperThread.prepareForBackgroundSweep())
         cx->gcBackgroundFree = &rt->gcHelperThread;
 #endif
 
     /* Purge the ArenaLists before sweeping. */
     for (GCCompartmentsIter c(rt); !c.done(); c.next())
         c->arenas.purge();
 
+    FreeOp fop(rt, !!cx->gcBackgroundFree, false, cx);
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_START);
         if (rt->gcFinalizeCallback)
-            rt->gcFinalizeCallback(cx, JSFINALIZE_START);
+            rt->gcFinalizeCallback(&fop, JSFINALIZE_START);
     }
 
     /* Finalize unreachable (key,value) pairs in all weak maps. */
     WeakMapBase::sweepAll(&rt->gcMarker);
 
     js_SweepAtomState(rt);
 
     /* Collect watch points associated with unreachable objects. */
     WatchpointMap::sweepAll(rt);
 
     /* Detach unreachable debuggers and global objects from each other. */
-    Debugger::sweepAll(cx);
+    Debugger::sweepAll(&fop);
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_COMPARTMENTS);
 
         bool releaseTypes = rt->gcIsFull && ReleaseObservedTypes(rt);
         for (GCCompartmentsIter c(rt); !c.done(); c.next())
-            c->sweep(cx, releaseTypes);
+            c->sweep(&fop, releaseTypes);
     }
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_OBJECT);
 
         /*
          * We finalize objects before other GC things to ensure that the object's
          * finalizer can access the other things even if they will be freed.
          */
         for (GCCompartmentsIter c(rt); !c.done(); c.next())
-            c->arenas.finalizeObjects(cx);
+            c->arenas.finalizeObjects(&fop);
     }
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_STRING);
         for (GCCompartmentsIter c(rt); !c.done(); c.next())
-            c->arenas.finalizeStrings(cx);
+            c->arenas.finalizeStrings(&fop);
     }
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_SCRIPT);
         for (GCCompartmentsIter c(rt); !c.done(); c.next())
-            c->arenas.finalizeScripts(cx);
+            c->arenas.finalizeScripts(&fop);
     }
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_SWEEP_SHAPE);
         for (GCCompartmentsIter c(rt); !c.done(); c.next())
-            c->arenas.finalizeShapes(cx);
+            c->arenas.finalizeShapes(&fop);
     }
 
 #ifdef DEBUG
-     PropertyTree::dumpShapes(cx);
+     PropertyTree::dumpShapes(rt);
 #endif
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DESTROY);
 
         /*
          * Sweep script filenames after sweeping functions in the generic loop
          * above. In this way when a scripted function's finalizer destroys the
@@ -3182,32 +3187,32 @@ SweepPhase(JSContext *cx, JSGCInvocation
         for (GCCompartmentsIter c(rt); !c.done(); c.next())
             SweepScriptFilenames(c);
 
         /*
          * This removes compartments from rt->compartment, so we do it last to make
          * sure we don't miss sweeping any compartments.
          */
         if (rt->gcIsFull)
-            SweepCompartments(cx, gckind);
+            SweepCompartments(&fop, gckind);
 
 #ifndef JS_THREADSAFE
         /*
          * Destroy arenas after we finished the sweeping so finalizers can safely
          * use IsAboutToBeFinalized().
          * This is done on the GCHelperThread if JS_THREADSAFE is defined.
          */
         ExpireChunksAndArenas(rt, gckind == GC_SHRINK);
 #endif
     }
 
     {
         gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_FINALIZE_END);
         if (rt->gcFinalizeCallback)
-            rt->gcFinalizeCallback(cx, JSFINALIZE_END);
+            rt->gcFinalizeCallback(&fop, JSFINALIZE_END);
     }
 
     for (CompartmentsIter c(rt); !c.done(); c.next())
         c->setGCLastBytes(c->gcBytes, c->gcMallocAndFreeBytes, gckind);
 }
 
 /* Perform mark-and-sweep GC. If comp is set, we perform a single-compartment GC. */
 static void
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -587,17 +587,17 @@ struct Arena {
         return address() | firstThingOffset(thingKind);
     }
 
     uintptr_t thingsEnd() {
         return address() + ArenaSize;
     }
 
     template <typename T>
-    bool finalize(JSContext *cx, AllocKind thingKind, size_t thingSize, bool background);
+    bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize);
 };
 
 /* The chunk header (located at the end of the chunk to preserve arena alignment). */
 struct ChunkInfo {
     Chunk           *next;
     Chunk           **prevp;
 
     /* Free arenas are linked together with aheader.next. */
@@ -1246,28 +1246,28 @@ struct ArenaLists {
             JS_ASSERT(freeLists[i].isEmpty());
 #endif
     }
 
     void checkEmptyFreeList(AllocKind kind) {
         JS_ASSERT(freeLists[kind].isEmpty());
     }
 
-    void finalizeObjects(JSContext *cx);
-    void finalizeStrings(JSContext *cx);
-    void finalizeShapes(JSContext *cx);
-    void finalizeScripts(JSContext *cx);
+    void finalizeObjects(FreeOp *fop);
+    void finalizeStrings(FreeOp *fop);
+    void finalizeShapes(FreeOp *fop);
+    void finalizeScripts(FreeOp *fop);
 
 #ifdef JS_THREADSAFE
-    static void backgroundFinalize(JSContext *cx, ArenaHeader *listHead);
+    static void backgroundFinalize(FreeOp *fop, ArenaHeader *listHead);
 #endif
 
   private:
-    inline void finalizeNow(JSContext *cx, AllocKind thingKind);
-    inline void finalizeLater(JSContext *cx, AllocKind thingKind);
+    inline void finalizeNow(FreeOp *fop, AllocKind thingKind);
+    inline void finalizeLater(FreeOp *fop, AllocKind thingKind);
 
     inline void *allocateFromArena(JSCompartment *comp, AllocKind thingKind);
 };
 
 /*
  * Initial allocation size for data structures holding chunks is set to hold
  * chunks with total capacity of 16MB to avoid buffer resizes during browser
  * startup.
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5972,17 +5972,17 @@ TypeCompartment::sweep(JSContext *cx)
     if (pendingArray)
         cx->free_(pendingArray);
 
     pendingArray = NULL;
     pendingCapacity = 0;
 }
 
 void
-JSCompartment::sweepNewTypeObjectTable(JSContext *cx, TypeObjectSet &table)
+JSCompartment::sweepNewTypeObjectTable(TypeObjectSet &table)
 {
     if (table.initialized()) {
         for (TypeObjectSet::Enum e(table); !e.empty(); e.popFront()) {
             TypeObject *type = e.front();
             if (!type->isMarked())
                 e.removeFront();
         }
     }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -870,17 +870,17 @@ struct TypeObject : gc::Cell
 
     void sizeOfExcludingThis(TypeInferenceSizes *sizes, JSMallocSizeOfFun mallocSizeOf);
 
     /*
      * Type objects don't have explicit finalizers. Memory owned by a type
      * object pending deletion is released when weak references are sweeped
      * from all the compartment's type objects.
      */
-    void finalize(JSContext *cx, bool background) {}
+    void finalize(FreeOp *fop) {}
 
     static inline void writeBarrierPre(TypeObject *type);
     static inline void writeBarrierPost(TypeObject *type, void *addr);
     static inline void readBarrier(TypeObject *type);
 
     static inline ThingRootKind rootKind() { return THING_ROOT_TYPE_OBJECT; }
 
   private:
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -79,17 +79,17 @@
 #include "vm/MethodGuard-inl.h"
 #include "vm/Stack-inl.h"
 #include "vm/String-inl.h"
 
 using namespace mozilla;
 using namespace js;
 using namespace js::gc;
 
-static void iterator_finalize(JSContext *cx, JSObject *obj);
+static void iterator_finalize(FreeOp *fop, JSObject *obj);
 static void iterator_trace(JSTracer *trc, JSObject *obj);
 static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
 
 Class js::IteratorClass = {
     "Iterator",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
     JS_PropertyStub,         /* addProperty */
@@ -146,24 +146,24 @@ NativeIterator::mark(JSTracer *trc)
 {
     for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
         MarkString(trc, str, "prop");
     if (obj)
         MarkObject(trc, &obj, "obj");
 }
 
 static void
-iterator_finalize(JSContext *cx, JSObject *obj)
+iterator_finalize(FreeOp *fop, JSObject *obj)
 {
     JS_ASSERT(obj->isIterator());
 
     NativeIterator *ni = obj->getNativeIterator();
     if (ni) {
         obj->setPrivate(NULL);
-        cx->free_(ni);
+        fop->free_(ni);
     }
 }
 
 static void
 iterator_trace(JSTracer *trc, JSObject *obj)
 {
     NativeIterator *ni = obj->getNativeIterator();
 
@@ -1326,30 +1326,30 @@ Class js::StopIterationClass = {
     NULL,                    /* call        */
     NULL,                    /* construct   */
     stopiter_hasInstance
 };
 
 #if JS_HAS_GENERATORS
 
 static void
-generator_finalize(JSContext *cx, JSObject *obj)
+generator_finalize(FreeOp *fop, JSObject *obj)
 {
     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
     if (!gen)
         return;
 
     /*
      * gen is open when a script has not called its close method while
      * explicitly manipulating it.
      */
     JS_ASSERT(gen->state == JSGEN_NEWBORN ||
               gen->state == JSGEN_CLOSED ||
               gen->state == JSGEN_OPEN);
-    cx->free_(gen);
+    fop->free_(gen);
 }
 
 static void
 MarkGenerator(JSTracer *trc, JSGenerator *gen)
 {
     StackFrame *fp = gen->floatingFrame();
 
     /*
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -839,18 +839,18 @@ struct JSObject : public js::ObjectImpl
     inline jsval getQNameLocalNameVal() const;
     inline void setQNameLocalName(JSAtom *name);
 
     /*
      * Back to generic stuff.
      */
     inline bool isCallable();
 
-    inline void finish(JSContext *cx);
-    JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background);
+    inline void finish(js::FreeOp *fop);
+    JS_ALWAYS_INLINE void finalize(js::FreeOp *fop);
 
     inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, unsigned flags = 0);
 
     /*
      * Allocate and free an object slot.
      *
      * FIXME: bug 593129 -- slot allocation should be done by object methods
      * after calling object-parameter-free shape methods, avoiding coupling
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -236,32 +236,32 @@ JSObject::deleteSpecial(JSContext *cx, j
     jsid id = SPECIALID_TO_JSID(sid);
     js::types::AddTypePropertyId(cx, this, id, js::types::Type::UndefinedType());
     js::types::MarkTypePropertyConfigured(cx, this, id);
     js::DeleteSpecialOp op = getOps()->deleteSpecial;
     return (op ? op : js_DeleteSpecial)(cx, this, sid, rval, strict);
 }
 
 inline void
-JSObject::finalize(JSContext *cx, bool background)
+JSObject::finalize(js::FreeOp *fop)
 {
     js::Probes::finalizeObject(this);
 
-    if (!background) {
+    if (!fop->onBackgroundThread()) {
         /*
          * Finalize obj first, in case it needs map and slots. Objects with
          * finalize hooks are not finalized in the background, as the class is
          * stored in the object's shape, which may have already been destroyed.
          */
         js::Class *clasp = getClass();
         if (clasp->finalize)
-            clasp->finalize(cx, this);
+            clasp->finalize(fop, this);
     }
 
-    finish(cx);
+    finish(fop);
 }
 
 inline JSObject *
 JSObject::getParent() const
 {
     return lastProperty()->getObjectParent();
 }
 
@@ -887,22 +887,22 @@ JSObject::createDenseArray(JSContext *cx
     obj->slots = NULL;
     obj->setFixedElements();
     new (obj->getElementsHeader()) js::ObjectElements(capacity, length);
 
     return obj;
 }
 
 inline void
-JSObject::finish(JSContext *cx)
+JSObject::finish(js::FreeOp *fop)
 {
     if (hasDynamicSlots())
-        cx->free_(slots);
+        fop->free_(slots);
     if (hasDynamicElements())
-        cx->free_(getElementsHeader());
+        fop->free_(getElementsHeader());
 }
 
 inline bool
 JSObject::hasProperty(JSContext *cx, jsid id, bool *foundp, unsigned flags)
 {
     JSObject *pobj;
     JSProperty *prop;
     JSAutoResolveFlags rf(cx, flags);
--- a/js/src/jspropertytree.cpp
+++ b/js/src/jspropertytree.cpp
@@ -215,24 +215,24 @@ PropertyTree::getChild(JSContext *cx, Sh
 
     if (!insertChild(cx, parent, shape))
         return NULL;
 
     return shape;
 }
 
 void
-Shape::finalize(JSContext *cx, bool background)
+Shape::finalize(FreeOp *fop)
 {
     if (!inDictionary()) {
         if (parent && parent->isMarked())
             parent->removeChild(this);
 
         if (kids.isHash())
-            cx->delete_(kids.toHash());
+            fop->delete_(kids.toHash());
     }
 }
 
 #ifdef DEBUG
 
 void
 KidsPointer::checkConsistency(const Shape *aKid) const
 {
@@ -332,41 +332,40 @@ Shape::dumpSubtree(JSContext *cx, int le
                 JS_ASSERT(kid->parent == this);
                 kid->dumpSubtree(cx, level, fp);
             }
         }
     }
 }
 
 void
-js::PropertyTree::dumpShapes(JSContext *cx)
+js::PropertyTree::dumpShapes(JSRuntime *rt)
 {
     static bool init = false;
     static FILE *dumpfp = NULL;
     if (!init) {
         init = true;
         const char *name = getenv("JS_DUMP_SHAPES_FILE");
         if (!name)
             return;
         dumpfp = fopen(name, "a");
     }
 
     if (!dumpfp)
         return;
 
-    JSRuntime *rt = cx->runtime;
     fprintf(dumpfp, "rt->gcNumber = %lu", (unsigned long)rt->gcNumber);
 
     for (gc::GCCompartmentsIter c(rt); !c.done(); c.next()) {
         fprintf(dumpfp, "*** Compartment %p ***\n", (void *)c.get());
 
         /*
         typedef JSCompartment::EmptyShapeSet HS;
         HS &h = c->emptyShapes;
         for (HS::Range r = h.all(); !r.empty(); r.popFront()) {
             Shape *empty = r.front();
-            empty->dumpSubtree(cx, 0, dumpfp);
+            empty->dumpSubtree(rt, 0, dumpfp);
             putc('\n', dumpfp);
         }
         */
     }
 }
 #endif
--- a/js/src/jspropertytree.h
+++ b/js/src/jspropertytree.h
@@ -114,16 +114,16 @@ class PropertyTree
         : compartment(comp)
     {
     }
     
     js::Shape *newShape(JSContext *cx);
     js::Shape *getChild(JSContext *cx, Shape *parent, uint32_t nfixed, const StackShape &child);
 
 #ifdef DEBUG
-    static void dumpShapes(JSContext *cx);
+    static void dumpShapes(JSRuntime *rt);
     static void meter(JSBasicStats *bs, js::Shape *node);
 #endif
 };
 
 } /* namespace js */
 
 #endif /* jspropertytree_h___ */
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -395,17 +395,17 @@ ProxyHandler::typeOf(JSContext *cx, JSOb
 bool
 ProxyHandler::objectClassIs(JSObject *proxy, ESClassValue classValue, JSContext *cx)
 {
     JS_ASSERT(OperationInProgress(cx, proxy));
     return false;
 }
 
 void
-ProxyHandler::finalize(JSContext *cx, JSObject *proxy)
+ProxyHandler::finalize(JSFreeOp *fop, JSObject *proxy)
 {
 }
 
 void
 ProxyHandler::trace(JSTracer *trc, JSObject *proxy)
 {
 }
 
@@ -1297,21 +1297,21 @@ proxy_Fix(JSContext *cx, JSObject *obj, 
     if (ok) {
         *fixed = isFixed;
         return GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, props);
     }
     return false;
 }
 
 static void
-proxy_Finalize(JSContext *cx, JSObject *obj)
+proxy_Finalize(FreeOp *fop, JSObject *obj)
 {
     JS_ASSERT(obj->isProxy());
     if (!obj->getSlot(JSSLOT_PROXY_HANDLER).isUndefined())
-        GetProxyHandler(obj)->finalize(cx, obj);
+        GetProxyHandler(obj)->finalize(fop, obj);
 }
 
 static JSBool
 proxy_HasInstance(JSContext *cx, JSObject *proxy, const Value *v, JSBool *bp)
 {
     AutoPendingProxyOperation pending(cx, proxy);
     bool b;
     if (!Proxy::hasInstance(cx, proxy, v, &b))
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -82,17 +82,17 @@ class JS_FRIEND_API(ProxyHandler) {
     virtual bool hasInstance(JSContext *cx, JSObject *proxy, const Value *vp, bool *bp);
     virtual JSType typeOf(JSContext *cx, JSObject *proxy);
     virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
     virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
     virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, unsigned indent);
     virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g);
     virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
     virtual bool iteratorNext(JSContext *cx, JSObject *proxy, Value *vp);
-    virtual void finalize(JSContext *cx, JSObject *proxy);
+    virtual void finalize(JSFreeOp *fop, JSObject *proxy);
     virtual void trace(JSTracer *trc, JSObject *proxy);
     virtual bool getElementIfPresent(JSContext *cx, JSObject *obj, JSObject *receiver,
                                      uint32_t index, Value *vp, bool *present);
 
     virtual bool isOuterWindow() {
         return false;
     }
 
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -319,16 +319,18 @@ typedef Handle<Value>              Handl
 enum XDRMode {
     XDR_ENCODE,
     XDR_DECODE
 };
 
 template <XDRMode mode>
 class XDRState;
 
+class FreeOp;
+
 } /* namespace js */
 
 namespace JSC {
 
 class ExecutableAllocator;
 
 } /* namespace JSC */
 
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -1210,32 +1210,32 @@ BaseShape::getUnowned(JSContext *cx, con
 
     if (!table.relookupOrAdd(p, &base, nbase))
         return NULL;
 
     return nbase;
 }
 
 void
-JSCompartment::sweepBaseShapeTable(JSContext *cx)
+JSCompartment::sweepBaseShapeTable()
 {
     if (baseShapes.initialized()) {
         for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
             UnownedBaseShape *base = e.front();
             if (!base->isMarked())
                 e.removeFront();
         }
     }
 }
 
 void
-BaseShape::finalize(JSContext *cx, bool background)
+BaseShape::finalize(FreeOp *fop)
 {
     if (table_) {
-        cx->delete_(table_);
+        fop->delete_(table_);
         table_ = NULL;
     }
 }
 
 /* static */ Shape *
 Shape::setExtensibleParents(JSContext *cx, Shape *shape)
 {
     JS_ASSERT(!shape->inDictionary());
@@ -1392,17 +1392,17 @@ EmptyShape::insertInitialShape(JSContext
      * the NewObject must always check for a nativeEmpty() result and generate
      * the appropriate properties if found. Clearing the cache entry avoids
      * this duplicate regeneration.
      */
     cx->compartment->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
 }
 
 void
-JSCompartment::sweepInitialShapeTable(JSContext *cx)
+JSCompartment::sweepInitialShapeTable()
 {
     if (initialShapes.initialized()) {
         for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
             const InitialShapeEntry &entry = e.front();
             if (!entry.shape->isMarked() || (entry.proto && !entry.proto->isMarked()))
                 e.removeFront();
         }
     }
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -323,17 +323,17 @@ class BaseShape : public js::gc::Cell
 
     /* For owned BaseShapes, the canonical unowned BaseShape. */
     HeapPtr<UnownedBaseShape> unowned_;
 
     /* For owned BaseShapes, the shape's property table. */
     PropertyTable       *table_;
 
   public:
-    void finalize(JSContext *cx, bool background);
+    void finalize(FreeOp *fop);
 
     inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags);
     inline BaseShape(Class *clasp, JSObject *parent, uint32_t objectFlags,
                      uint8_t attrs, PropertyOp rawGetter, StrictPropertyOp rawSetter);
     inline BaseShape(const StackBaseShape &base);
 
     /* Not defined: BaseShapes must not be stack allocated. */
     ~BaseShape();
@@ -860,17 +860,17 @@ struct Shape : public js::gc::Cell
         return false;
     }
 
 #ifdef DEBUG
     void dump(JSContext *cx, FILE *fp) const;
     void dumpSubtree(JSContext *cx, int level, FILE *fp) const;
 #endif
 
-    void finalize(JSContext *cx, bool background);
+    void finalize(FreeOp *fop);
     void removeChild(js::Shape *child);
 
     static inline void writeBarrierPre(const Shape *shape);
     static inline void writeBarrierPost(const Shape *shape, void *addr);
 
     /*
      * All weak references need a read barrier for incremental GC. This getter
      * method implements the read barrier. It's used to obtain initial shapes
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1387,53 +1387,53 @@ js_CallDestroyScriptHook(JSContext *cx, 
 
     if (JSDestroyScriptHook hook = cx->runtime->debugHooks.destroyScriptHook)
         hook(cx, script, cx->runtime->debugHooks.destroyScriptHookData);
     script->callDestroyHook = false;
     JS_ClearScriptTraps(cx, script);
 }
 
 void
-JSScript::finalize(JSContext *cx, bool background)
+JSScript::finalize(FreeOp *fop)
 {
-    js_CallDestroyScriptHook(cx, this);
+    js_CallDestroyScriptHook(fop->context, this);
 
     JS_ASSERT_IF(principals, originPrincipals);
     if (principals)
-        JS_DropPrincipals(cx->runtime, principals);
+        JS_DropPrincipals(fop->runtime(), principals);
     if (originPrincipals)
-        JS_DropPrincipals(cx->runtime, originPrincipals);
+        JS_DropPrincipals(fop->runtime(), originPrincipals);
 
     if (types)
         types->destroy();
 
 #ifdef JS_METHODJIT
-    mjit::ReleaseScriptCode(cx, this);
+    mjit::ReleaseScriptCode(fop->context, this);
 #endif
 
-    destroyScriptCounts(cx);
+    destroyScriptCounts(fop->context);
 
     if (sourceMap)
-        cx->free_(sourceMap);
+        fop->free_(sourceMap);
 
     if (debug) {
         jsbytecode *end = code + length;
         for (jsbytecode *pc = code; pc < end; pc++) {
             if (BreakpointSite *site = getBreakpointSite(pc)) {
                 /* Breakpoints are swept before finalization. */
                 JS_ASSERT(site->firstBreakpoint() == NULL);
-                site->clearTrap(cx, NULL, NULL);
+                site->clearTrap(fop->context, NULL, NULL);
                 JS_ASSERT(getBreakpointSite(pc) == NULL);
             }
         }
-        cx->free_(debug);
+        fop->free_(debug);
     }
 
     JS_POISON(data, 0xdb, computedSizeOfData());
-    cx->free_(data);
+    fop->free_(data);
 }
 
 namespace js {
 
 static const uint32_t GSN_CACHE_THRESHOLD = 100;
 static const uint32_t GSN_CACHE_MAP_INIT_SIZE = 20;
 
 void
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -798,17 +798,17 @@ struct JSScript : public js::gc::Cell
     bool changeStepModeCount(JSContext *cx, int delta);
 
     bool stepModeEnabled() { return debug && !!debug->stepMode; }
 
 #ifdef DEBUG
     uint32_t stepModeCount() { return debug ? (debug->stepMode & stepCountMask) : 0; }
 #endif
 
-    void finalize(JSContext *cx, bool background);
+    void finalize(js::FreeOp *fop);
 
     static inline void writeBarrierPre(JSScript *script);
     static inline void writeBarrierPost(JSScript *script, void *addr);
 
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
 
     static JSPrincipals *normalizeOriginPrincipals(JSPrincipals *principals,
                                                    JSPrincipals *originPrincipals) {
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -330,26 +330,26 @@ JS_NondeterministicGetWeakMapKeys(JSCont
 static void
 WeakMap_mark(JSTracer *trc, JSObject *obj)
 {
     if (ObjectValueMap *map = GetObjectMap(obj))
         map->trace(trc);
 }
 
 static void
-WeakMap_finalize(JSContext *cx, JSObject *obj)
+WeakMap_finalize(FreeOp *fop, JSObject *obj)
 {
     if (ObjectValueMap *map = GetObjectMap(obj)) {
         map->check();
 #ifdef DEBUG
         map->~ObjectValueMap();
         memset(static_cast<void *>(map), 0xdc, sizeof(*map));
-        cx->free_(map);
+        fop->free_(map);
 #else
-        cx->delete_(map);
+        fop->delete_(map);
 #endif
     }
 }
 
 static JSBool
 WeakMap_construct(JSContext *cx, unsigned argc, Value *vp)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &WeakMapClass);
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1170,23 +1170,23 @@ HAS_NS_AFTER_XML(const jschar *chars)
 
 #define STARTS_WITH_XML(chars,length)                                         \
     (length >= 3 && IS_XML_CHARS(chars))
 
 static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
 static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
 
 void
-JSXML::finalize(JSContext *cx, bool builtin)
+JSXML::finalize(FreeOp *fop)
 {
     if (JSXML_HAS_KIDS(this)) {
-        xml_kids.finish(cx);
+        xml_kids.finish(fop->context);
         if (xml_class == JSXML_CLASS_ELEMENT) {
-            xml_namespaces.finish(cx);
-            xml_attrs.finish(cx);
+            xml_namespaces.finish(fop->context);
+            xml_attrs.finish(fop->context);
         }
     }
 #ifdef DEBUG_notme
     JS_REMOVE_LINK(&links);
 #endif
 }
 
 static JSObject *
@@ -7896,23 +7896,23 @@ xmlfilter_trace(JSTracer *trc, JSObject 
 
     /*
      * We do not need to trace the cursor as that would be done when
      * tracing the filter->list.
      */
 }
 
 static void
-xmlfilter_finalize(JSContext *cx, JSObject *obj)
+xmlfilter_finalize(FreeOp *fop, JSObject *obj)
 {
     JSXMLFilter *filter = (JSXMLFilter *) obj->getPrivate();
     if (!filter)
         return;
 
-    cx->delete_(filter);
+    fop->delete_(filter);
 }
 
 Class js_XMLFilterClass = {
     "XMLFilter",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
--- a/js/src/jsxml.h
+++ b/js/src/jsxml.h
@@ -198,17 +198,17 @@ struct JSXML : js::gc::Cell {
     JSXMLElemVar        elem;
     js::HeapPtrString   value;
 
 #if JS_BITS_PER_WORD == 32
     /* The size of every GC thing must be divisible by the FreeCell size. */
     void *pad;
 #endif
 
-    void finalize(JSContext *cx, bool background);
+    void finalize(js::FreeOp *fop);
 
     static void writeBarrierPre(JSXML *xml);
     static void writeBarrierPost(JSXML *xml, void *addr);
 };
 
 /* xml_flags values */
 #define XMLF_WHITESPACE_TEXT    0x1
 
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -179,17 +179,17 @@ static const struct pm_const {
     CONSTANT(ALL),
     CONSTANT(NUM_MEASURABLE_EVENTS),
     { 0, PerfMeasurement::EventMask(0) }
 };
 
 #undef CONSTANT
 
 static JSBool pm_construct(JSContext* cx, unsigned argc, jsval* vp);
-static void pm_finalize(JSContext* cx, JSObject* obj);
+static void pm_finalize(JSFreeOp* fop, JSObject* obj);
 
 static JSClass pm_class = {
     "PerfMeasurement", JSCLASS_HAS_PRIVATE,
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, pm_finalize
 };
 
 // Constructor and destructor
@@ -215,19 +215,19 @@ pm_construct(JSContext* cx, unsigned arg
     }
 
     JS_SetPrivate(obj, p);
     *vp = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }
 
 static void
-pm_finalize(JSContext* cx, JSObject* obj)
+pm_finalize(JSFreeOp* fop, JSObject* obj)
 {
-    cx->delete_((PerfMeasurement*) JS_GetPrivate(obj));
+    js::FreeOp::get(fop)->delete_(static_cast<PerfMeasurement*>(JS_GetPrivate(obj)));
 }
 
 // Helpers (declared above)
 
 static PerfMeasurement*
 GetPM(JSContext* cx, JSObject* obj, const char* fname)
 {
     PerfMeasurement* p = (PerfMeasurement*)
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4082,24 +4082,24 @@ static JSBool
 its_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
 {
     if (its_noisy)
         fprintf(gOutFile, "converting it to %s type\n", JS_GetTypeName(cx, type));
     return JS_ConvertStub(cx, obj, type, vp);
 }
 
 static void
-its_finalize(JSContext *cx, JSObject *obj)
+its_finalize(JSFreeOp *fop, JSObject *obj)
 {
     jsval *rootedVal;
     if (its_noisy)
         fprintf(gOutFile, "finalizing it\n");
     rootedVal = (jsval *) JS_GetPrivate(obj);
     if (rootedVal) {
-      JS_RemoveValueRoot(cx, rootedVal);
+      JS_RemoveValueRoot(fop->context, rootedVal);
       JS_SetPrivate(obj, NULL);
       delete rootedVal;
     }
 }
 
 static JSClass its_class = {
     "It", JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE | JSCLASS_HAS_PRIVATE,
     its_addProperty,  its_delProperty,  its_getProperty,  its_setProperty,
--- a/js/src/shell/jsworkers.cpp
+++ b/js/src/shell/jsworkers.cpp
@@ -571,17 +571,17 @@ class ThreadPool
         ThreadPool *tp = unwrap(obj);
         if (tp->mq) {
             tp->mq->traceChildren(trc);
             tp->wq->trace(trc);
         }
     }
 
 
-    static void jsFinalize(JSContext *cx, JSObject *obj) {
+    static void jsFinalize(JSFreeOp *fop, JSObject *obj) {
         if (ThreadPool *tp = unwrap(obj))
             delete tp;
     }
 
   public:
     static ThreadPool *unwrap(JSObject *obj) {
         JS_ASSERT(JS_GetClass(obj) == &jsClass);
         return (ThreadPool *) JS_GetPrivate(obj);
@@ -707,17 +707,17 @@ class Worker MOZ_FINAL : public WorkerPa
             w->parent->trace(trc);
             w->events.trace(trc);
             if (w->current)
                 w->current->trace(trc);
             JS_CALL_OBJECT_TRACER(trc, JS_GetGlobalObject(w->context), "Worker global");
         }
     }
 
-    static void jsFinalize(JSContext *cx, JSObject *obj) {
+    static void jsFinalize(JSFreeOp *fop, JSObject *obj) {
         JS_ASSERT(JS_GetClass(obj) == &jsWorkerClass);
         if (Worker *w = (Worker *) JS_GetPrivate(obj))
             delete w;
     }
 
     static JSBool jsOperationCallback(JSContext *cx) {
         Worker *w = (Worker *) JS_GetContextPrivate(cx);
         JSAutoSuspendRequest suspend(cx);  // avoid nesting w->lock in a request
--- a/js/src/vm/ArgumentsObject.cpp
+++ b/js/src/vm/ArgumentsObject.cpp
@@ -491,19 +491,19 @@ strictargs_enumerate(JSContext *cx, JSOb
         if (!js_LookupProperty(cx, argsobj, INT_TO_JSID(i), &pobj, &prop))
             return false;
     }
 
     return true;
 }
 
 static void
-args_finalize(JSContext *cx, JSObject *obj)
+args_finalize(FreeOp *fop, JSObject *obj)
 {
-    cx->free_(reinterpret_cast<void *>(obj->asArguments().data()));
+    fop->free_(reinterpret_cast<void *>(obj->asArguments().data()));
 }
 
 static void
 args_trace(JSTracer *trc, JSObject *obj)
 {
     ArgumentsObject &argsobj = obj->asArguments();
     ArgumentsData *data = argsobj.data();
     MarkValue(trc, &data->callee, js_callee_str);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1457,64 +1457,64 @@ Debugger::trace(JSTracer *trc)
     /* Trace the referent -> Debugger.Object weak map. */
     objects.trace(trc);
 
     /* Trace the referent -> Debugger.Environment weak map. */
     environments.trace(trc);
 }
 
 void
-Debugger::sweepAll(JSContext *cx)
+Debugger::sweepAll(FreeOp *fop)
 {
-    JSRuntime *rt = cx->runtime;
+    JSRuntime *rt = fop->runtime();
 
     for (JSCList *p = &rt->debuggerList; (p = JS_NEXT_LINK(p)) != &rt->debuggerList;) {
         Debugger *dbg = Debugger::fromLinks(p);
 
         if (IsAboutToBeFinalized(dbg->object)) {
             /*
              * dbg is being GC'd. Detach it from its debuggees. The debuggee
              * might be GC'd too. Since detaching requires access to both
              * objects, this must be done before finalize time.
              */
             for (GlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
-                dbg->removeDebuggeeGlobal(cx, e.front(), NULL, &e);
+                dbg->removeDebuggeeGlobal(fop->context, e.front(), NULL, &e);
         }
 
     }
 
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); c++) {
         /* For each debuggee being GC'd, detach it from all its debuggers. */
         GlobalObjectSet &debuggees = (*c)->getDebuggees();
         for (GlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
             GlobalObject *global = e.front();
             if (IsAboutToBeFinalized(global))
-                detachAllDebuggersFromGlobal(cx, global, &e);
+                detachAllDebuggersFromGlobal(fop->context, global, &e);
         }
     }
 }
 
 void
 Debugger::detachAllDebuggersFromGlobal(JSContext *cx, GlobalObject *global,
                                        GlobalObjectSet::Enum *compartmentEnum)
 {
     const GlobalObject::DebuggerVector *debuggers = global->getDebuggers();
     JS_ASSERT(!debuggers->empty());
     while (!debuggers->empty())
         debuggers->back()->removeDebuggeeGlobal(cx, global, compartmentEnum, NULL);
 }
 
 void
-Debugger::finalize(JSContext *cx, JSObject *obj)
+Debugger::finalize(FreeOp *fop, JSObject *obj)
 {
     Debugger *dbg = fromJSObject(obj);
     if (!dbg)
         return;
     JS_ASSERT(dbg->debuggees.empty());
-    cx->delete_(dbg);
+    fop->delete_(dbg);
 }
 
 Class Debugger::jsclass = {
     "Debugger",
     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Debugger::finalize,
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -169,17 +169,17 @@ class Debugger {
      */
     JSTrapStatus parseResumptionValue(AutoCompartment &ac, bool ok, const Value &rv, Value *vp,
                                       bool callHook = true);
 
     JSObject *unwrapDebuggeeArgument(JSContext *cx, const Value &v);
 
     static void traceObject(JSTracer *trc, JSObject *obj);
     void trace(JSTracer *trc);
-    static void finalize(JSContext *cx, JSObject *obj);
+    static void finalize(FreeOp *fop, JSObject *obj);
     void markKeysInCompartment(JSTracer *tracer);
 
     static Class jsclass;
 
     static Debugger *fromThisValue(JSContext *cx, const CallArgs &ca, const char *fnname);
     static JSBool getEnabled(JSContext *cx, unsigned argc, Value *vp);
     static JSBool setEnabled(JSContext *cx, unsigned argc, Value *vp);
     static JSBool getHookImpl(JSContext *cx, unsigned argc, Value *vp, Hook which);
@@ -258,17 +258,17 @@ class Debugger {
      *       - it has a watchpoint set on a live object.
      *
      * Debugger::markAllIteratively handles the last case. If it finds any
      * Debugger objects that are definitely live but not yet marked, it marks
      * them and returns true. If not, it returns false.
      */
     static void markCrossCompartmentDebuggerObjectReferents(JSTracer *tracer);
     static bool markAllIteratively(GCMarker *trc);
-    static void sweepAll(JSContext *cx);
+    static void sweepAll(FreeOp *fop);
     static void detachAllDebuggersFromGlobal(JSContext *cx, GlobalObject *global,
                                              GlobalObjectSet::Enum *compartmentEnum);
 
     static inline JSTrapStatus onEnterFrame(JSContext *cx, Value *vp);
     static inline bool onLeaveFrame(JSContext *cx, bool ok);
     static inline JSTrapStatus onDebuggerStatement(JSContext *cx, Value *vp);
     static inline JSTrapStatus onExceptionUnwind(JSContext *cx, Value *vp);
     static inline void onNewScript(JSContext *cx, JSScript *script,
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -448,19 +448,19 @@ DefinePropertiesAndBrand(JSContext *cx, 
     RootObject root(cx, &obj);
 
     if ((ps && !JS_DefineProperties(cx, obj, ps)) || (fs && !JS_DefineFunctions(cx, obj, fs)))
         return false;
     return true;
 }
 
 void
-GlobalDebuggees_finalize(JSContext *cx, JSObject *obj)
+GlobalDebuggees_finalize(FreeOp *fop, JSObject *obj)
 {
-    cx->delete_((GlobalObject::DebuggerVector *) obj->getPrivate());
+    fop->delete_((GlobalObject::DebuggerVector *) obj->getPrivate());
 }
 
 static Class
 GlobalDebuggees_class = {
     "GlobalDebuggee", JSCLASS_HAS_PRIVATE,
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, GlobalDebuggees_finalize
 };
--- a/js/src/vm/RegExpStatics.cpp
+++ b/js/src/vm/RegExpStatics.cpp
@@ -49,20 +49,20 @@ using namespace js;
 /*
  * RegExpStatics allocates memory -- in order to keep the statics stored
  * per-global and not leak, we create a js::Class to wrap the C++ instance and
  * provide an appropriate finalizer. We store an instance of that js::Class in
  * a global reserved slot.
  */
 
 static void
-resc_finalize(JSContext *cx, JSObject *obj)
+resc_finalize(FreeOp *fop, JSObject *obj)
 {
     RegExpStatics *res = static_cast<RegExpStatics *>(obj->getPrivate());
-    cx->delete_(res);
+    fop->delete_(res);
 }
 
 static void
 resc_trace(JSTracer *trc, JSObject *obj)
 {
     void *pdata = obj->getPrivate();
     JS_ASSERT(pdata);
     RegExpStatics *res = static_cast<RegExpStatics *>(pdata);
--- a/js/src/vm/String-inl.h
+++ b/js/src/vm/String-inl.h
@@ -383,64 +383,58 @@ js::StaticStrings::lookup(const jschar *
         }
         return NULL;
     }
 
     return NULL;
 }
 
 JS_ALWAYS_INLINE void
-JSString::finalize(JSContext *cx, bool background)
+JSString::finalize(js::FreeOp *fop)
 {
     /* Shorts are in a different arena. */
     JS_ASSERT(!isShort());
 
     if (isFlat())
-        asFlat().finalize(cx->runtime);
+        asFlat().finalize(fop);
     else
         JS_ASSERT(isDependent() || isRope());
 }
 
 inline void
-JSFlatString::finalize(JSRuntime *rt)
+JSFlatString::finalize(js::FreeOp *fop)
 {
     JS_ASSERT(!isShort());
 
     /*
      * This check depends on the fact that 'chars' is only initialized to the
      * beginning of inlineStorage. E.g., this is not the case for short strings.
      */
     if (chars() != d.inlineStorage)
-        rt->free_(const_cast<jschar *>(chars()));
+        fop->free_(const_cast<jschar *>(chars()));
 }
 
 inline void
-JSShortString::finalize(JSContext *cx, bool background)
+JSShortString::finalize(js::FreeOp *fop)
 {
     JS_ASSERT(JSString::isShort());
 }
 
 inline void
-JSAtom::finalize(JSRuntime *rt)
+JSAtom::finalize(js::FreeOp *fop)
 {
     JS_ASSERT(JSString::isAtom());
     if (getAllocKind() == js::gc::FINALIZE_STRING)
-        JSFlatString::finalize(rt);
+        JSFlatString::finalize(fop);
     else
         JS_ASSERT(getAllocKind() == js::gc::FINALIZE_SHORT_STRING);
 }
 
 inline void
-JSExternalString::finalize(JSContext *cx, bool background)
-{
-    finalize();
-}
-
-inline void
-JSExternalString::finalize()
+JSExternalString::finalize(js::FreeOp *fop)
 {
     const JSStringFinalizer *fin = externalFinalizer();
     fin->finalize(fin, const_cast<jschar *>(chars()));
 }
 
 namespace js {
 
 static JS_ALWAYS_INLINE JSFixedString *
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -386,17 +386,17 @@ class JSString : public js::gc::Cell
     JS_ALWAYS_INLINE
     JSAtom &asAtom() const {
         JS_ASSERT(isAtom());
         return *(JSAtom *)this;
     }
 
     /* Only called by the GC for strings with the FINALIZE_STRING kind. */
 
-    inline void finalize(JSContext *cx, bool background);
+    inline void finalize(js::FreeOp *fop);
 
     /* Gets the number of bytes that the chars take on the heap. */
 
     size_t sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf);
 
     /* Offsets for direct field from jit code. */
 
     static size_t offsetOfLengthAndFlags() {
@@ -524,19 +524,17 @@ class JSFlatString : public JSLinearStri
 
     /*
      * Returns a property name represented by this string, or null on failure.
      * You must verify that this is not an index per isIndex before calling
      * this method.
      */
     inline js::PropertyName *toPropertyName(JSContext *cx);
 
-    /* Only called by the GC for strings with the FINALIZE_STRING kind. */
-
-    inline void finalize(JSRuntime *rt);
+    inline void finalize(js::FreeOp *fop);
 };
 
 JS_STATIC_ASSERT(sizeof(JSFlatString) == sizeof(JSString));
 
 class JSExtensibleString : public JSFlatString
 {
     /* Vacuous and therefore unimplemented. */
     bool isExtensible() const MOZ_DELETE;
@@ -621,17 +619,17 @@ class JSShortString : public JSInlineStr
                                            -1 /* null terminator */;
 
     static bool lengthFits(size_t length) {
         return length <= MAX_SHORT_LENGTH;
     }
 
     /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
 
-    JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background);
+    JS_ALWAYS_INLINE void finalize(js::FreeOp *fop);
 };
 
 JS_STATIC_ASSERT(sizeof(JSShortString) == 2 * sizeof(JSString));
 
 class JSExternalString : public JSFixedString
 {
     void init(const jschar *chars, size_t length, const JSStringFinalizer *fin);
 
@@ -645,33 +643,32 @@ class JSExternalString : public JSFixedS
 
     const JSStringFinalizer *externalFinalizer() const {
         JS_ASSERT(JSString::isExternal());
         return d.s.u2.externalFinalizer;
     }
 
     /* Only called by the GC for strings with the FINALIZE_EXTERNAL_STRING kind. */
 
-    inline void finalize(JSContext *cx, bool background);
-    inline void finalize();
+    inline void finalize(js::FreeOp *fop);
 };
 
 JS_STATIC_ASSERT(sizeof(JSExternalString) == sizeof(JSString));
 
 class JSAtom : public JSFixedString
 {
     /* Vacuous and therefore unimplemented. */
     bool isAtom() const MOZ_DELETE;
     JSAtom &asAtom() const MOZ_DELETE;
 
   public:
     /* Returns the PropertyName for this.  isIndex() must be false. */
     inline js::PropertyName *asPropertyName();
 
-    inline void finalize(JSRuntime *rt);
+    inline void finalize(js::FreeOp *fop);
 
 #ifdef DEBUG
     void dump();
 #endif
 };
 
 JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString));
 
--- a/js/xpconnect/idl/nsIXPCScriptable.idl
+++ b/js/xpconnect/idl/nsIXPCScriptable.idl
@@ -156,17 +156,17 @@ interface nsIXPCScriptable : nsISupports
                       in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                       in PRUint32 flags, out JSObjectPtr objp);
 
     boolean convert(in nsIXPConnectWrappedNative wrapper,
                    in JSContextPtr cx, in JSObjectPtr obj,
                    in PRUint32 type, in JSValPtr vp);
 
     void   finalize(in nsIXPConnectWrappedNative wrapper,
-                    in JSContextPtr cx, in JSObjectPtr obj);
+                    in JSFreeOpPtr fop, in JSObjectPtr obj);
 
     boolean checkAccess(in nsIXPConnectWrappedNative wrapper,
                        in JSContextPtr cx, in JSObjectPtr obj, in jsid id,
                        in PRUint32 mode, in JSValPtr vp);
 
     boolean call(in nsIXPConnectWrappedNative wrapper,
                 in JSContextPtr cx, in JSObjectPtr obj,
                 in PRUint32 argc, in JSValPtr argv, in JSValPtr vp);
--- a/js/xpconnect/idl/nsIXPConnect.idl
+++ b/js/xpconnect/idl/nsIXPConnect.idl
@@ -62,16 +62,17 @@ class nsWrapperCache;
 %}
 
 /***************************************************************************/
 
 // NB: jsval and jsid are declared in nsIVariant.idl
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSClassPtr(JSClass);
+[ptr] native JSFreeOpPtr(JSFreeOp);
 [ptr] native JSObjectPtr(JSObject);
 [ptr] native JSValPtr(jsval);
 [ptr] native JSValConstPtr(const jsval);
       native JSPropertyOp(JSPropertyOp);
       native JSEqualityOp(JSEqualityOp);
 [ptr] native JSScriptPtr(JSScript);
 [ptr] native voidPtrPtr(void*);
 [ptr] native nsScriptObjectTracerPtr(nsScriptObjectTracer);
--- a/js/xpconnect/public/xpc_map_end.h
+++ b/js/xpconnect/public/xpc_map_end.h
@@ -176,17 +176,17 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::NewReso
 #endif
 
 #ifndef XPC_MAP_WANT_CONVERT
 NS_IMETHODIMP XPC_MAP_CLASSNAME::Convert(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, PRUint32 type, jsval * vp, bool *_retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #ifndef XPC_MAP_WANT_FINALIZE
-NS_IMETHODIMP XPC_MAP_CLASSNAME::Finalize(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj)
+NS_IMETHODIMP XPC_MAP_CLASSNAME::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp * fop, JSObject * obj)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
 #ifndef XPC_MAP_WANT_CHECKACCESS
 NS_IMETHODIMP XPC_MAP_CLASSNAME::CheckAccess(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, jsid id, PRUint32 mode, jsval * vp, bool *_retval)
     {NS_ERROR("never called"); return NS_ERROR_NOT_IMPLEMENTED;}
 #endif
 
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2883,17 +2883,17 @@ sandbox_enumerate(JSContext *cx, JSObjec
 static JSBool
 sandbox_resolve(JSContext *cx, JSObject *obj, jsid id)
 {
     JSBool resolved;
     return JS_ResolveStandardClass(cx, obj, id, &resolved);
 }
 
 static void
-sandbox_finalize(JSContext *cx, JSObject *obj)
+sandbox_finalize(JSFreeOp *fop, JSObject *obj)
 {
     nsIScriptObjectPrincipal *sop =
         (nsIScriptObjectPrincipal *)xpc_GetJSPrivate(obj);
     NS_IF_RELEASE(sop);
     DestroyProtoOrIfaceCache(obj);
 }
 
 static JSBool
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -698,17 +698,17 @@ XPCJSRuntime::GCCallback(JSRuntime *rt, 
     }
 
     nsTArray<JSGCCallback> callbacks(self->extraGCCallbacks);
     for (PRUint32 i = 0; i < callbacks.Length(); ++i)
         callbacks[i](rt, status);
 }
 
 /* static */ void
-XPCJSRuntime::FinalizeCallback(JSContext *cx, JSFinalizeStatus status)
+XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status)
 {
     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
     if (!self)
         return;
 
     switch (status) {
         case JSFINALIZE_START:
         {
@@ -729,17 +729,17 @@ XPCJSRuntime::FinalizeCallback(JSContext
             // refcount of these wrappers.
             // We add them to the array now and Release the array members
             // later to avoid the posibility of doing any JS GCThing
             // allocations during the gc cycle.
             self->mWrappedJSMap->
                 Enumerate(WrappedJSDyingJSObjectFinder, dyingWrappedJSArray);
 
             // Find dying scopes.
-            XPCWrappedNativeScope::FinishedMarkPhaseOfGC(cx, self);
+            XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self);
 
             // Sweep compartments.
             self->GetCompartmentMap().EnumerateRead((XPCCompartmentMap::EnumReadFunction)
                                                     SweepCompartment, nsnull);
 
             self->mDoingFinalization = true;
             break;
         }
@@ -845,17 +845,17 @@ XPCJSRuntime::FinalizeCallback(JSContext
             printf("XPCNativeSets:        before: %d  collected: %d  remaining: %d\n",
                    setsBefore, setsBefore - setsAfter, setsAfter);
             printf("XPCNativeInterfaces:  before: %d  collected: %d  remaining: %d\n",
                    ifacesBefore, ifacesBefore - ifacesAfter, ifacesAfter);
             printf("--------------------------------------------------------------\n");
 #endif
 
             // Sweep scopes needing cleanup
-            XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(cx);
+            XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC();
 
             // Now we are going to recycle any unused WrappedNativeTearoffs.
             // We do this by iterating all the live callcontexts (on all
             // threads!) and marking the tearoffs in use. And then we
             // iterate over all the WrappedNative wrappers and sweep their
             // tearoffs.
             //
             // This allows us to perhaps minimize the growth of the
--- a/js/xpconnect/src/XPCQuickStubs.cpp
+++ b/js/xpconnect/src/XPCQuickStubs.cpp
@@ -160,17 +160,17 @@ PropertyOpForwarder(JSContext *cx, unsig
     jsid id;
     if (!JS_ValueToId(cx, argval, &id))
         return false;
     JS_SET_RVAL(cx, vp, argval);
     return ApplyPropertyOp<Op>(cx, *popp, obj, id, vp);
 }
 
 static void
-PointerFinalize(JSContext *cx, JSObject *obj)
+PointerFinalize(JSFreeOp *fop, JSObject *obj)
 {
     JSPropertyOp *popp = static_cast<JSPropertyOp *>(JS_GetPrivate(obj));
     delete popp;
 }
 
 static JSClass
 PointerHolderClass = {
     "Pointer", JSCLASS_HAS_PRIVATE,
--- a/js/xpconnect/src/XPCThreadContext.cpp
+++ b/js/xpconnect/src/XPCThreadContext.cpp
@@ -162,17 +162,17 @@ XPCJSContextStack::DEBUG_StackHasJSConte
 static JSBool
 SafeGlobalResolve(JSContext *cx, JSObject *obj, jsid id)
 {
     JSBool resolved;
     return JS_ResolveStandardClass(cx, obj, id, &resolved);
 }
 
 static void
-SafeFinalize(JSContext* cx, JSObject* obj)
+SafeFinalize(JSFreeOp *fop, JSObject* obj)
 {
     nsIScriptObjectPrincipal* sop =
         static_cast<nsIScriptObjectPrincipal*>(xpc_GetJSPrivate(obj));
     NS_IF_RELEASE(sop);
     DestroyProtoOrIfaceCache(obj);
 }
 
 static JSClass global_class = {
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -611,17 +611,17 @@ XPC_WN_Shared_Enumerate(JSContext *cx, J
 
 /***************************************************************************/
 
 #ifdef DEBUG_slimwrappers
 static PRUint32 sFinalizedSlimWrappers;
 #endif
 
 static void
-XPC_WN_NoHelper_Finalize(JSContext *cx, JSObject *obj)
+XPC_WN_NoHelper_Finalize(js::FreeOp *fop, JSObject *obj)
 {
     js::Class* clazz = js::GetObjectClass(obj);
     if (clazz->flags & JSCLASS_DOM_GLOBAL) {
         mozilla::dom::bindings::DestroyProtoOrIfaceCache(obj);
     }
     nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
     if (!p)
         return;
@@ -1041,17 +1041,17 @@ XPC_WN_Helper_HasInstance(JSContext *cx,
     bool retval2;
     PRE_HELPER_STUB_NO_SLIM
     HasInstance(wrapper, cx, obj, *valp, &retval2, &retval);
     *bp = retval2;
     POST_HELPER_STUB
 }
 
 static void
-XPC_WN_Helper_Finalize(JSContext *cx, JSObject *obj)
+XPC_WN_Helper_Finalize(js::FreeOp *fop, JSObject *obj)
 {
     js::Class* clazz = js::GetObjectClass(obj);
     if (clazz->flags & JSCLASS_DOM_GLOBAL) {
         mozilla::dom::bindings::DestroyProtoOrIfaceCache(obj);
     }
     nsISupports* p = static_cast<nsISupports*>(xpc_GetJSPrivate(obj));
     if (IS_SLIM_WRAPPER(obj)) {
         SLIM_LOG(("----- %i finalized slim wrapper (%p, %p)\n",
@@ -1062,17 +1062,18 @@ XPC_WN_Helper_Finalize(JSContext *cx, JS
         cache->ClearWrapper();
         NS_RELEASE(p);
         return;
     }
 
     XPCWrappedNative* wrapper = (XPCWrappedNative*)p;
     if (!wrapper)
         return;
-    wrapper->GetScriptableCallback()->Finalize(wrapper, cx, obj);
+
+    wrapper->GetScriptableCallback()->Finalize(wrapper, js::CastToJSFreeOp(fop), obj);
     wrapper->FlatJSObjectFinalized();
 }
 
 static JSBool
 XPC_WN_Helper_NewResolve(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
                          JSObject **objp)
 {
     nsresult rv = NS_OK;
@@ -1635,22 +1636,22 @@ XPC_WN_Shared_Proto_Enumerate(JSContext 
                 return false;
         }
     }
 
     return true;
 }
 
 static void
-XPC_WN_Shared_Proto_Finalize(JSContext *cx, JSObject *obj)
+XPC_WN_Shared_Proto_Finalize(js::FreeOp *fop, JSObject *obj)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
     if (p)
-        p->JSProtoObjectFinalized(cx, obj);
+        p->JSProtoObjectFinalized(fop, obj);
 }
 
 static void
 XPC_WN_Shared_Proto_Trace(JSTracer *trc, JSObject *obj)
 {
     // This can be null if xpc shutdown has already happened
     XPCWrappedNativeProto* p =
         (XPCWrappedNativeProto*) xpc_GetJSPrivate(obj);
@@ -1894,17 +1895,17 @@ XPC_WN_TearOff_Resolve(JSContext *cx, JS
                                  wrapper->GetScope(),
                                  true, nsnull, nsnull, nsnull,
                                  JSPROP_READONLY |
                                  JSPROP_PERMANENT |
                                  JSPROP_ENUMERATE, nsnull);
 }
 
 static void
-XPC_WN_TearOff_Finalize(JSContext *cx, JSObject *obj)
+XPC_WN_TearOff_Finalize(js::FreeOp *fop, JSObject *obj)
 {
     XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
         xpc_GetJSPrivate(obj);
     if (!p)
         return;
     p->JSObjectFinalized();
 }
 
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -158,32 +158,32 @@ XPCWrappedNativeProto::CallPostCreatePro
         XPCThrower::Throw(rv, ccx);
         return false;
     }
 
     return true;
 }
 
 void
-XPCWrappedNativeProto::JSProtoObjectFinalized(JSContext *cx, JSObject *obj)
+XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj)
 {
     NS_ASSERTION(obj == mJSProtoObject, "huh?");
 
     // Map locking is not necessary since we are running gc.
 
     // Only remove this proto from the map if it is the one in the map.
     ClassInfo2WrappedNativeProtoMap* map =
         GetScope()->GetWrappedNativeProtoMap(ClassIsMainThreadOnly());
     if (map->Find(mClassInfo) == this)
         map->Remove(mClassInfo);
 
     GetRuntime()->GetDetachedWrappedNativeProtoMap()->Remove(this);
     GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this);
 
-    mJSProtoObject.finalize(cx);
+    mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime());
 }
 
 void
 XPCWrappedNativeProto::SystemIsBeingShutDown()
 {
     // Note that the instance might receive this call multiple times
     // as we walk to here from various places.
 
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -391,17 +391,17 @@ XPCWrappedNativeScope::SuspectAllWrapper
 
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
         cur->mWrappedNativeMap->Enumerate(WrappedNativeSuspecter, &cb);
     }
 }
 
 // static
 void
-XPCWrappedNativeScope::FinishedMarkPhaseOfGC(JSContext* cx, XPCJSRuntime* rt)
+XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt)
 {
     // FIXME The lock may not be necessary since we are inside JSGC_MARK_END
     // callback and GX serializes access to JS runtime. See bug 380139.
     XPCAutoLock lock(rt->GetMapLock());
 
     // We are in JSGC_MARK_END and JSGC_FINALIZE_END must always follow it
     // calling FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
     // KillDyingScopes.
@@ -411,47 +411,47 @@ XPCWrappedNativeScope::FinishedMarkPhase
     XPCWrappedNativeScope* prev = nsnull;
     XPCWrappedNativeScope* cur = gScopes;
 
     while (cur) {
         XPCWrappedNativeScope* next = cur->mNext;
 
         if (cur->mGlobalJSObject &&
             JS_IsAboutToBeFinalized(cur->mGlobalJSObject)) {
-            cur->mGlobalJSObject.finalize(cx);
+            cur->mGlobalJSObject.finalize(fop->runtime());
             cur->mScriptObjectPrincipal = nsnull;
             if (cur->GetCachedDOMPrototypes().IsInitialized())
                  cur->GetCachedDOMPrototypes().Clear();
             // Move this scope from the live list to the dying list.
             if (prev)
                 prev->mNext = next;
             else
                 gScopes = next;
             cur->mNext = gDyingScopes;
             gDyingScopes = cur;
             cur = nsnull;
         } else {
             if (cur->mPrototypeJSObject &&
                 JS_IsAboutToBeFinalized(cur->mPrototypeJSObject)) {
-                cur->mPrototypeJSObject.finalize(cx);
+                cur->mPrototypeJSObject.finalize(fop->runtime());
             }
             if (cur->mPrototypeNoHelper &&
                 JS_IsAboutToBeFinalized(cur->mPrototypeNoHelper)) {
                 cur->mPrototypeNoHelper = nsnull;
             }
         }
         if (cur)
             prev = cur;
         cur = next;
     }
 }
 
 // static
 void
-XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(JSContext* cx)
+XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC()
 {
     XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
 
     // FIXME The lock may not be necessary since we are inside
     // JSGC_FINALIZE_END callback and at this point GC still serializes access
     // to JS runtime. See bug 380139.
     XPCAutoLock lock(rt->GetMapLock());
     KillDyingScopes();
--- a/js/xpconnect/src/dombindings.cpp
+++ b/js/xpconnect/src/dombindings.cpp
@@ -1226,17 +1226,17 @@ ListBase<LC>::obj_toString(JSContext *cx
     JSString *str = JS_NewUCString(cx, chars, nchars);
     if (!str)
         JS_free(cx, chars);
     return str;
 }
 
 template<class LC>
 void
-ListBase<LC>::finalize(JSContext *cx, JSObject *proxy)
+ListBase<LC>::finalize(JSFreeOp *fop, JSObject *proxy)
 {
     ListType *list = getListObject(proxy);
     nsWrapperCache *cache;
     CallQueryInterface(list, &cache);
     if (cache) {
         cache->ClearWrapper();
     }
     NS_RELEASE(list);
--- a/js/xpconnect/src/dombindings.h
+++ b/js/xpconnect/src/dombindings.h
@@ -223,17 +223,17 @@ public:
     bool set(JSContext *cx, JSObject *proxy, JSObject *receiver, jsid id, bool strict,
              JS::Value *vp);
     bool keys(JSContext *cx, JSObject *proxy, JS::AutoIdVector &props);
     bool iterate(JSContext *cx, JSObject *proxy, unsigned flags, JS::Value *vp);
 
     /* Spidermonkey extensions. */
     bool hasInstance(JSContext *cx, JSObject *proxy, const JS::Value *vp, bool *bp);
     JSString *obj_toString(JSContext *cx, JSObject *proxy);
-    void finalize(JSContext *cx, JSObject *proxy);
+    void finalize(JSFreeOp *fop, JSObject *proxy);
 
     static bool proxyHandlerIsList(js::ProxyHandler *handler) {
         return handler == &instance;
     }
     static bool objIsList(JSObject *obj) {
         return js::IsProxy(obj) && proxyHandlerIsList(js::GetProxyHandler(obj));
     }
     static inline bool instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee);
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -742,17 +742,17 @@ public:
 
     static void TraceBlackJS(JSTracer* trc, void* data);
     static void TraceGrayJS(JSTracer* trc, void* data);
     void TraceXPConnectRoots(JSTracer *trc);
     void AddXPConnectRoots(nsCycleCollectionTraversalCallback& cb);
     void UnmarkSkippableJSHolders();
 
     static void GCCallback(JSRuntime *rt, JSGCStatus status);
-    static void FinalizeCallback(JSContext *cx, JSFinalizeStatus status);
+    static void FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status);
 
     inline void AddVariantRoot(XPCTraceableVariant* variant);
     inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
     inline void AddObjectHolderRoot(XPCJSObjectHolder* holder);
 
     nsresult AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
     nsresult RemoveJSHolder(void* aHolder);
 
@@ -1557,20 +1557,20 @@ public:
 
     static void
     TraceJS(JSTracer* trc, XPCJSRuntime* rt);
 
     static void
     SuspectAllWrappers(XPCJSRuntime* rt, nsCycleCollectionTraversalCallback &cb);
 
     static void
-    FinishedMarkPhaseOfGC(JSContext* cx, XPCJSRuntime* rt);
+    StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt);
 
     static void
-    FinishedFinalizationPhaseOfGC(JSContext* cx);
+    FinishedFinalizationPhaseOfGC();
 
     static void
     MarkAllWrappedNativesAndProtos();
 
     static nsresult
     ClearAllWrappedNativeSecurityPolicies(XPCCallContext& ccx);
 
 #ifdef DEBUG
@@ -2303,17 +2303,17 @@ public:
 
     XPCLock* GetLock() const
         {return ClassIsThreadSafe() ? GetRuntime()->GetMapLock() : nsnull;}
 
     void SetScriptableInfo(XPCNativeScriptableInfo* si)
         {NS_ASSERTION(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
 
     bool CallPostCreatePrototype(XPCCallContext& ccx);
-    void JSProtoObjectFinalized(JSContext *cx, JSObject *obj);
+    void JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj);
 
     void SystemIsBeingShutDown();
 
     void DebugDump(PRInt16 depth);
 
     void TraceJS(JSTracer* trc)
     {
         if (mJSProtoObject) {