Bug 845545: Part 2 - Refactor context creation callbacks. r=bholley,mccr8

This commit is contained in:
Kyle Huey 2013-08-03 16:55:39 -07:00
parent b09945aa3b
commit 147c3dee65
11 changed files with 124 additions and 56 deletions

View File

@ -854,14 +854,11 @@ JS_IsInRequest(JSRuntime *rt)
#endif
}
JS_PUBLIC_API(JSContextCallback)
JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback)
JS_PUBLIC_API(void)
JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback, void *data)
{
JSContextCallback old;
old = rt->cxCallback;
rt->cxCallback = cxCallback;
return old;
rt->cxCallbackData = data;
}
JS_PUBLIC_API(JSContext *)

View File

@ -965,7 +965,7 @@ typedef enum JSContextOp {
* and return true in this case.
*/
typedef JSBool
(* JSContextCallback)(JSContext *cx, unsigned contextOp);
(* JSContextCallback)(JSContext *cx, unsigned contextOp, void *data);
typedef enum JSGCStatus {
JSGC_BEGIN,
@ -1909,8 +1909,8 @@ class JSAutoCheckRequest
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
extern JS_PUBLIC_API(JSContextCallback)
JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback);
extern JS_PUBLIC_API(void)
JS_SetContextCallback(JSRuntime *rt, JSContextCallback cxCallback, void *data);
extern JS_PUBLIC_API(JSContext *)
JS_NewContext(JSRuntime *rt, size_t stackChunkSize);

View File

@ -211,7 +211,7 @@ js::NewContext(JSRuntime *rt, size_t stackChunkSize)
}
JSContextCallback cxCallback = rt->cxCallback;
if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW)) {
if (cxCallback && !cxCallback(cx, JSCONTEXT_NEW, rt->cxCallbackData)) {
DestroyContext(cx, DCM_NEW_FAILED);
return NULL;
}
@ -241,7 +241,8 @@ js::DestroyContext(JSContext *cx, DestroyContextMode mode)
* JSCONTEXT_DESTROY callback is not allowed to fail and must
* return true.
*/
JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY));
JS_ALWAYS_TRUE(cxCallback(cx, JSCONTEXT_DESTROY,
rt->cxCallbackData));
}
}

View File

@ -923,6 +923,7 @@ struct JSRuntime : public JS::shadow::Runtime,
/* Context create/destroy callback. */
JSContextCallback cxCallback;
void *cxCallbackData;
/* Compartment destroy callback. */
JSDestroyCompartmentCallback destroyCompartmentCallback;

View File

@ -7,18 +7,23 @@
#include "nsISupports.idl"
[ptr] native JSRuntime(JSRuntime);
[ptr] native JSContext(JSContext);
native xpcGCCallback(xpcGCCallback);
native xpcContextCallback(xpcContextCallback);
%{C++
typedef void
(* xpcGCCallback)(JSGCStatus status);
typedef bool
(* xpcContextCallback)(JSContext* cx, unsigned operation);
%}
interface nsIBackstagePass;
[uuid(996ef894-88cd-42c8-ac2d-28c60970daaf)]
[uuid( 2ac111f2-e492-488e-85df-353c453e98f3)]
interface nsIJSRuntimeService : nsISupports
{
readonly attribute JSRuntime runtime;
@ -29,4 +34,11 @@ interface nsIJSRuntimeService : nsISupports
*/
[noscript, notxpcom] void registerGCCallback(in xpcGCCallback func);
[noscript, notxpcom] void unregisterGCCallback(in xpcGCCallback func);
/**
* Register additional context callback which will run after the
* standard XPConnect callback.
*/
[noscript, notxpcom] void registerContextCallback(in xpcContextCallback func);
[noscript, notxpcom] void unregisterContextCallback(in xpcContextCallback func);
};

View File

