Bug 716033 - Move write barriers from jsapi.h to jsfriendapi.h (r=luke,bholley)
authorBill McCloskey <wmccloskey@mozilla.com>
Mon, 23 Jan 2012 16:43:12 -0800
changeset 86387 b1d7e7ac1b7781dab432ac17e8a15fedbc28679e
parent 86386 859a8f4d3536aa6e746ca7c124080adbd94b1761
child 86388 a6a01e576efc4a0e6cbdfa9b58644e287a8ffa79
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, bholley
bugs716033
milestone12.0a1
Bug 716033 - Move write barriers from jsapi.h to jsfriendapi.h (r=luke,bholley)
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/xpcprivate.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6709,89 +6709,16 @@ JS_ScheduleGC(JSContext *cx, uint32_t co
 JS_FRIEND_API(void *)
 js_GetCompartmentPrivate(JSCompartment *compartment)
 {
     return compartment->data;
 }
 
 /************************************************************************/
 
-JS_PUBLIC_API(void)
-JS_RegisterReference(void **ref)
-{
-}
-
-JS_PUBLIC_API(void)
-JS_ModifyReference(void **ref, void *newval)
-{
-    // XPConnect uses the lower bits of its JSObject refs for evil purposes,
-    // so we need to fix this.
-    void *thing = *ref;
-    *ref = newval;
-    thing = (void *)((uintptr_t)thing & ~7);
-    if (!thing)
-        return;
-    JS_ASSERT(!static_cast<gc::Cell *>(thing)->compartment()->rt->gcRunning);
-    uint32_t kind = GetGCThingTraceKind(thing);
-    if (kind == JSTRACE_OBJECT)
-        JSObject::writeBarrierPre((JSObject *) thing);
-    else if (kind == JSTRACE_STRING)
-        JSString::writeBarrierPre((JSString *) thing);
-    else
-        JS_NOT_REACHED("invalid trace kind");
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterReference(void **ref)
-{
-    // For now we just want to trigger a write barrier.
-    JS_ModifyReference(ref, NULL);
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterReferenceRT(JSRuntime *rt, void **ref)
-{
-    // For now we just want to trigger a write barrier.
-    if (!rt->gcRunning)
-        JS_ModifyReference(ref, NULL);
-}
-
-JS_PUBLIC_API(void)
-JS_RegisterValue(jsval *val)
-{
-}
-
-JS_PUBLIC_API(void)
-JS_ModifyValue(jsval *val, jsval newval)
-{
-    HeapValue::writeBarrierPre(*val);
-    *val = newval;
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterValue(jsval *val)
-{
-    JS_ModifyValue(val, JSVAL_VOID);
-}
-
-JS_PUBLIC_API(void)
-JS_UnregisterValueRT(JSRuntime *rt, jsval *val)
-{
-    if (!rt->gcRunning)
-        JS_ModifyValue(val, JSVAL_VOID);
-}
-
-JS_PUBLIC_API(JSTracer *)
-JS_GetIncrementalGCTracer(JSRuntime *rt)
-{
-    return rt->gcIncrementalTracer;
-}
-
-/************************************************************************/
-
 #if !defined(STATIC_EXPORTABLE_JS_API) && !defined(STATIC_JS_API) && defined(XP_WIN)
 
 #include "jswin.h"
 
 /*
  * Initialization routine for the JS DLL.
  */
 BOOL WINAPI DllMain (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -3223,115 +3223,16 @@ JS_GetTraceEdgeName(JSTracer *trc, char 
  */
 extern JS_PUBLIC_API(JSBool)
 JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, JSGCTraceKind kind,
             void *thingToFind, size_t maxDepth, void *thingToIgnore);
 
 #endif
 
 /*
- * Write barrier API.
- *
- * This API is used to inform SpiderMonkey of pointers to JS GC things in the
- * malloc heap. There is no need to use this API unless incremental GC is
- * enabled. When they are, the requirements for using the API are as follows:
- *
- * All pointers to JS GC things from the malloc heap must be registered and
- * unregistered with the API functions below. This is *in addition* to the
- * normal rooting and tracing that must be done normally--these functions will
- * not take care of rooting for you.
- *
- * Besides registration, the JS_ModifyReference function must be called to
- * change the value of these references. You should not change them using
- * assignment.
- *
- * Only the RT versions of these functions (which take a JSRuntime argument)
- * should be called during GC. Without a JSRuntime, it is not possible to know
- * if the object being barriered has already been finalized.
- *
- * To avoid the headache of using these API functions, the JSBarrieredObjectPtr
- * C++ class is provided--simply replace your JSObject* with a
- * JSBarrieredObjectPtr. It will take care of calling the registration and
- * modification APIs.
- *
- * For more explanation, see the comment in gc/Barrier.h.
- */
-
-/* These functions are to be used for objects and strings. */
-extern JS_PUBLIC_API(void)
-JS_RegisterReference(void **ref);
-
-extern JS_PUBLIC_API(void)
-JS_ModifyReference(void **ref, void *newval);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterReference(void **ref);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterReferenceRT(JSRuntime *rt, void **ref);
-
-/* These functions are for values. */
-extern JS_PUBLIC_API(void)
-JS_RegisterValue(jsval *val);
-
-extern JS_PUBLIC_API(void)
-JS_ModifyValue(jsval *val, jsval newval);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterValue(jsval *val);
-
-extern JS_PUBLIC_API(void)
-JS_UnregisterValueRT(JSRuntime *rt, jsval *val);
-
-extern JS_PUBLIC_API(JSTracer *)
-JS_GetIncrementalGCTracer(JSRuntime *rt);
-
-#ifdef __cplusplus
-JS_END_EXTERN_C
-
-namespace JS {
-
-class HeapPtrObject
-{
-    JSObject *value;
-
-  public:
-    HeapPtrObject() : value(NULL) { JS_RegisterReference((void **) &value); }
-
-    HeapPtrObject(JSObject *obj) : value(obj) { JS_RegisterReference((void **) &value); }
-
-    /* Always call finalize before the destructor. */
-    ~HeapPtrObject() { JS_ASSERT(!value); }
-
-    void finalize(JSRuntime *rt) {
-        JS_UnregisterReferenceRT(rt, (void **) &value);
-        value = NULL;
-    }
-    void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); }
-
-    void init(JSObject *obj) { value = obj; }
-
-    JSObject *get() const { return value; }
-
-    HeapPtrObject &operator=(JSObject *obj) {
-        JS_ModifyReference((void **) &value, obj);
-        return *this;
-    }
-
-    JSObject &operator*() const { return *value; }
-    JSObject *operator->() const { return value; }
-    operator JSObject *() const { return value; }
-};
-
-} /* namespace JS */
-
-JS_BEGIN_EXTERN_C
-#endif
-
-/*
  * Garbage collector API.
  */
 extern JS_PUBLIC_API(void)
 JS_GC(JSContext *cx);
 
 extern JS_PUBLIC_API(void)
 JS_CompartmentGC(JSContext *cx, JSCompartment *comp);
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -464,16 +464,49 @@ js::DumpHeapComplete(JSContext *cx, FILE
 
     dtrc.visited.finish();
 }
 
 #endif
 
 namespace js {
 
+JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSRuntime *rt)
+{
+    return !!rt->gcIncrementalTracer && !rt->gcRunning;
+}
+
+JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSContext *cx)
+{
+    return IsIncrementalBarrierNeeded(cx->runtime);
+}
+
+extern JS_FRIEND_API(void)
+IncrementalReferenceBarrier(void *ptr)
+{
+    if (!ptr)
+        return;
+    JS_ASSERT(!static_cast<gc::Cell *>(ptr)->compartment()->rt->gcRunning);
+    uint32_t kind = gc::GetGCThingTraceKind(ptr);
+    if (kind == JSTRACE_OBJECT)
+        JSObject::writeBarrierPre((JSObject *) ptr);
+    else if (kind == JSTRACE_STRING)
+        JSString::writeBarrierPre((JSString *) ptr);
+    else
+        JS_NOT_REACHED("invalid trace kind");
+}
+
+extern JS_FRIEND_API(void)
+IncrementalValueBarrier(const Value &v)
+{
+    HeapValue::writeBarrierPre(v);
+}
+
 /* static */ void
 AutoLockGC::LockGC(JSRuntime *rt)
 {
     JS_ASSERT(rt);
     JS_LOCK_GC(rt);
 }
 
 /* static */ void
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -560,16 +560,66 @@ TriggerOperationCallbacksForActiveContex
 class SystemAllocPolicy;
 typedef Vector<JSCompartment*, 0, SystemAllocPolicy> CompartmentVector;
 extern JS_FRIEND_API(const CompartmentVector&)
 GetRuntimeCompartments(JSRuntime *rt);
 
 extern JS_FRIEND_API(size_t)
 SizeOfJSContext();
 
+extern JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSRuntime *rt);
+
+extern JS_FRIEND_API(bool)
+IsIncrementalBarrierNeeded(JSContext *cx);
+
+extern JS_FRIEND_API(void)
+IncrementalReferenceBarrier(void *ptr);
+
+extern JS_FRIEND_API(void)
+IncrementalValueBarrier(const Value &v);
+
+class ObjectPtr
+{
+    JSObject *value;
+
+  public:
+    ObjectPtr() : value(NULL) {}
+
+    ObjectPtr(JSObject *obj) : value(obj) {}
+
+    /* 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);
+    }
+
+    ObjectPtr &operator=(JSObject *obj) {
+        IncrementalReferenceBarrier(value);
+        value = obj;
+        return *this;
+    }
+
+    JSObject &operator*() const { return *value; }
+    JSObject *operator->() const { return value; }
+    operator JSObject *() const { return value; }
+};
+
 } /* namespace js */
 
 /*
  * If protoKey is not JSProto_Null, then clasp is ignored. If protoKey is
  * JSProto_Null, clasp must non-null.
  */
 extern JS_FRIEND_API(JSBool)
 js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -821,18 +821,16 @@ XPCWrappedNative::XPCWrappedNative(alrea
 #endif
 {
     mIdentity = aIdentity.get();
 
     NS_ASSERTION(mMaybeProto, "bad ctor param");
     NS_ASSERTION(mSet, "bad ctor param");
 
     DEBUG_TrackNewWrapper(this);
-
-    JS_RegisterReference((void **) &mWrapperWord);
 }
 
 // This ctor is used if this object will NOT have a proto.
 XPCWrappedNative::XPCWrappedNative(already_AddRefed<nsISupports> aIdentity,
                                    XPCWrappedNativeScope* aScope,
                                    XPCNativeSet* aSet)
 
     : mMaybeScope(TagScope(aScope)),
@@ -845,18 +843,16 @@ XPCWrappedNative::XPCWrappedNative(alrea
 #endif
 {
     mIdentity = aIdentity.get();
 
     NS_ASSERTION(aScope, "bad ctor param");
     NS_ASSERTION(aSet, "bad ctor param");
 
     DEBUG_TrackNewWrapper(this);
-
-    JS_RegisterReference((void **) &mWrapperWord);
 }
 
 XPCWrappedNative::~XPCWrappedNative()
 {
     DEBUG_TrackDeleteWrapper(this);
 
     Destroy();
 }
@@ -904,49 +900,49 @@ XPCWrappedNative::Destroy()
 
     /*
      * The only time GetRuntime() will be NULL is if Destroy is called a second
      * time on a wrapped native. Since we already unregistered the pointer the
      * first time, there's no need to unregister again. Unregistration is safe
      * the first time because mWrapperWord isn't used afterwards.
      */
     if (XPCJSRuntime *rt = GetRuntime()) {
-        JS_UnregisterReferenceRT(rt->GetJSRuntime(), (void **) &mWrapperWord);
+        if (js::IsIncrementalBarrierNeeded(rt->GetJSRuntime()))
+            js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
         mWrapperWord = WRAPPER_WORD_POISON;
     } else {
         MOZ_ASSERT(mWrapperWord == WRAPPER_WORD_POISON);
     }
 
     mMaybeScope = nsnull;
 }
 
 void
 XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si)
 {
     NS_ASSERTION(mScriptableInfo, "UpdateScriptableInfo expects an existing scriptable info");
 
     // Write barrier for incremental GC.
     JSRuntime* rt = GetRuntime()->GetJSRuntime();
-    if (JS_GetIncrementalGCTracer(rt))
+    if (js::IsIncrementalBarrierNeeded(rt))
         mScriptableInfo->Mark();
 
     mScriptableInfo = si;
 }
 
 void
 XPCWrappedNative::SetProto(XPCWrappedNativeProto* p)
 {
     NS_ASSERTION(!IsWrapperExpired(), "bad ptr!");
 
+    MOZ_ASSERT(HasProto());
+
     // Write barrier for incremental GC.
-    if (HasProto()) {
-        JSRuntime* rt = GetRuntime()->GetJSRuntime();
-        if (JS_GetIncrementalGCTracer(rt))
-            GetProto()->TraceJS(JS_GetIncrementalGCTracer(rt));
-    }
+    JSRuntime* rt = GetRuntime()->GetJSRuntime();
+    GetProto()->WriteBarrierPre(rt);
 
     mMaybeProto = p;
 }
 
 // This is factored out so that it can be called publicly
 // static
 void
 XPCWrappedNative::GatherProtoScriptableCreateInfo(nsIClassInfo* classInfo,
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1649,20 +1649,20 @@ private:
     ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
     ClassInfo2WrappedNativeProtoMap* mMainThreadWrappedNativeProtoMap;
     nsXPCComponents*                 mComponents;
     XPCWrappedNativeScope*           mNext;
     // The JS global object for this scope.  If non-null, this will be the
     // default parent for the XPCWrappedNatives that have us as the scope,
     // unless a PreCreate hook overrides it.  Note that this _may_ be null (see
     // constructor).
-    JS::HeapPtrObject                mGlobalJSObject;
+    js::ObjectPtr                    mGlobalJSObject;
 
     // Cached value of Object.prototype
-    JS::HeapPtrObject                mPrototypeJSObject;
+    js::ObjectPtr                    mPrototypeJSObject;
     // Prototype to use for wrappers with no helper.
     JSObject*                        mPrototypeNoHelper;
 
     XPCContext*                      mContext;
 
     // The script object principal instance corresponding to our current global
     // JS object.
     // XXXbz what happens if someone calls JS_SetPrivate on mGlobalJSObject.
@@ -2301,16 +2301,22 @@ public:
         if (mJSProtoObject) {
             JS_CALL_OBJECT_TRACER(trc, mJSProtoObject,
                                   "XPCWrappedNativeProto::mJSProtoObject");
         }
         if (mScriptableInfo && JS_IsGCMarkingTracer(trc))
             mScriptableInfo->Mark();
     }
 
+    void WriteBarrierPre(JSRuntime* rt)
+    {
+        if (js::IsIncrementalBarrierNeeded(rt) && mJSProtoObject)
+            mJSProtoObject.writeBarrierPre(rt);
+    }
+
     // NOP. This is just here to make the AutoMarkingPtr code compile.
     inline void AutoTrace(JSTracer* trc) {}
 
     // Yes, we *do* need to mark the mScriptableInfo in both cases.
     void Mark() const
         {mSet->Mark();
          if (mScriptableInfo) mScriptableInfo->Mark();}
 
@@ -2343,17 +2349,17 @@ private:
 private:
     bool
     InitedOffsets()
     {
         return mOffsets != UNKNOWN_OFFSETS;
     }
 
     XPCWrappedNativeScope*   mScope;
-    JS::HeapPtrObject        mJSProtoObject;
+    js::ObjectPtr            mJSProtoObject;
     nsCOMPtr<nsIClassInfo>   mClassInfo;
     PRUint32                 mClassInfoFlags;
     XPCNativeSet*            mSet;
     void*                    mSecurityInfo;
     XPCNativeScriptableInfo* mScriptableInfo;
     QITableEntry*            mOffsets;
 };
 
@@ -2732,18 +2738,19 @@ public:
             xpc_UnmarkGrayObject(wrapper);
             // Call this to unmark mFlatJSObject.
             GetFlatJSObject();
         }
         return wrapper;
     }
     void SetWrapper(JSObject *obj)
     {
+        js::IncrementalReferenceBarrier(GetWrapperPreserveColor());
         PRWord newval = PRWord(obj) | (mWrapperWord & FLAG_MASK);
-        JS_ModifyReference((void **)&mWrapperWord, (void *)newval);
+        mWrapperWord = newval;
     }
 
     void NoteTearoffs(nsCycleCollectionTraversalCallback& cb);
 
     QITableEntry* GetOffsets()
     {
         if (!HasProto() || !GetProto()->ClassIsDOMObject())
             return nsnull;