Relanding part of the fix for bug 372960 (Make XPConnect traverse more JS edges). r=brendan, sr=jst.
authorpeterv@propagandism.org
Fri, 23 Mar 2007 06:21:22 -0700
changeset 33 137cb083e239ee0dae86dfe9c89edcb1f8521c77
parent 32 a3a81775ad8909c3af7352a4e2e8b3a30265345f
child 34 677d356a05fc756e0a965875952ea2e579050bbe
push idunknown
push userunknown
push dateunknown
reviewersbrendan, jst
bugs372960
milestone1.9a3pre
Relanding part of the fix for bug 372960 (Make XPConnect traverse more JS edges). r=brendan, sr=jst.
js/src/xpconnect/src/nsXPConnect.cpp
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -41,17 +41,20 @@
  * ***** END LICENSE BLOCK ***** */
 
 /* High level class and public functions implementation. */
 
 #include "xpcprivate.h"
 #include "XPCNativeWrapper.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
+#include "jsatom.h"
+#include "jsfun.h"
 #include "jsobj.h"
+#include "jsscript.h"
 
 NS_IMPL_THREADSAFE_ISUPPORTS2(nsXPConnect,nsIXPConnect,nsISupportsWeakReference)
 
 nsXPConnect* nsXPConnect::gSelf = nsnull;
 JSBool       nsXPConnect::gOnceAliveNowDead = JS_FALSE;
 
 const char XPC_CONTEXT_STACK_CONTRACTID[] = "@mozilla.org/js/xpc/ContextStack;1";
 const char XPC_RUNTIME_CONTRACTID[]       = "@mozilla.org/js/xpc/RuntimeService;1";
@@ -572,16 +575,32 @@ nsXPConnect::Unroot(const nsDeque &nodes
             continue;
         JSObject *obj = NS_STATIC_CAST(JSObject*, p);
         if (!JS_UnlockGCThing(cx, obj))
             return NS_ERROR_FAILURE;
     }
     return NS_OK;
 }
 
+static void
+TraverseJSScript(JSScript* script, nsCycleCollectionTraversalCallback& cb)
+{
+    JSAtomMap* map = &script->atomMap;
+    uintN i, length = map->length;
+    JSAtom** vector = map->vector;
+
+    for(i = 0; i < length; i++)
+    {
+        JSAtom* atom = vector[i];
+        if(ATOM_IS_OBJECT(atom))
+            cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
+                               ATOM_TO_OBJECT(atom));
+    }
+}
+
 nsresult 
 nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
 {
     XPCCallContext cx(NATIVE_CALLER);
 
     PRUint32 refcount = 0;
     if (!mObjRefcounts->Get(p, refcount))
         return NS_OK;
@@ -589,42 +608,101 @@ nsXPConnect::Traverse(void *p, nsCycleCo
     JSObject *obj = NS_STATIC_CAST(JSObject*, p);
     JSClass* clazz = OBJ_GET_CLASS(cx, obj);
 
 #ifdef DEBUG
     char name[72];
     if(XPCNativeWrapper::IsNativeWrapperClass(clazz))
     {
         XPCWrappedNative* wn = XPCNativeWrapper::GetWrappedNative(cx, obj);
-        XPCNativeScriptableInfo* si = wn ? wn->GetScriptableInfo() : nsnull;
-        if(si)
-            JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)",
-                        si->GetJSClass()->name);
+        if(wn)
+        {
+            XPCNativeScriptableInfo* si = wn->GetScriptableInfo();
+            if(si)
+            {
+                JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)",
+                            si->GetJSClass()->name);
+            }
+            else
+            {
+                nsIClassInfo* ci = wn->GetClassInfo();
+                char* className = nsnull;
+                if(ci)
+                    ci->GetClassDescription(&className);
+                if(className)
+                {
+                    JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)",
+                                className);
+                    PR_Free(className);
+                }
+                else
+                {
+                    XPCNativeSet* set = wn->GetSet();
+                    XPCNativeInterface** array = set->GetInterfaceArray();
+                    PRUint16 count = set->GetInterfaceCount();
+
+                    if(count > 0)
+                        JS_snprintf(name, sizeof(name), "XPCNativeWrapper (%s)",
+                                    array[0]->GetNameString());
+                    else
+                        JS_snprintf(name, sizeof(name), "XPCNativeWrapper");
+                }
+            }
+        }
         else
+        {
             JS_snprintf(name, sizeof(name), "XPCNativeWrapper");
+        }
     }