@ -1450,9 +1450,6 @@ nsXPCFunctionThisTranslator::TranslateThis(nsISupports *aInitialThis,
#endif
// ContextCallback calls are chained
static JSContextCallback gOldJSContextCallback;
void
XPCShellErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep)
{
@ -1466,12 +1463,9 @@ XPCShellErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep)
xpc::SystemErrorReporterExternal(cx, message, rep);
}
static JSBool
static bool
ContextCallback(JSContext *cx, unsigned contextOp)
{
if (gOldJSContextCallback && !gOldJSContextCallback(cx, contextOp))
return false;
if (contextOp == JSCONTEXT_NEW) {
JS_SetErrorReporter(cx, XPCShellErrorReporter);
JS_SetOperationCallback(cx, XPCShellOperationCallback);
@ -1646,7 +1640,7 @@ main(int argc, char **argv, char **envp)
return 1;
}
gOldJSContextCallback = JS_SetContextCallback(rt, ContextCallback);
rtsvc->RegisterContextCallback(ContextCallback);
cx = JS_NewContext(rt, 8192);
if (!cx) {

View File

@ -201,19 +201,24 @@ DetachedWrappedNativeProtoMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
return PL_DHASH_NEXT;
}
// GCCallback calls are chained
static JSBool
ContextCallback(JSContext *cx, unsigned operation)
bool
XPCJSRuntime::CustomContextCallback(JSContext *cx, unsigned operation)
{
XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
if (self) {
if (operation == JSCONTEXT_NEW) {
if (!self->OnJSContextNew(cx))
return false;
} else if (operation == JSCONTEXT_DESTROY) {
delete XPCContext::GetXPCContext(cx);
if (operation == JSCONTEXT_NEW) {
if (!OnJSContextNew(cx)) {
return false;
}
} else if (operation == JSCONTEXT_DESTROY) {
delete XPCContext::GetXPCContext(cx);
}
nsTArray<xpcContextCallback> callbacks(extraContextCallbacks);
for (uint32_t i = 0; i < callbacks.Length(); ++i) {
if (!callbacks[i](cx, operation)) {
return false;
}
}
return true;
}
@ -605,24 +610,6 @@ XPCJSRuntime::GCSliceCallback(JSRuntime *rt,
void
XPCJSRuntime::CustomGCCallback(JSGCStatus status)
{
switch (status) {
case JSGC_BEGIN:
{
// We seem to sometime lose the unrooted global flag. Restore it
// here. FIXME: bug 584495.
JSContext *iter = nullptr;
while (JSContext *acx = JS_ContextIterator(Runtime(), &iter)) {
if (!js::HasUnrootedGlobal(acx))
JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL);
}
break;
}
case JSGC_END:
{
break;
}
}
nsTArray<xpcGCCallback> callbacks(extraGCCallbacks);
for (uint32_t i = 0; i < callbacks.Length(); ++i)
callbacks[i](status);
@ -2748,7 +2735,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
#else
JS_SetNativeStackQuota(runtime, 128 * sizeof(size_t) * 1024);
#endif
JS_SetContextCallback(runtime, ContextCallback);
JS_SetDestroyCompartmentCallback(runtime, CompartmentDestroyedCallback);
JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
@ -2869,9 +2855,6 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx)
if (!xpc)
return false;
// we want to mark the global object ourselves since we use a different color
JS_ToggleOptions(cx, JSOPTION_UNROOTED_GLOBAL);
return true;
}
@ -3064,6 +3047,23 @@ XPCJSRuntime::RemoveGCCallback(xpcGCCallback cb)
}
}
void
XPCJSRuntime::AddContextCallback(xpcContextCallback cb)
{
NS_ASSERTION(cb, "null callback");
extraContextCallbacks.AppendElement(cb);
}
void
XPCJSRuntime::RemoveContextCallback(xpcContextCallback cb)
{
NS_ASSERTION(cb, "null callback");
bool found = extraContextCallbacks.RemoveElement(cb);
if (!found) {
NS_ERROR("Removing a callback which was never added.");
}
}
JSObject *
XPCJSRuntime::GetJunkScope()
{

View File

@ -1242,6 +1242,20 @@ nsXPConnect::UnregisterGCCallback(xpcGCCallback func)
mRuntime->RemoveGCCallback(func);
}
/* [noscript, notxpcom] void registerContextCallback(in xpcContextCallback func); */
NS_IMETHODIMP_(void)
nsXPConnect::RegisterContextCallback(xpcContextCallback func)
{
mRuntime->AddContextCallback(func);
}
/* [noscript, notxpcom] void unregisterContextCallback(in xpcContextCallback func); */
NS_IMETHODIMP_(void)
nsXPConnect::UnregisterContextCallback(xpcContextCallback func)
{
mRuntime->RemoveContextCallback(func);
}
#ifdef MOZ_JSDEBUGGER
void
nsXPConnect::CheckForDebugMode(JSRuntime *rt)

View File

