Back to index

lightning-sunbird  0.9+nobinonly
xpcjsruntime.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is Mozilla Communicator client code, released
00017  * March 31, 1998.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications Corporation.
00021  * Portions created by the Initial Developer are Copyright (C) 1998
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   John Bandhauer <jband@netscape.com> (original author)
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /* Per JSRuntime object */
00042 
00043 #include "xpcprivate.h"
00044 
00045 /***************************************************************************/
00046 
00047 const char* XPCJSRuntime::mStrings[] = {
00048     "constructor",          // IDX_CONSTRUCTOR
00049     "toString",             // IDX_TO_STRING
00050     "toSource",             // IDX_TO_SOURCE
00051     "lastResult",           // IDX_LAST_RESULT
00052     "returnCode",           // IDX_RETURN_CODE
00053     "value",                // IDX_VALUE
00054     "QueryInterface",       // IDX_QUERY_INTERFACE
00055     "Components",           // IDX_COMPONENTS
00056     "wrappedJSObject",      // IDX_WRAPPED_JSOBJECT
00057     "Object",               // IDX_OBJECT
00058     "Function",             // IDX_FUNCTION
00059     "prototype",            // IDX_PROTOTYPE
00060     "createInstance",       // IDX_CREATE_INSTANCE
00061     "item"                  // IDX_ITEM
00062 #ifdef XPC_IDISPATCH_SUPPORT
00063     , "GeckoActiveXObject"  // IDX_ACTIVEX_OBJECT
00064     , "COMObject"           // IDX_COMOBJECT
00065     , "supports"            // IDX_ACTIVEX_SUPPORTS
00066 #endif
00067 };
00068 
00069 /***************************************************************************/
00070 
00071 // ContextCallback calls are chained
00072 static JSContextCallback gOldJSContextCallback;
00073 
00074 // GCCallback calls are chained
00075 static JSGCCallback gOldJSGCCallback;
00076 
00077 // data holder class for the enumerator callback below
00078 struct JSDyingJSObjectData
00079 {
00080     JSContext* cx;
00081     nsVoidArray* array;
00082 };
00083 
00084 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00085 WrappedJSDyingJSObjectFinder(JSDHashTable *table, JSDHashEntryHdr *hdr,
00086                 uint32 number, void *arg)
00087 {
00088     JSDyingJSObjectData* data = (JSDyingJSObjectData*) arg;
00089     nsXPCWrappedJS* wrapper = ((JSObject2WrappedJSMap::Entry*)hdr)->value;
00090     NS_ASSERTION(wrapper, "found a null JS wrapper!");
00091 
00092     // walk the wrapper chain and find any whose JSObject is to be finalized
00093     while(wrapper)
00094     {
00095         if(wrapper->IsSubjectToFinalization())
00096         {
00097             if(JS_IsAboutToBeFinalized(data->cx, wrapper->GetJSObject()))
00098                 data->array->AppendElement(wrapper);
00099         }
00100         wrapper = wrapper->GetNextWrapper();
00101     }
00102     return JS_DHASH_NEXT;
00103 }
00104 
00105 struct CX_AND_XPCRT_Data
00106 {
00107     JSContext* cx;
00108     XPCJSRuntime* rt;
00109 };
00110 
00111 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00112 NativeInterfaceGC(JSDHashTable *table, JSDHashEntryHdr *hdr,
00113                   uint32 number, void *arg)
00114 {
00115     CX_AND_XPCRT_Data* data = (CX_AND_XPCRT_Data*) arg;
00116     ((IID2NativeInterfaceMap::Entry*)hdr)->value->
00117             DealWithDyingGCThings(data->cx, data->rt);
00118     return JS_DHASH_NEXT;
00119 }
00120 
00121 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00122 NativeInterfaceSweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
00123                        uint32 number, void *arg)
00124 {
00125     CX_AND_XPCRT_Data* data = (CX_AND_XPCRT_Data*) arg;
00126     XPCNativeInterface* iface = ((IID2NativeInterfaceMap::Entry*)hdr)->value;
00127     if(iface->IsMarked())
00128     {
00129         iface->Unmark();
00130         return JS_DHASH_NEXT;
00131     }
00132 
00133 #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING
00134     printf("- Destroying XPCNativeInterface for %s\n",
00135             JS_GetStringBytes(JSVAL_TO_STRING(iface->GetName())));
00136 #endif
00137 
00138     XPCNativeInterface::DestroyInstance(data->cx, data->rt, iface);
00139     return JS_DHASH_REMOVE;
00140 }
00141 
00142 // *Some* NativeSets are referenced from mClassInfo2NativeSetMap.
00143 // *All* NativeSets are referenced from mNativeSetMap.
00144 // So, in mClassInfo2NativeSetMap we just clear references to the unmarked.
00145 // In mNativeSetMap we clear the references to the unmarked *and* delete them.
00146 
00147 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00148 NativeUnMarkedSetRemover(JSDHashTable *table, JSDHashEntryHdr *hdr,
00149                          uint32 number, void *arg)
00150 {
00151     XPCNativeSet* set = ((ClassInfo2NativeSetMap::Entry*)hdr)->value;
00152     if(set->IsMarked())
00153         return JS_DHASH_NEXT;
00154     return JS_DHASH_REMOVE;
00155 }
00156 
00157 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00158 NativeSetSweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
00159                  uint32 number, void *arg)
00160 {
00161     XPCNativeSet* set = ((NativeSetMap::Entry*)hdr)->key_value;
00162     if(set->IsMarked())
00163     {
00164         set->Unmark();
00165         return JS_DHASH_NEXT;
00166     }
00167 
00168 #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING
00169     printf("- Destroying XPCNativeSet for:\n");
00170     PRUint16 count = set->GetInterfaceCount();
00171     for(PRUint16 k = 0; k < count; k++)
00172     {
00173         XPCNativeInterface* iface = set->GetInterfaceAt(k);
00174         printf("    %s\n",JS_GetStringBytes(JSVAL_TO_STRING(iface->GetName())));
00175     }
00176 #endif
00177 
00178     XPCNativeSet::DestroyInstance(set);
00179     return JS_DHASH_REMOVE;
00180 }
00181 
00182 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00183 JSClassSweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
00184                uint32 number, void *arg)
00185 {
00186     XPCNativeScriptableShared* shared =
00187         ((XPCNativeScriptableSharedMap::Entry*) hdr)->key;
00188     if(shared->IsMarked())
00189     {
00190 #ifdef off_XPC_REPORT_JSCLASS_FLUSHING
00191         printf("+ Marked XPCNativeScriptableShared for: %s @ %x\n",
00192                shared->GetJSClass()->name,
00193                shared->GetJSClass());
00194 #endif
00195         shared->Unmark();
00196         return JS_DHASH_NEXT;
00197     }
00198 
00199 #ifdef XPC_REPORT_JSCLASS_FLUSHING
00200     printf("- Destroying XPCNativeScriptableShared for: %s @ %x\n",
00201            shared->GetJSClass()->name,
00202            shared->GetJSClass());
00203 #endif
00204 
00205     delete shared;
00206     return JS_DHASH_REMOVE;
00207 }
00208 
00209 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00210 DyingProtoKiller(JSDHashTable *table, JSDHashEntryHdr *hdr,
00211                  uint32 number, void *arg)
00212 {
00213     XPCWrappedNativeProto* proto =
00214         (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
00215     delete proto;
00216     return JS_DHASH_REMOVE;
00217 }
00218 
00219 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00220 DetachedWrappedNativeProtoMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
00221                                  uint32 number, void *arg)
00222 {
00223     XPCWrappedNativeProto* proto = 
00224         (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
00225 
00226     proto->Mark();
00227     return JS_DHASH_NEXT;
00228 }
00229 
00230 // GCCallback calls are chained
00231 JS_STATIC_DLL_CALLBACK(JSBool)
00232 ContextCallback(JSContext *cx, uintN operation)
00233 {
00234     XPCJSRuntime* self = nsXPConnect::GetRuntime();
00235     if (self)
00236     {
00237         if (operation == JSCONTEXT_NEW)
00238         {
00239             XPCPerThreadData* tls = XPCPerThreadData::GetData();
00240             if(tls)
00241             {
00242                 JS_SetThreadStackLimit(cx, tls->GetStackLimit());
00243             }
00244         }
00245     }
00246 
00247     return gOldJSContextCallback
00248            ? gOldJSContextCallback(cx, operation)
00249            : JS_TRUE;
00250 }
00251 
00252 // static
00253 JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
00254 {
00255     nsVoidArray* dyingWrappedJSArray;
00256 
00257     XPCJSRuntime* self = nsXPConnect::GetRuntime();
00258     if(self)
00259     {
00260         switch(status)
00261         {
00262             case JSGC_BEGIN:
00263             {
00264                 if(self->GetMainThreadOnlyGC() &&
00265                    PR_GetCurrentThread() != nsXPConnect::GetMainThread())
00266                 {
00267                     return JS_FALSE;
00268                 }
00269                 break;
00270             }
00271             case JSGC_MARK_END:
00272             {
00273                 NS_ASSERTION(!self->mDoingFinalization, "bad state");
00274     
00275                 // mThreadRunningGC indicates that GC is running
00276                 { // scoped lock
00277                     XPCAutoLock lock(self->GetMapLock());
00278                     NS_ASSERTION(!self->mThreadRunningGC, "bad state");
00279                     self->mThreadRunningGC = PR_GetCurrentThread();
00280                 }
00281 
00282                 // Skip this part if XPConnect is shutting down. We get into
00283                 // bad locking problems with the thread iteration otherwise.
00284                 if(!self->GetXPConnect()->IsShuttingDown())
00285                 {
00286                     PRLock* threadLock = XPCPerThreadData::GetLock();
00287                     if(threadLock)
00288                     { // scoped lock
00289                         nsAutoLock lock(threadLock);
00290 
00291                         XPCPerThreadData* iterp = nsnull;
00292                         XPCPerThreadData* thread;
00293 
00294                         while(nsnull != (thread =
00295                                      XPCPerThreadData::IterateThreads(&iterp)))
00296                         {
00297                             // Mark those AutoMarkingPtr lists!
00298                             thread->MarkAutoRootsBeforeJSFinalize(cx);
00299                         }
00300                     }
00301                 }
00302 
00303                 dyingWrappedJSArray = &self->mWrappedJSToReleaseArray;
00304                 {
00305                     XPCLock* lock = self->GetMainThreadOnlyGC() ?
00306                                     nsnull : self->GetMapLock();
00307 
00308                     XPCAutoLock al(lock); // lock the wrapper map if necessary
00309                     JSDyingJSObjectData data = {cx, dyingWrappedJSArray};
00310 
00311                     // Add any wrappers whose JSObjects are to be finalized to
00312                     // this array. Note that this is a nsVoidArray because
00313                     // we do not want to be changing the refcount of these wrappers.
00314                     // We add them to the array now and Release the array members
00315                     // later to avoid the posibility of doing any JS GCThing
00316                     // allocations during the gc cycle.
00317                     self->mWrappedJSMap->
00318                         Enumerate(WrappedJSDyingJSObjectFinder, &data);
00319                 }
00320 
00321                 // Do cleanup in NativeInterfaces. This part just finds 
00322                 // member cloned function objects that are about to be 
00323                 // collected. It does not deal with collection of interfaces or
00324                 // sets at this point.
00325                 CX_AND_XPCRT_Data data = {cx, self};
00326 
00327                 self->mIID2NativeInterfaceMap->
00328                     Enumerate(NativeInterfaceGC, &data);
00329 
00330                 // Find dying scopes...
00331                 XPCWrappedNativeScope::FinishedMarkPhaseOfGC(cx, self);
00332 
00333                 self->mDoingFinalization = JS_TRUE;
00334                 break;
00335             }
00336             case JSGC_FINALIZE_END:
00337             {
00338                 NS_ASSERTION(self->mDoingFinalization, "bad state");
00339                 self->mDoingFinalization = JS_FALSE;
00340 
00341                 // Release all the members whose JSObjects are now known
00342                 // to be dead.
00343 
00344                 dyingWrappedJSArray = &self->mWrappedJSToReleaseArray;
00345                 XPCLock* mapLock = self->GetMainThreadOnlyGC() ?
00346                                    nsnull : self->GetMapLock();
00347                 while(1)
00348                 {
00349                     nsXPCWrappedJS* wrapper;
00350                     {
00351                         XPCAutoLock al(mapLock); // lock if necessary
00352                         PRInt32 count = dyingWrappedJSArray->Count();
00353                         if(!count)
00354                         {
00355                             dyingWrappedJSArray->Compact();
00356                             break;
00357                         }
00358                         wrapper = NS_STATIC_CAST(nsXPCWrappedJS*,
00359                                     dyingWrappedJSArray->ElementAt(count-1));
00360                         dyingWrappedJSArray->RemoveElementAt(count-1);
00361                     }
00362                     NS_RELEASE(wrapper);
00363                 }
00364 
00365 
00366 #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING
00367                 printf("--------------------------------------------------------------\n");
00368                 int setsBefore = (int) self->mNativeSetMap->Count();
00369                 int ifacesBefore = (int) self->mIID2NativeInterfaceMap->Count();
00370 #endif
00371 
00372                 // We use this occasion to mark and sweep NativeInterfaces,
00373                 // NativeSets, and the WrappedNativeJSClasses...
00374 
00375                 // Do the marking...
00376                 XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos();
00377 
00378                 self->mDetachedWrappedNativeProtoMap->
00379                     Enumerate(DetachedWrappedNativeProtoMarker, nsnull);
00380 
00381                 // Mark the sets used in the call contexts. There is a small
00382                 // chance that a wrapper's set will change *while* a call is
00383                 // happening which uses that wrapper's old interfface set. So,
00384                 // we need to do this marking to avoid collecting those sets
00385                 // that might no longer be otherwise reachable from the wrappers
00386                 // or the wrapperprotos.
00387 
00388                 // Skip this part if XPConnect is shutting down. We get into
00389                 // bad locking problems with the thread iteration otherwise.
00390                 if(!self->GetXPConnect()->IsShuttingDown())
00391                 {
00392                     PRLock* threadLock = XPCPerThreadData::GetLock();
00393                     if(threadLock)
00394                     { // scoped lock
00395                         nsAutoLock lock(threadLock);
00396 
00397                         XPCPerThreadData* iterp = nsnull;
00398                         XPCPerThreadData* thread;
00399 
00400                         while(nsnull != (thread =
00401                                      XPCPerThreadData::IterateThreads(&iterp)))
00402                         {
00403                             // Mark those AutoMarkingPtr lists!
00404                             thread->MarkAutoRootsAfterJSFinalize();
00405 
00406                             XPCCallContext* ccxp = thread->GetCallContext();
00407                             while(ccxp)
00408                             {
00409                                 // Deal with the strictness of callcontext that
00410                                 // complains if you ask for a set when
00411                                 // it is in a state where the set could not
00412                                 // possibly be valid.
00413                                 if(ccxp->CanGetSet())
00414                                 {
00415                                     XPCNativeSet* set = ccxp->GetSet();
00416                                     if(set)
00417                                         set->Mark();
00418                                 }
00419                                 if(ccxp->CanGetInterface())
00420                                 {
00421                                     XPCNativeInterface* iface = ccxp->GetInterface();
00422                                     if(iface)
00423                                         iface->Mark();
00424                                 }
00425                                 ccxp = ccxp->GetPrevCallContext();
00426                             }
00427                         }
00428                     }
00429                 }
00430 
00431                 // Do the sweeping...
00432 
00433                 // We don't want to sweep the JSClasses at shutdown time.
00434                 // At this point there may be JSObjects using them that have
00435                 // been removed from the other maps.
00436                 if(!self->GetXPConnect()->IsShuttingDown())
00437                 {
00438                     self->mNativeScriptableSharedMap->
00439                         Enumerate(JSClassSweeper, nsnull);
00440                 }
00441 
00442                 self->mClassInfo2NativeSetMap->
00443                     Enumerate(NativeUnMarkedSetRemover, nsnull);
00444 
00445                 self->mNativeSetMap->
00446                     Enumerate(NativeSetSweeper, nsnull);
00447 
00448                 CX_AND_XPCRT_Data data = {cx, self};
00449 
00450                 self->mIID2NativeInterfaceMap->
00451                     Enumerate(NativeInterfaceSweeper, &data);
00452 
00453 #ifdef DEBUG
00454                 XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked();
00455 #endif
00456 
00457 #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING
00458                 int setsAfter = (int) self->mNativeSetMap->Count();
00459                 int ifacesAfter = (int) self->mIID2NativeInterfaceMap->Count();
00460 
00461                 printf("\n");
00462                 printf("XPCNativeSets:        before: %d  collected: %d  remaining: %d\n",
00463                        setsBefore, setsBefore - setsAfter, setsAfter);
00464                 printf("XPCNativeInterfaces:  before: %d  collected: %d  remaining: %d\n",
00465                        ifacesBefore, ifacesBefore - ifacesAfter, ifacesAfter);
00466                 printf("--------------------------------------------------------------\n");
00467 #endif
00468 
00469                 // Sweep scopes needing cleanup
00470                 XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC(cx);
00471 
00472                 // Now we are going to recycle any unused WrappedNativeTearoffs.
00473                 // We do this by iterating all the live callcontexts (on all
00474                 // threads!) and marking the tearoffs in use. And then we
00475                 // iterate over all the WrappedNative wrappers and sweep their
00476                 // tearoffs.
00477                 //
00478                 // This allows us to perhaps minimize the growth of the
00479                 // tearoffs. And also makes us not hold references to interfaces
00480                 // on our wrapped natives that we are not actually using.
00481                 //
00482                 // XXX We may decide to not do this on *every* gc cycle.
00483 
00484                 // Skip this part if XPConnect is shutting down. We get into
00485                 // bad locking problems with the thread iteration otherwise.
00486                 if(!self->GetXPConnect()->IsShuttingDown())
00487                 {
00488                     PRLock* threadLock = XPCPerThreadData::GetLock();
00489                     if(threadLock)
00490                     {
00491                         // Do the marking...
00492                         
00493                         { // scoped lock
00494                             nsAutoLock lock(threadLock);
00495 
00496                             XPCPerThreadData* iterp = nsnull;
00497                             XPCPerThreadData* thread;
00498 
00499                             while(nsnull != (thread =
00500                                      XPCPerThreadData::IterateThreads(&iterp)))
00501                             {
00502                                 XPCCallContext* ccxp = thread->GetCallContext();
00503                                 while(ccxp)
00504                                 {
00505                                     // Deal with the strictness of callcontext that
00506                                     // complains if you ask for a tearoff when
00507                                     // it is in a state where the tearoff could not
00508                                     // possibly be valid.
00509                                     if(ccxp->CanGetTearOff())
00510                                     {
00511                                         XPCWrappedNativeTearOff* to = 
00512                                             ccxp->GetTearOff();
00513                                         if(to)
00514                                             to->Mark();
00515                                     }
00516                                     ccxp = ccxp->GetPrevCallContext();
00517                                 }
00518                             }
00519                         }
00520     
00521                         // Do the sweeping...
00522                         XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs();
00523                     }
00524                 }
00525 
00526                 // Now we need to kill the 'Dying' XPCWrappedNativeProtos.
00527                 // We transfered these native objects to this table when their
00528                 // JSObject's were finalized. We did not destroy them immediately
00529                 // at that point because the ordering of JS finalization is not
00530                 // deterministic and we did not yet know if any wrappers that
00531                 // might still be referencing the protos where still yet to be
00532                 // finalized and destroyed. We *do* know that the protos'
00533                 // JSObjects would not have been finalized if there were any
00534                 // wrappers that referenced the proto but where not themselves
00535                 // slated for finalization in this gc cycle. So... at this point
00536                 // we know that any and all wrappers that might have been
00537                 // referencing the protos in the dying list are themselves dead.
00538                 // So, we can safely delete all the protos in the list.
00539 
00540                 self->mDyingWrappedNativeProtoMap->
00541                     Enumerate(DyingProtoKiller, nsnull);
00542 
00543 
00544                 // mThreadRunningGC indicates that GC is running.
00545                 // Clear it and notify waiters.
00546                 { // scoped lock
00547                     XPCAutoLock lock(self->GetMapLock());
00548                     NS_ASSERTION(self->mThreadRunningGC == PR_GetCurrentThread(), "bad state");
00549                     self->mThreadRunningGC = nsnull;
00550                     xpc_NotifyAll(self->GetMapLock());
00551                 }
00552 
00553                 break;
00554             }
00555             case JSGC_END:
00556             {
00557                 // NOTE that this event happens outside of the gc lock in
00558                 // the js engine. So this could be simultaneous with the
00559                 // events above.
00560 
00561                 XPCLock* lock = self->GetMainThreadOnlyGC() ?
00562                                 nsnull : self->GetMapLock();
00563 
00564                 // Do any deferred released of native objects.
00565                 if(self->GetDeferReleases())
00566                 {
00567                     nsVoidArray* array = &self->mNativesToReleaseArray;
00568 #ifdef XPC_TRACK_DEFERRED_RELEASES
00569                     printf("XPC - Begin deferred Release of %d nsISupports pointers\n",
00570                            array->Count());
00571 #endif
00572                     while(1)
00573                     {
00574                         nsISupports* obj;
00575                         {
00576                             XPCAutoLock al(lock); // lock if necessary
00577                             PRInt32 count = array->Count();
00578                             if(!count)
00579                             {
00580                                 array->Compact();
00581                                 break;
00582                             }
00583                             obj = NS_REINTERPRET_CAST(nsISupports*,
00584                                     array->ElementAt(count-1));
00585                             array->RemoveElementAt(count-1);
00586                         }
00587                         NS_RELEASE(obj);
00588                     }
00589 #ifdef XPC_TRACK_DEFERRED_RELEASES
00590                     printf("XPC - End deferred Releases\n");
00591 #endif
00592                 }
00593                 break;
00594             }
00595             default:
00596                 break;
00597         }
00598     }
00599 
00600     // always chain to old GCCallback if non-null.
00601     return gOldJSGCCallback ? gOldJSGCCallback(cx, status) : JS_TRUE;
00602 }
00603 
00604 /***************************************************************************/
00605 
00606 #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
00607 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00608 DEBUG_WrapperChecker(JSDHashTable *table, JSDHashEntryHdr *hdr,
00609                      uint32 number, void *arg)
00610 {
00611     XPCWrappedNative* wrapper = (XPCWrappedNative*)((JSDHashEntryStub*)hdr)->key;
00612     NS_ASSERTION(!wrapper->IsValid(), "found a 'valid' wrapper!");
00613     ++ *((int*)arg);
00614     return JS_DHASH_NEXT;
00615 }
00616 #endif
00617 
00618 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00619 WrappedJSShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
00620                         uint32 number, void *arg)
00621 {
00622     JSRuntime* rt = (JSRuntime*) arg;
00623     nsXPCWrappedJS* wrapper = ((JSObject2WrappedJSMap::Entry*)hdr)->value;
00624     NS_ASSERTION(wrapper, "found a null JS wrapper!");
00625     NS_ASSERTION(wrapper->IsValid(), "found an invalid JS wrapper!");
00626     wrapper->SystemIsBeingShutDown(rt);
00627     return JS_DHASH_NEXT;
00628 }
00629 
00630 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00631 DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
00632                                          uint32 number, void *arg)
00633 {
00634     XPCWrappedNativeProto* proto = 
00635         (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
00636 
00637     proto->SystemIsBeingShutDown(*((XPCCallContext*)arg));
00638     return JS_DHASH_NEXT;
00639 }
00640 
00641 void XPCJSRuntime::SystemIsBeingShutDown(XPCCallContext* ccx)
00642 {
00643     if(mDetachedWrappedNativeProtoMap)
00644         mDetachedWrappedNativeProtoMap->
00645             Enumerate(DetachedWrappedNativeProtoShutdownMarker, ccx);
00646 }
00647 
00648 XPCJSRuntime::~XPCJSRuntime()
00649 {
00650 #ifdef XPC_DUMP_AT_SHUTDOWN
00651     {
00652     // count the total JSContexts in use
00653     JSContext* iter = nsnull;
00654     int count = 0;
00655     while(JS_ContextIterator(mJSRuntime, &iter))
00656         count ++;
00657     if(count)
00658         printf("deleting XPCJSRuntime with %d live JSContexts\n", count);
00659     }
00660 #endif
00661 
00662     // clean up and destroy maps...
00663 
00664     if(mContextMap)
00665     {
00666         PurgeXPCContextList();
00667         delete mContextMap;
00668     }
00669 
00670     if(mWrappedJSMap)
00671     {
00672 #ifdef XPC_DUMP_AT_SHUTDOWN
00673         uint32 count = mWrappedJSMap->Count();
00674         if(count)
00675             printf("deleting XPCJSRuntime with %d live wrapped JSObject\n", (int)count);
00676 #endif
00677         mWrappedJSMap->Enumerate(WrappedJSShutdownMarker, mJSRuntime);
00678         delete mWrappedJSMap;
00679     }
00680 
00681     if(mWrappedJSClassMap)
00682     {
00683 #ifdef XPC_DUMP_AT_SHUTDOWN
00684         uint32 count = mWrappedJSClassMap->Count();
00685         if(count)
00686             printf("deleting XPCJSRuntime with %d live nsXPCWrappedJSClass\n", (int)count);
00687 #endif
00688         delete mWrappedJSClassMap;
00689     }
00690 
00691     if(mIID2NativeInterfaceMap)
00692     {
00693 #ifdef XPC_DUMP_AT_SHUTDOWN
00694         uint32 count = mIID2NativeInterfaceMap->Count();
00695         if(count)
00696             printf("deleting XPCJSRuntime with %d live XPCNativeInterfaces\n", (int)count);
00697 #endif
00698         delete mIID2NativeInterfaceMap;
00699     }
00700 
00701     if(mClassInfo2NativeSetMap)
00702     {
00703 #ifdef XPC_DUMP_AT_SHUTDOWN
00704         uint32 count = mClassInfo2NativeSetMap->Count();
00705         if(count)
00706             printf("deleting XPCJSRuntime with %d live XPCNativeSets\n", (int)count);
00707 #endif
00708         delete mClassInfo2NativeSetMap;
00709     }
00710 
00711     if(mNativeSetMap)
00712     {
00713 #ifdef XPC_DUMP_AT_SHUTDOWN
00714         uint32 count = mNativeSetMap->Count();
00715         if(count)
00716             printf("deleting XPCJSRuntime with %d live XPCNativeSets\n", (int)count);
00717 #endif
00718         delete mNativeSetMap;
00719     }
00720 
00721     if(mMapLock)
00722         XPCAutoLock::DestroyLock(mMapLock);
00723     NS_IF_RELEASE(mJSRuntimeService);
00724 
00725     if(mThisTranslatorMap)
00726     {
00727 #ifdef XPC_DUMP_AT_SHUTDOWN
00728         uint32 count = mThisTranslatorMap->Count();
00729         if(count)
00730             printf("deleting XPCJSRuntime with %d live ThisTranslator\n", (int)count);
00731 #endif
00732         delete mThisTranslatorMap;
00733     }
00734 
00735 #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
00736     if(DEBUG_WrappedNativeHashtable)
00737     {
00738         int LiveWrapperCount = 0;
00739         JS_DHashTableEnumerate(DEBUG_WrappedNativeHashtable,
00740                                DEBUG_WrapperChecker, &LiveWrapperCount);
00741         if(LiveWrapperCount)
00742             printf("deleting XPCJSRuntime with %d live XPCWrappedNative (found in wrapper check)\n", (int)LiveWrapperCount);
00743         JS_DHashTableDestroy(DEBUG_WrappedNativeHashtable);
00744     }
00745 #endif
00746 
00747     if(mNativeScriptableSharedMap)
00748     {
00749 #ifdef XPC_DUMP_AT_SHUTDOWN
00750         uint32 count = mNativeScriptableSharedMap->Count();
00751         if(count)
00752             printf("deleting XPCJSRuntime with %d live XPCNativeScriptableShared\n", (int)count);
00753 #endif
00754         delete mNativeScriptableSharedMap;
00755     }
00756 
00757     if(mDyingWrappedNativeProtoMap)
00758     {
00759 #ifdef XPC_DUMP_AT_SHUTDOWN
00760         uint32 count = mDyingWrappedNativeProtoMap->Count();
00761         if(count)
00762             printf("deleting XPCJSRuntime with %d live but dying XPCWrappedNativeProto\n", (int)count);
00763 #endif
00764         delete mDyingWrappedNativeProtoMap;
00765     }
00766 
00767     if(mDetachedWrappedNativeProtoMap)
00768     {
00769 #ifdef XPC_DUMP_AT_SHUTDOWN
00770         uint32 count = mDetachedWrappedNativeProtoMap->Count();
00771         if(count)
00772             printf("deleting XPCJSRuntime with %d live detached XPCWrappedNativeProto\n", (int)count);
00773 #endif
00774         delete mDetachedWrappedNativeProtoMap;
00775     }
00776 
00777     if(mExplicitNativeWrapperMap)
00778     {
00779 #ifdef XPC_DUMP_AT_SHUTDOWN
00780         uint32 count = mExplicitNativeWrapperMap->Count();
00781         if(count)
00782             printf("deleting XPCJSRuntime with %d live explicit XPCNativeWrapper\n", (int)count);
00783 #endif
00784         delete mExplicitNativeWrapperMap;
00785     }
00786 
00787     // unwire the readable/JSString sharing magic
00788     XPCStringConvert::ShutdownDOMStringFinalizer();
00789 
00790     XPCConvert::RemoveXPCOMUCStringFinalizer();
00791 
00792     gOldJSGCCallback = NULL;
00793     gOldJSContextCallback = NULL;
00794 }
00795 
00796 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect,
00797                            nsIJSRuntimeService* aJSRuntimeService)
00798  : mXPConnect(aXPConnect),
00799    mJSRuntime(nsnull),
00800    mJSRuntimeService(aJSRuntimeService),
00801    mContextMap(JSContext2XPCContextMap::newMap(XPC_CONTEXT_MAP_SIZE)),
00802    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
00803    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
00804    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
00805    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
00806    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
00807    mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
00808    mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
00809    mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
00810    mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)),
00811    mExplicitNativeWrapperMap(XPCNativeWrapperMap::newMap(XPC_NATIVE_WRAPPER_MAP_SIZE)),
00812    mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")),
00813    mThreadRunningGC(nsnull),
00814    mWrappedJSToReleaseArray(),
00815    mNativesToReleaseArray(),
00816    mMainThreadOnlyGC(JS_FALSE),
00817    mDeferReleases(JS_FALSE),
00818    mDoingFinalization(JS_FALSE)
00819 {
00820 #ifdef XPC_CHECK_WRAPPERS_AT_SHUTDOWN
00821     DEBUG_WrappedNativeHashtable =
00822         JS_NewDHashTable(JS_DHashGetStubOps(), nsnull,
00823                          sizeof(JSDHashEntryStub), 128);
00824 #endif
00825 
00826     // these jsids filled in later when we have a JSContext to work with.
00827     mStrIDs[0] = 0;
00828 
00829     if(mJSRuntimeService)
00830     {
00831         NS_ADDREF(mJSRuntimeService);
00832         mJSRuntimeService->GetRuntime(&mJSRuntime);
00833     }
00834 
00835     NS_ASSERTION(!gOldJSGCCallback, "XPCJSRuntime created more than once");
00836     if(mJSRuntime)
00837     {
00838         gOldJSContextCallback = JS_SetContextCallback(mJSRuntime,
00839                                                       ContextCallback);
00840         gOldJSGCCallback = JS_SetGCCallbackRT(mJSRuntime, GCCallback);
00841     }
00842 
00843     // Install a JavaScript 'debugger' keyword handler in debug builds only
00844 #ifdef DEBUG
00845     if(mJSRuntime && !mJSRuntime->debuggerHandler)
00846         xpc_InstallJSDebuggerKeywordHandler(mJSRuntime);
00847 #endif
00848 }
00849 
00850 // static
00851 XPCJSRuntime*
00852 XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect,
00853                               nsIJSRuntimeService* aJSRuntimeService)
00854 {
00855     NS_PRECONDITION(aXPConnect,"bad param");
00856     NS_PRECONDITION(aJSRuntimeService,"bad param");
00857 
00858     XPCJSRuntime* self;
00859 
00860     self = new XPCJSRuntime(aXPConnect,
00861                             aJSRuntimeService);
00862 
00863     if(self                                  &&
00864        self->GetJSRuntime()                  &&
00865        self->GetContextMap()                 &&
00866        self->GetWrappedJSMap()               &&
00867        self->GetWrappedJSClassMap()          &&
00868        self->GetIID2NativeInterfaceMap()     &&
00869        self->GetClassInfo2NativeSetMap()     &&
00870        self->GetNativeSetMap()               &&
00871        self->GetThisTranslatorMap()          &&
00872        self->GetNativeScriptableSharedMap()  &&
00873        self->GetDyingWrappedNativeProtoMap() &&
00874        self->GetExplicitNativeWrapperMap()   &&
00875        self->GetMapLock())
00876     {
00877         return self;
00878     }
00879     delete self;
00880     return nsnull;
00881 }
00882 
00883 XPCContext*
00884 XPCJSRuntime::GetXPCContext(JSContext* cx)
00885 {
00886     XPCContext* xpcc;
00887 
00888     // find it in the map.
00889 
00890     { // scoped lock
00891         XPCAutoLock lock(GetMapLock());
00892         xpcc = mContextMap->Find(cx);
00893     }
00894 
00895     // else resync with the JSRuntime's JSContext list and see if it is found
00896     if(!xpcc)
00897         xpcc = SyncXPCContextList(cx);
00898     return xpcc;
00899 }
00900 
00901 
00902 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00903 SweepContextsCB(JSDHashTable *table, JSDHashEntryHdr *hdr,
00904                 uint32 number, void *arg)
00905 {
00906     XPCContext* xpcc = ((JSContext2XPCContextMap::Entry*)hdr)->value;
00907     if(xpcc->IsMarked())
00908     {
00909         xpcc->Unmark();
00910         return JS_DHASH_NEXT;
00911     }
00912 
00913     // this XPCContext represents a dead JSContext - delete it
00914     delete xpcc;
00915     return JS_DHASH_REMOVE;
00916 }
00917 
00918 XPCContext*
00919 XPCJSRuntime::SyncXPCContextList(JSContext* cx /* = nsnull */)
00920 {
00921     // hold the map lock through this whole thing
00922     XPCAutoLock lock(GetMapLock());
00923 
00924     XPCContext* found = nsnull;
00925 
00926     // add XPCContexts that represent any JSContexts we have not seen before
00927     JSContext *cur, *iter = nsnull;
00928     while(nsnull != (cur = JS_ContextIterator(mJSRuntime, &iter)))
00929     {
00930         XPCContext* xpcc = mContextMap->Find(cur);
00931 
00932         if(!xpcc)
00933         {
00934             xpcc = XPCContext::newXPCContext(this, cur);
00935             if(xpcc)
00936                 mContextMap->Add(xpcc);
00937         }
00938         if(xpcc)
00939         {
00940             xpcc->Mark();
00941         }
00942 
00943         // if it is our first context then we need to generate our string ids
00944         if(!mStrIDs[0])
00945             GenerateStringIDs(cur);
00946 
00947         if(cx && cx == cur)
00948             found = xpcc;
00949     }
00950     // get rid of any XPCContexts that represent dead JSContexts
00951     mContextMap->Enumerate(SweepContextsCB, 0);
00952 
00953     XPCPerThreadData* tls = XPCPerThreadData::GetData();
00954     if(tls)
00955     {
00956         if(found)
00957             tls->SetRecentContext(cx, found);
00958         else
00959             tls->ClearRecentContext();
00960     }
00961 
00962     return found;
00963 }
00964 
00965 
00966 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
00967 PurgeContextsCB(JSDHashTable *table, JSDHashEntryHdr *hdr,
00968                 uint32 number, void *arg)
00969 {
00970     delete ((JSContext2XPCContextMap::Entry*)hdr)->value;
00971     return JS_DHASH_REMOVE;
00972 }
00973 
00974 void
00975 XPCJSRuntime::PurgeXPCContextList()
00976 {
00977     // hold the map lock through this whole thing
00978     XPCAutoLock lock(GetMapLock());
00979 
00980     // get rid of all XPCContexts
00981     mContextMap->Enumerate(PurgeContextsCB, nsnull);
00982 }
00983 
00984 JSBool
00985 XPCJSRuntime::GenerateStringIDs(JSContext* cx)
00986 {
00987     NS_PRECONDITION(!mStrIDs[0],"string ids generated twice!");
00988     for(uintN i = 0; i < IDX_TOTAL_COUNT; i++)
00989     {
00990         JSString* str = JS_InternString(cx, mStrings[i]);
00991         if(!str || !JS_ValueToId(cx, STRING_TO_JSVAL(str), &mStrIDs[i]))
00992         {
00993             mStrIDs[0] = 0;
00994             return JS_FALSE;
00995         }
00996 
00997         mStrJSVals[i] = STRING_TO_JSVAL(str);
00998     }
00999     return JS_TRUE;
01000 }
01001 
01002 JSBool
01003 XPCJSRuntime::DeferredRelease(nsISupports* obj)
01004 {
01005     NS_ASSERTION(obj, "bad param");
01006     NS_ASSERTION(GetDeferReleases(), "bad call");
01007 
01008     XPCLock* lock = GetMainThreadOnlyGC() ? nsnull : GetMapLock();
01009     {
01010         XPCAutoLock al(lock); // lock if necessary
01011         if(!mNativesToReleaseArray.Count())
01012         {
01013             // This array sometimes has 1000's
01014             // of entries, and usually has 50-200 entries. Avoid lots
01015             // of incremental grows.  We compact it down when we're done.
01016             mNativesToReleaseArray.SizeTo(256);
01017         }
01018         return mNativesToReleaseArray.AppendElement(obj);
01019     }        
01020 }
01021 
01022 /***************************************************************************/
01023 
01024 #ifdef DEBUG
01025 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
01026 ContextMapDumpEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
01027                          uint32 number, void *arg)
01028 {
01029     ((JSContext2XPCContextMap::Entry*)hdr)->value->DebugDump(*(PRInt16*)arg);
01030     return JS_DHASH_NEXT;
01031 }
01032 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
01033 WrappedJSClassMapDumpEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
01034                                 uint32 number, void *arg)
01035 {
01036     ((IID2WrappedJSClassMap::Entry*)hdr)->value->DebugDump(*(PRInt16*)arg);
01037     return JS_DHASH_NEXT;
01038 }
01039 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
01040 WrappedJSMapDumpEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
01041                            uint32 number, void *arg)
01042 {
01043     ((JSObject2WrappedJSMap::Entry*)hdr)->value->DebugDump(*(PRInt16*)arg);
01044     return JS_DHASH_NEXT;
01045 }
01046 JS_STATIC_DLL_CALLBACK(JSDHashOperator)
01047 NativeSetDumpEnumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
01048                         uint32 number, void *arg)
01049 {
01050     ((NativeSetMap::Entry*)hdr)->key_value->DebugDump(*(PRInt16*)arg);
01051     return JS_DHASH_NEXT;
01052 }
01053 #endif
01054 
01055 void
01056 XPCJSRuntime::DebugDump(PRInt16 depth)
01057 {
01058 #ifdef DEBUG
01059     depth--;
01060     XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", this));
01061         XPC_LOG_INDENT();
01062         XPC_LOG_ALWAYS(("mXPConnect @ %x", mXPConnect));
01063         XPC_LOG_ALWAYS(("mJSRuntime @ %x", mJSRuntime));
01064         XPC_LOG_ALWAYS(("mMapLock @ %x", mMapLock));
01065         XPC_LOG_ALWAYS(("mJSRuntimeService @ %x", mJSRuntimeService));
01066 
01067         XPC_LOG_ALWAYS(("mWrappedJSToReleaseArray @ %x with %d wrappers(s)", \
01068                          &mWrappedJSToReleaseArray,
01069                          mWrappedJSToReleaseArray.Count()));
01070 
01071         XPC_LOG_ALWAYS(("mContextMap @ %x with %d context(s)", \
01072                          mContextMap, mContextMap ? mContextMap->Count() : 0));
01073         // iterate contexts...
01074         if(depth && mContextMap && mContextMap->Count())
01075         {
01076             XPC_LOG_INDENT();
01077             mContextMap->Enumerate(ContextMapDumpEnumerator, &depth);
01078             XPC_LOG_OUTDENT();
01079         }
01080 
01081         XPC_LOG_ALWAYS(("mWrappedJSClassMap @ %x with %d wrapperclasses(s)", \
01082                          mWrappedJSClassMap, mWrappedJSClassMap ? \
01083                                             mWrappedJSClassMap->Count() : 0));
01084         // iterate wrappersclasses...
01085         if(depth && mWrappedJSClassMap && mWrappedJSClassMap->Count())
01086         {
01087             XPC_LOG_INDENT();
01088             mWrappedJSClassMap->Enumerate(WrappedJSClassMapDumpEnumerator, &depth);
01089             XPC_LOG_OUTDENT();
01090         }
01091         XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)", \
01092                          mWrappedJSMap, mWrappedJSMap ? \
01093                                             mWrappedJSMap->Count() : 0));
01094         // iterate wrappers...
01095         if(depth && mWrappedJSMap && mWrappedJSMap->Count())
01096         {
01097             XPC_LOG_INDENT();
01098             mWrappedJSMap->Enumerate(WrappedJSMapDumpEnumerator, &depth);
01099             XPC_LOG_OUTDENT();
01100         }
01101 
01102         XPC_LOG_ALWAYS(("mIID2NativeInterfaceMap @ %x with %d interface(s)", \
01103                          mIID2NativeInterfaceMap, mIID2NativeInterfaceMap ? \
01104                                     mIID2NativeInterfaceMap->Count() : 0));
01105 
01106         XPC_LOG_ALWAYS(("mClassInfo2NativeSetMap @ %x with %d sets(s)", \
01107                          mClassInfo2NativeSetMap, mClassInfo2NativeSetMap ? \
01108                                     mClassInfo2NativeSetMap->Count() : 0));
01109 
01110         XPC_LOG_ALWAYS(("mThisTranslatorMap @ %x with %d translator(s)", \
01111                          mThisTranslatorMap, mThisTranslatorMap ? \
01112                                     mThisTranslatorMap->Count() : 0));
01113 
01114         XPC_LOG_ALWAYS(("mNativeSetMap @ %x with %d sets(s)", \
01115                          mNativeSetMap, mNativeSetMap ? \
01116                                     mNativeSetMap->Count() : 0));
01117 
01118         // iterate sets...
01119         if(depth && mNativeSetMap && mNativeSetMap->Count())
01120         {
01121             XPC_LOG_INDENT();
01122             mNativeSetMap->Enumerate(NativeSetDumpEnumerator, &depth);
01123             XPC_LOG_OUTDENT();
01124         }
01125 
01126         XPC_LOG_OUTDENT();
01127 #endif
01128 }
01129