-    else if(obj)
+    else
     {
         XPCNativeScriptableInfo* si = nsnull;
         if(IS_PROTO_CLASS(clazz))
         {
             XPCWrappedNativeProto* p =
                 (XPCWrappedNativeProto*) JS_GetPrivate(cx, obj);
             si = p->GetScriptableInfo();
         }
         if(si)
+        {
             JS_snprintf(name, sizeof(name), "JS Object (%s - %s)", clazz->name,
                         si->GetJSClass()->name);
+        }
+        else if(clazz == &js_ScriptClass)
+        {
+            JSScript* script = (JSScript*) JS_GetPrivate(cx, obj);
+            if(script->filename)
+            {
+                JS_snprintf(name, sizeof(name), "JS Object (Script - %s)",
+                            script->filename);
+            }
+            else
+            {
+                JS_snprintf(name, sizeof(name), "JS Object (Script)");
+            }
+        }
+        else if(clazz == &js_FunctionClass)
+        {
+            JSFunction* fun = (JSFunction*) JS_GetPrivate(cx, obj);
+            if(fun->atom)
+            {
+                JS_snprintf(name, sizeof(name), "JS Object (Function - %s)",
+                            js_AtomToPrintableString(cx, fun->atom));
+            }
+            else
+            {
+                JS_snprintf(name, sizeof(name), "JS Object (Function)");
+            }
+        }
         else
+        {
             JS_snprintf(name, sizeof(name), "JS Object (%s)", clazz->name);
+        }
     }
-    else
-    {
-        JS_snprintf(name, sizeof(name), "JS Object");
-    }
+
     cb.DescribeNode(refcount, sizeof(JSObject), name);
 #else
     cb.DescribeNode(refcount, sizeof(JSObject), "JS Object");
 #endif
     if (!p)
         return NS_OK;    
 
     if(XPCNativeWrapper::IsNativeWrapperClass(clazz))
@@ -653,30 +731,70 @@ nsXPConnect::Traverse(void *p, nsCycleCo
             // Mark scope.
             p->GetScope()->Traverse(cb);
     }
     else if(clazz->flags & JSCLASS_HAS_PRIVATE &&
             clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
     {
         cb.NoteXPCOMChild(NS_STATIC_CAST(nsISupports*, JS_GetPrivate(cx, obj)));
     }
+    else if(clazz == &js_ScriptClass)
+    {
+        JSScript* script = (JSScript*) JS_GetPrivate(cx, obj);
+        TraverseJSScript(script, cb);
+    }
+    else if(clazz == &js_FunctionClass)
+    {
+        JSFunction* fun = (JSFunction*) JS_GetPrivate(cx, obj);
+        if (fun) {
+            JSAtom* atom = fun->atom;
+            if(atom && ATOM_IS_OBJECT(atom))
+                cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
+                                   ATOM_TO_OBJECT(atom));
+            JSScript* script = FUN_SCRIPT(fun);
+            if (script)
+                TraverseJSScript(script, cb);
+        }
+    }
 
     cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
                        OBJ_GET_PARENT(cx, obj));
     cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
                        OBJ_GET_PROTO(cx, obj));
 
     for(uint32 i = JSSLOT_START(clazz); i < STOBJ_NSLOTS(obj); ++i) 
     {
         jsval val = STOBJ_GET_SLOT(obj, i);
         if (JSVAL_IS_OBJECT(val)) 
             cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
                                JSVAL_TO_OBJECT(val));
     }
 
+    JSScope* scope;
+    if(OBJ_IS_NATIVE(obj) && (scope = OBJ_SCOPE(obj)))
+    {
+        JSScopeProperty* sprop;
+        for(sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent)
+        {
+            if(SCOPE_HAD_MIDDLE_DELETE(scope) &&
+               !SCOPE_HAS_PROPERTY(scope, sprop))
+                continue;
+            jsid id = sprop->id;
+            if(JSID_IS_OBJECT(id))
+                cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
+                                   JSID_TO_OBJECT(id));
+            if(sprop->attrs & JSPROP_GETTER)
+                cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
+                                   JSVAL_TO_GCTHING((jsval)sprop->getter));
+            if(sprop->attrs & JSPROP_SETTER)
+                cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
+                                   JSVAL_TO_GCTHING((jsval)sprop->setter));
+        }
+    }
+
 #ifndef XPCONNECT_STANDALONE
     if(clazz->flags & JSCLASS_IS_GLOBAL)
     {
         nsISupports *principal = nsnull;
         mObjRefcounts->mScopes.Get(obj, &principal);
         cb.NoteXPCOMChild(principal);
     }
 #endif