void XPCJSRuntime::RootContextGlobals() { JSContext *iter = nsnull, *acx; while((acx = JS_ContextIterator(GetJSRuntime(), &iter))) { if(JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL)) { JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL); --mUnrootedGlobalCount; } } NS_ASSERTION(mUnrootedGlobalCount == 0, "bad state"); }
Application::Application() : _myTerminate(false), _myInterval(100000), _myDebugger(0) { JSRuntime *myRuntime = JS_NewRuntime(JS_HEAP_SIZE); JSContext *myContext = JS_NewContext(myRuntime, JS_STACK_CHUNK); JS_ToggleOptions(myContext, JSOPTION_STRICT); JSObject *myGlobal = JS_NewObject(myContext, &tuttle_global_class, NULL, NULL); JS_InitStandardClasses(myContext, myGlobal); JS_DefineFunctions(myContext, myGlobal, tuttle_global_functions); _myRuntime = myRuntime; _myContext = myContext; _myGlobal = myGlobal; }
JSBool XPCJSRuntime::OnJSContextNew(JSContext *cx) { NS_TIME_FUNCTION; // if it is our first context then we need to generate our string ids JSBool ok = JS_TRUE; if(JSID_IS_VOID(mStrIDs[0])) { JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024); JSAutoRequest ar(cx); for(uintN i = 0; i < IDX_TOTAL_COUNT; i++) { JSString* str = JS_InternString(cx, mStrings[i]); if(!str || !JS_ValueToId(cx, STRING_TO_JSVAL(str), &mStrIDs[i])) { mStrIDs[0] = JSID_VOID; ok = JS_FALSE; break; } mStrJSVals[i] = STRING_TO_JSVAL(str); } } if (!ok) return JS_FALSE; XPCPerThreadData* tls = XPCPerThreadData::GetData(cx); if(!tls) return JS_FALSE; XPCContext* xpc = new XPCContext(this, cx); if (!xpc) return JS_FALSE; JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024); JS_SetScriptStackQuota(cx, 25 * sizeof(size_t) * 1024 * 1024); // we want to mark the global object ourselves since we use a different color JS_ToggleOptions(cx, JSOPTION_UNROOTED_GLOBAL); return JS_TRUE; }
void XPCJSRuntime::UnrootContextGlobals() { mUnrootedGlobalCount = 0; JSContext *iter = nsnull, *acx; while((acx = JS_ContextIterator(GetJSRuntime(), &iter))) { NS_ASSERTION(!JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL), "unrooted global should be set only during CC"); if(XPCPerThreadData::IsMainThread(acx) && nsXPConnect::GetXPConnect()->GetRequestDepth(acx) == 0) { JS_ClearNewbornRoots(acx); if(acx->globalObject) { JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL); ++mUnrootedGlobalCount; } } } }
void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc, JSBool rootGlobals) { if(mUnrootedGlobalCount != 0) { JSContext *iter = nsnull, *acx; while((acx = JS_ContextIterator(GetJSRuntime(), &iter))) { if(JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL)) { NS_ASSERTION(nsXPConnect::GetXPConnect()->GetRequestDepth(acx) == 0, "active cx must be always rooted"); NS_ASSERTION(acx->globalObject, "bad state"); JS_CALL_OBJECT_TRACER(trc, acx->globalObject, "global object"); if(rootGlobals) { NS_ASSERTION(mUnrootedGlobalCount != 0, "bad state"); NS_ASSERTION(trc == acx->runtime->gcMarkingTracer, "bad tracer"); JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL); --mUnrootedGlobalCount; } } } } XPCWrappedNativeScope::TraceJS(trc, this); for(XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) static_cast<XPCTraceableVariant*>(e)->TraceJS(trc); for(XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot()) static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc); if(mJSHolders.ops) JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc); }
int main(int argc, const char* argv[]) { JSRuntime* rt = NULL; JSContext* cx = NULL; JSObject* global = NULL; JSObject* klass = NULL; JSScript* script; JSString* scriptsrc; jschar* schars; size_t slen; jsval sroot; jsval result; couch_args* args = couch_parse_args(argc, argv); rt = JS_NewRuntime(64L * 1024L * 1024L); if(rt == NULL) return 1; cx = JS_NewContext(rt, args->stack_size); if(cx == NULL) return 1; JS_SetErrorReporter(cx, couch_error); JS_ToggleOptions(cx, JSOPTION_XML); SETUP_REQUEST(cx); global = JS_NewObject(cx, &global_class, NULL, NULL); if(global == NULL) return 1; JS_SetGlobalObject(cx, global); if(!JS_InitStandardClasses(cx, global)) return 1; if(couch_load_funcs(cx, global, global_functions) != JS_TRUE) return 1; if(args->use_http) { http_check_enabled(); klass = JS_InitClass( cx, global, NULL, &CouchHTTPClass, req_ctor, 0, CouchHTTPProperties, CouchHTTPFunctions, NULL, NULL ); if(!klass) { fprintf(stderr, "Failed to initialize CouchHTTP class.\n"); exit(2); } } // Convert script source to jschars. scriptsrc = dec_string(cx, args->script, strlen(args->script)); if(!scriptsrc) return 1; schars = JS_GetStringChars(scriptsrc); slen = JS_GetStringLength(scriptsrc); // Root it so GC doesn't collect it. sroot = STRING_TO_JSVAL(scriptsrc); if(JS_AddRoot(cx, &sroot) != JS_TRUE) { fprintf(stderr, "Internal root error.\n"); return 1; } // Compile and run script = JS_CompileUCScript(cx, global, schars, slen, args->script_name, 1); if(!script) { fprintf(stderr, "Failed to compile script.\n"); return 1; } JS_ExecuteScript(cx, global, script, &result); // Warning message if we don't remove it. JS_RemoveRoot(cx, &sroot); FINISH_REQUEST(cx); JS_DestroyContext(cx); JS_DestroyRuntime(rt); JS_ShutDown(); return 0; }
static int ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) { const char rcfilename[] = "xpcshell.js"; FILE *rcfile; int i, j, length; JSObject *argsObj; char *filename = NULL; JSBool isInteractive = JS_TRUE; JSBool forceTTY = JS_FALSE; rcfile = fopen(rcfilename, "r"); if (rcfile) { printf("[loading '%s'...]\n", rcfilename); ProcessFile(cx, obj, rcfilename, rcfile, JS_FALSE); } /* * Scan past all optional arguments so we can create the arguments object * before processing any -f options, which must interleave properly with * -v and -w options. This requires two passes, and without getopt, we'll * have to keep the option logic here and in the second for loop in sync. */ for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { ++i; break; } switch (argv[i][1]) { case 'v': case 'f': case 'e': ++i; break; default:; } } /* * Create arguments early and define it to root it, so it's safe from any * GC calls nested below, and so it is available to -f <file> arguments. */ argsObj = JS_NewArrayObject(cx, 0, NULL); if (!argsObj) return 1; if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0)) { return 1; } length = argc - i; for (j = 0; j < length; j++) { JSString *str = JS_NewStringCopyZ(cx, argv[i++]); if (!str) return 1; if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), NULL, NULL, JSPROP_ENUMERATE)) { return 1; } } for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { filename = argv[i++]; isInteractive = JS_FALSE; break; } switch (argv[i][1]) { case 'v': if (++i == argc) { return usage(); } JS_SetVersion(cx, JSVersion(atoi(argv[i]))); break; case 'W': reportWarnings = JS_FALSE; break; case 'w': reportWarnings = JS_TRUE; break; case 's': JS_ToggleOptions(cx, JSOPTION_STRICT); break; case 'x': JS_ToggleOptions(cx, JSOPTION_XML); break; case 'P': if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) { JSObject *gobj; if (!JS_SealObject(cx, obj, JS_TRUE)) return JS_FALSE; gobj = JS_NewObject(cx, &global_class, NULL, NULL); if (!gobj) return JS_FALSE; if (!JS_SetPrototype(cx, gobj, obj)) return JS_FALSE; JS_SetParent(cx, gobj, NULL); JS_SetGlobalObject(cx, gobj); obj = gobj; } break; case 'f': if (++i == argc) { return usage(); } Process(cx, obj, argv[i], JS_FALSE); /* * XXX: js -f foo.js should interpret foo.js and then * drop into interactive mode, but that breaks test * harness. Just execute foo.js for now. */ isInteractive = JS_FALSE; break; case 'i': isInteractive = forceTTY = JS_TRUE; break; case 'e': { jsval rval; if (++i == argc) { return usage(); } JS_EvaluateScript(cx, obj, argv[i], strlen(argv[i]), "-e", 1, &rval); isInteractive = JS_FALSE; break; } case 'C': compileOnly = JS_TRUE; isInteractive = JS_FALSE; break; #ifdef MOZ_SHARK case 'k': JS_ConnectShark(); break; #endif default: return usage(); } } if (filename || isInteractive) Process(cx, obj, filename, forceTTY); return gExitCode; }
static int ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc) { int i, j, length; JSObject *argsObj; char *filename = NULL; JSBool isInteractive = JS_TRUE; JSBool forceTTY = JS_FALSE; /* * Scan past all optional arguments so we can create the arguments object * before processing any -f options, which must interleave properly with * -v and -w options. This requires two passes, and without getopt, we'll * have to keep the option logic here and in the second for loop in sync. */ for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { ++i; break; } switch (argv[i][1]) { case 'b': case 'c': case 'f': case 'e': case 'v': case 'S': ++i; break; default:; } } /* * Create arguments early and define it to root it, so it's safe from any * GC calls nested below, and so it is available to -f <file> arguments. */ argsObj = JS_NewArrayObject(cx, 0, NULL); if (!argsObj) return 1; if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(argsObj), NULL, NULL, 0)) { return 1; } length = argc - i; for (j = 0; j < length; j++) { JSString *str = JS_NewStringCopyZ(cx, argv[i++]); if (!str) return 1; if (!JS_DefineElement(cx, argsObj, j, STRING_TO_JSVAL(str), NULL, NULL, JSPROP_ENUMERATE)) { return 1; } } for (i = 0; i < argc; i++) { if (argv[i][0] != '-' || argv[i][1] == '\0') { filename = argv[i++]; isInteractive = JS_FALSE; break; } switch (argv[i][1]) { case 'v': if (++i == argc) return usage(); JS_SetVersion(cx, (JSVersion) atoi(argv[i])); break; case 'w': reportWarnings = JS_TRUE; break; case 'W': reportWarnings = JS_FALSE; break; case 's': JS_ToggleOptions(cx, JSOPTION_STRICT); break; case 'E': JS_ToggleOptions(cx, JSOPTION_RELIMIT); break; case 'x': JS_ToggleOptions(cx, JSOPTION_XML); break; case 'o': if (++i == argc) return usage(); for (j = 0; js_options[j].name; ++j) { if (strcmp(js_options[j].name, argv[i]) == 0) { JS_ToggleOptions(cx, js_options[j].flag); break; } } break; case 'c': /* set stack chunk size */ gStackChunkSize = atoi(argv[++i]); break; case 'f': if (++i == argc) return usage(); Process(cx, obj, argv[i], JS_FALSE); /* * XXX: js -f foo.js should interpret foo.js and then * drop into interactive mode, but that breaks the test * harness. Just execute foo.js for now. */ isInteractive = JS_FALSE; break; case 'e': { jsval rval; if (++i == argc) return usage(); /* Pass a filename of -e to imitate PERL */ JS_EvaluateScript(cx, obj, argv[i], SG_STRLEN(argv[i]), "-e", 1, &rval); isInteractive = JS_FALSE; break; } case 'C': compileOnly = JS_TRUE; isInteractive = JS_FALSE; break; case 'i': isInteractive = forceTTY = JS_TRUE; break; case 'S': if (++i == argc) return usage(); /* Set maximum stack size. */ gMaxStackSize = atoi(argv[i]); break; #ifdef MOZ_SHARK case 'k': JS_ConnectShark(); break; #endif default: return usage(); } } if (filename || isInteractive) Process(cx, obj, filename, forceTTY); return gExitCode; }
// static JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status) { XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance(); if(self) { switch(status) { case JSGC_BEGIN: { if(!NS_IsMainThread()) { return JS_FALSE; } // We seem to sometime lose the unrooted global flag. Restore it // here. FIXME: bug 584495. JSContext *iter = nsnull, *acx; while((acx = JS_ContextIterator(cx->runtime, &iter))) { if (!JS_HAS_OPTION(acx, JSOPTION_UNROOTED_GLOBAL)) JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL); } break; } case JSGC_MARK_END: { NS_ASSERTION(!self->mDoingFinalization, "bad state"); // mThreadRunningGC indicates that GC is running { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(!self->mThreadRunningGC, "bad state"); self->mThreadRunningGC = PR_GetCurrentThread(); } nsTArray<nsXPCWrappedJS*>* dyingWrappedJSArray = &self->mWrappedJSToReleaseArray; { JSDyingJSObjectData data = {cx, dyingWrappedJSArray}; // Add any wrappers whose JSObjects are to be finalized to // this array. Note that we do not want to be changing the // 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, &data); } // Do cleanup in NativeInterfaces. This part just finds // member cloned function objects that are about to be // collected. It does not deal with collection of interfaces or // sets at this point. CX_AND_XPCRT_Data data = {cx, self}; self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceGC, &data); // Find dying scopes... XPCWrappedNativeScope::FinishedMarkPhaseOfGC(cx, self); self->mDoingFinalization = JS_TRUE; break; } case JSGC_FINALIZE_END: { NS_ASSERTION(self->mDoingFinalization, "bad state"); self->mDoingFinalization = JS_FALSE; // Release all the members whose JSObjects are now known // to be dead. DoDeferredRelease(self->mWrappedJSToReleaseArray); #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING printf("--------------------------------------------------------------\n"); int setsBefore = (int) self->mNativeSetMap->Count(); int ifacesBefore = (int) self->mIID2NativeInterfaceMap->Count(); #endif // We use this occasion to mark and sweep NativeInterfaces, // NativeSets, and the WrappedNativeJSClasses... // Do the marking... XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos(); self->mDetachedWrappedNativeProtoMap-> Enumerate(DetachedWrappedNativeProtoMarker, nsnull); DOM_MarkInterfaces(); // Mark the sets used in the call contexts. There is a small // chance that a wrapper's set will change *while* a call is // happening which uses that wrapper's old interfface set. So, // we need to do this marking to avoid collecting those sets // that might no longer be otherwise reachable from the wrappers // or the wrapperprotos. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { // Mark those AutoMarkingPtr lists! thread->MarkAutoRootsAfterJSFinalize(); XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a set when // it is in a state where the set could not // possibly be valid. if(ccxp->CanGetSet()) { XPCNativeSet* set = ccxp->GetSet(); if(set) set->Mark(); } if(ccxp->CanGetInterface()) { XPCNativeInterface* iface = ccxp->GetInterface(); if(iface) iface->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } } // Do the sweeping... // We don't want to sweep the JSClasses at shutdown time. // At this point there may be JSObjects using them that have // been removed from the other maps. if(!self->GetXPConnect()->IsShuttingDown()) { self->mNativeScriptableSharedMap-> Enumerate(JSClassSweeper, nsnull); } self->mClassInfo2NativeSetMap-> Enumerate(NativeUnMarkedSetRemover, nsnull); self->mNativeSetMap-> Enumerate(NativeSetSweeper, nsnull); self->mIID2NativeInterfaceMap-> Enumerate(NativeInterfaceSweeper, nsnull); #ifdef DEBUG XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked(); #endif #ifdef XPC_REPORT_NATIVE_INTERFACE_AND_SET_FLUSHING int setsAfter = (int) self->mNativeSetMap->Count(); int ifacesAfter = (int) self->mIID2NativeInterfaceMap->Count(); printf("\n"); 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); // 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 // tearoffs. And also makes us not hold references to interfaces // on our wrapped natives that we are not actually using. // // XXX We may decide to not do this on *every* gc cycle. // Skip this part if XPConnect is shutting down. We get into // bad locking problems with the thread iteration otherwise. if(!self->GetXPConnect()->IsShuttingDown()) { PRLock* threadLock = XPCPerThreadData::GetLock(); if(threadLock) { // Do the marking... { // scoped lock nsAutoLock lock(threadLock); XPCPerThreadData* iterp = nsnull; XPCPerThreadData* thread; while(nsnull != (thread = XPCPerThreadData::IterateThreads(&iterp))) { XPCCallContext* ccxp = thread->GetCallContext(); while(ccxp) { // Deal with the strictness of callcontext that // complains if you ask for a tearoff when // it is in a state where the tearoff could not // possibly be valid. if(ccxp->CanGetTearOff()) { XPCWrappedNativeTearOff* to = ccxp->GetTearOff(); if(to) to->Mark(); } ccxp = ccxp->GetPrevCallContext(); } } } // Do the sweeping... XPCWrappedNativeScope::SweepAllWrappedNativeTearOffs(); } } // Now we need to kill the 'Dying' XPCWrappedNativeProtos. // We transfered these native objects to this table when their // JSObject's were finalized. We did not destroy them immediately // at that point because the ordering of JS finalization is not // deterministic and we did not yet know if any wrappers that // might still be referencing the protos where still yet to be // finalized and destroyed. We *do* know that the protos' // JSObjects would not have been finalized if there were any // wrappers that referenced the proto but where not themselves // slated for finalization in this gc cycle. So... at this point // we know that any and all wrappers that might have been // referencing the protos in the dying list are themselves dead. // So, we can safely delete all the protos in the list. self->mDyingWrappedNativeProtoMap-> Enumerate(DyingProtoKiller, nsnull); // mThreadRunningGC indicates that GC is running. // Clear it and notify waiters. { // scoped lock XPCAutoLock lock(self->GetMapLock()); NS_ASSERTION(self->mThreadRunningGC == PR_GetCurrentThread(), "bad state"); self->mThreadRunningGC = nsnull; xpc_NotifyAll(self->GetMapLock()); } break; } case JSGC_END: { // NOTE that this event happens outside of the gc lock in // the js engine. So this could be simultaneous with the // events above. // Do any deferred released of native objects. #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - Begin deferred Release of %d nsISupports pointers\n", self->mNativesToReleaseArray.Length()); #endif DoDeferredRelease(self->mNativesToReleaseArray); #ifdef XPC_TRACK_DEFERRED_RELEASES printf("XPC - End deferred Releases\n"); #endif break; } default: break; } } nsTArray<JSGCCallback> callbacks(self->extraGCCallbacks); for (PRUint32 i = 0; i < callbacks.Length(); ++i) { if (!callbacks[i](cx, status)) return JS_FALSE; } return JS_TRUE; }