@ -731,6 +731,7 @@ public:
void PrepareForCollection() MOZ_OVERRIDE;
void CustomGCCallback(JSGCStatus status) MOZ_OVERRIDE;
bool CustomContextCallback(JSContext *cx, unsigned operation) MOZ_OVERRIDE;
static void GCSliceCallback(JSRuntime *rt,
JS::GCProgress progress,
const JS::GCDescription &desc);
@ -817,6 +818,8 @@ public:
void AddGCCallback(xpcGCCallback cb);
void RemoveGCCallback(xpcGCCallback cb);
void AddContextCallback(xpcContextCallback cb);
void RemoveContextCallback(xpcContextCallback cb);
static void ActivityCallback(void *arg, JSBool active);
static void CTypesActivityCallback(JSContext *cx,
@ -864,6 +867,7 @@ private:
XPCRootSetElem *mWrappedJSRoots;
XPCRootSetElem *mObjectHolderRoots;
nsTArray<xpcGCCallback> extraGCCallbacks;
nsTArray<xpcContextCallback> extraContextCallbacks;
nsRefPtr<WatchdogManager> mWatchdogManager;
JS::GCSliceCallback mPrevGCSliceCallback;
JSObject* mJunkScope;

View File

@ -478,10 +478,10 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(uint32_t aMaxbytes,
bool aExpectUnrootedGlobals)
: mGCThingCycleCollectorGlobal(sGCThingCycleCollectorGlobal),
mJSZoneCycleCollectorGlobal(sJSZoneCycleCollectorGlobal),
mJSRuntime(nullptr)
mJSRuntime(nullptr),
mExpectUnrootedGlobals(aExpectUnrootedGlobals)
#ifdef DEBUG
, mObjectToUnlink(nullptr)
, mExpectUnrootedGlobals(aExpectUnrootedGlobals)
#endif
{
mJSRuntime = JS_NewRuntime(aMaxbytes, aUseHelperThreads);
@ -494,6 +494,7 @@ CycleCollectedJSRuntime::CycleCollectedJSRuntime(uint32_t aMaxbytes,
}
JS_SetGrayGCRootsTracer(mJSRuntime, TraceGrayJS, this);
JS_SetGCCallback(mJSRuntime, GCCallback, this);
JS_SetContextCallback(mJSRuntime, ContextCallback, this);
mJSHolders.Init(512);
@ -795,6 +796,18 @@ CycleCollectedJSRuntime::GCCallback(JSRuntime* aRuntime,
self->OnGC(aStatus);
}
/* static */ JSBool
CycleCollectedJSRuntime::ContextCallback(JSContext* aContext,
unsigned aOperation,
void* aData)
{
CycleCollectedJSRuntime* self = static_cast<CycleCollectedJSRuntime*>(aData);
MOZ_ASSERT(JS_GetRuntime(aContext) == self->Runtime());
return self->OnContext(aContext, aOperation);
}
struct JsGcTracer : public TraceCallbacks
{
virtual void Trace(JS::Heap<JS::Value> *p, const char *name, void *closure) const MOZ_OVERRIDE {
@ -1186,6 +1199,18 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
switch (aStatus) {
case JSGC_BEGIN:
{
// XXXkhuey do we still need this?
// We seem to sometime lose the unrooted global flag. Restore it
// here. FIXME: bug 584495.
if (mExpectUnrootedGlobals){
JSContext* iter = nullptr;
while (JSContext* acx = JS_ContextIterator(Runtime(), &iter)) {
if (!js::HasUnrootedGlobal(acx)) {
JS_ToggleOptions(acx, JSOPTION_UNROOTED_GLOBAL);
}
}
}
break;
}
case JSGC_END:
@ -1212,3 +1237,15 @@ CycleCollectedJSRuntime::OnGC(JSGCStatus aStatus)
CustomGCCallback(aStatus);
}
bool
CycleCollectedJSRuntime::OnContext(JSContext* aCx, unsigned aOperation)
{
if (mExpectUnrootedGlobals && aOperation == JSCONTEXT_NEW) {
// XXXkhuey bholley is going to make this go away, but for now XPConnect
// needs it.
JS_ToggleOptions(aCx, JSOPTION_UNROOTED_GLOBAL);
}
return CustomContextCallback(aCx, aOperation);
}

View File

@ -84,7 +84,7 @@ class CycleCollectedJSRuntime
protected:
CycleCollectedJSRuntime(uint32_t aMaxbytes,
JSUseHelperThreads aUseHelperThreads,
bool aExpectRootedGlobals);
bool aExpectUnrootedGlobals);
virtual ~CycleCollectedJSRuntime();
JSRuntime* Runtime() const
@ -100,6 +100,10 @@ protected:
virtual void TraceAdditionalNativeGrayRoots(JSTracer* aTracer) = 0;
virtual void CustomGCCallback(JSGCStatus aStatus) {}
virtual bool CustomContextCallback(JSContext* aCx, unsigned aOperation)
{
return true; // Don't block context creation.
}
private:
@ -150,6 +154,8 @@ private:
static void TraceBlackJS(JSTracer* aTracer, void* aData);
static void TraceGrayJS(JSTracer* aTracer, void* aData);
static void GCCallback(JSRuntime* aRuntime, JSGCStatus aStatus, void* aData);
static JSBool ContextCallback(JSContext* aCx, unsigned aOperation,
void* aData);
virtual void TraceNativeBlackRoots(JSTracer* aTracer) { };
void TraceNativeGrayRoots(JSTracer* aTracer);
@ -162,6 +168,7 @@ private:
void FinalizeDeferredThings(DeferredFinalizeType aType);
void OnGC(JSGCStatus aStatus);
bool OnContext(JSContext* aCx, unsigned aOperation);
public:
void AddJSHolder(void* aHolder, nsScriptObjectTracer* aTracer);
@ -214,9 +221,10 @@ private:
nsRefPtr<IncrementalFinalizeRunnable> mFinalizeRunnable;
bool mExpectUnrootedGlobals;
#ifdef DEBUG
void* mObjectToUnlink;
bool mExpectUnrootedGlobals;
#endif
};