From 986a60874a5cc3c73bf7eb334b980f68c79ff4a2 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Tue, 24 Sep 2013 08:03:23 -0700 Subject: [PATCH] Bug 915613 - Introduce a mechanism to get a default context for a runtime, and use that in js-ctypes. r=jorendorff This causes us to use the SafeJSContext on main thread, and the singleton JSContext on workers. I opted for the slightly-heavier-weight dynamic callback, rather than just setting it as a member on the runtime, because we like to delay the creation of the SafeJSContext a bit, so I didn't want to spin it up right when we spin up the runtime. --- js/src/ctypes/CTypes.cpp | 37 ++----------------------------- js/src/ctypes/CTypes.h | 1 - js/src/jsfriendapi.cpp | 18 +++++++++++++++ js/src/jsfriendapi.h | 14 ++++++++++++ js/src/vm/Runtime.cpp | 1 + js/src/vm/Runtime.h | 2 ++ js/xpconnect/src/XPCJSRuntime.cpp | 11 ++++++++- js/xpconnect/src/xpcprivate.h | 1 + 8 files changed, 48 insertions(+), 37 deletions(-) diff --git a/js/src/ctypes/CTypes.cpp b/js/src/ctypes/CTypes.cpp index e3cd844e4a6..32478f4b326 100644 --- a/js/src/ctypes/CTypes.cpp +++ b/js/src/ctypes/CTypes.cpp @@ -164,7 +164,6 @@ namespace CType { static void Trace(JSTracer* trc, JSObject* obj); static void Finalize(JSFreeOp *fop, JSObject* obj); - static void FinalizeProtoClass(JSFreeOp *fop, JSObject* obj); static bool PrototypeGetter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp); @@ -468,7 +467,7 @@ static const JSClass sCTypeProtoClass = { "CType", JSCLASS_HAS_RESERVED_SLOTS(CTYPEPROTO_SLOTS), JS_PropertyStub, JS_DeletePropertyStub, JS_PropertyStub, JS_StrictPropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::FinalizeProtoClass, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, NULL, ConstructAbstract, NULL, ConstructAbstract }; @@ -3293,22 +3292,6 @@ CType::Finalize(JSFreeOp *fop, JSObject* obj) } } -void -CType::FinalizeProtoClass(JSFreeOp *fop, JSObject* obj) -{ - // Finalize the CTypeProto class. The only important bit here is our - // SLOT_CLOSURECX -- it contains the JSContext that was (lazily) instantiated - // for use with FunctionType closures. And if we're here, in this finalizer, - // we're guaranteed to not need it anymore. Note that this slot will only - // be set for the object (of class CTypeProto) ctypes.FunctionType.prototype. - jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSURECX); - if (JSVAL_IS_VOID(slot)) - return; - - JSContext* closureCx = static_cast(JSVAL_TO_PRIVATE(slot)); - JS_DestroyContextNoGC(closureCx); -} - void CType::Trace(JSTracer* trc, JSObject* obj) { @@ -6005,23 +5988,7 @@ CClosure::Create(JSContext* cx, JS_ASSERT(CType::IsCTypeProto(proto)); // Get a JSContext for use with the closure. - jsval slot = JS_GetReservedSlot(proto, SLOT_CLOSURECX); - if (!JSVAL_IS_VOID(slot)) { - // Use the existing JSContext. - cinfo->cx = static_cast(JSVAL_TO_PRIVATE(slot)); - JS_ASSERT(cinfo->cx); - } else { - // Lazily instantiate a new JSContext, and stash it on - // ctypes.FunctionType.prototype. - JSRuntime* runtime = JS_GetRuntime(cx); - cinfo->cx = JS_NewContext(runtime, 8192); - if (!cinfo->cx) { - JS_ReportOutOfMemory(cx); - return NULL; - } - - JS_SetReservedSlot(proto, SLOT_CLOSURECX, PRIVATE_TO_JSVAL(cinfo->cx)); - } + cinfo->cx = js::DefaultJSContext(JS_GetRuntime(cx)); // Prepare the error sentinel value. It's important to do this now, because // we might be unable to convert the value to the proper type. If so, we want diff --git a/js/src/ctypes/CTypes.h b/js/src/ctypes/CTypes.h index 588e2725bb9..d2e00f55ffa 100644 --- a/js/src/ctypes/CTypes.h +++ b/js/src/ctypes/CTypes.h @@ -335,7 +335,6 @@ enum CTypeProtoSlot { SLOT_UINT64PROTO = 10, // ctypes.UInt64.prototype object SLOT_CTYPES = 11, // ctypes object SLOT_OURDATAPROTO = 12, // the data prototype corresponding to this object - SLOT_CLOSURECX = 13, // JSContext for use with FunctionType closures CTYPEPROTO_SLOTS }; diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 43c26cdef64..45f95a70dc1 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1068,6 +1068,24 @@ js::detail::IdMatchesAtom(jsid id, JSAtom *atom) return id == INTERNED_STRING_TO_JSID(NULL, atom); } +JS_FRIEND_API(JSContext *) +js::DefaultJSContext(JSRuntime *rt) +{ + if (rt->defaultJSContextCallback) { + JSContext *cx = rt->defaultJSContextCallback(rt); + JS_ASSERT(cx); + return cx; + } + JS_ASSERT(rt->contextList.getFirst() == rt->contextList.getLast()); + return rt->contextList.getFirst(); +} + +JS_FRIEND_API(void) +js::SetDefaultJSContextCallback(JSRuntime *rt, DefaultJSContextCallback cb) +{ + rt->defaultJSContextCallback = cb; +} + JS_FRIEND_API(void) js::SetCTypesActivityCallback(JSRuntime *rt, CTypesActivityCallback cb) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index d9367a72319..9e648ae847e 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1507,6 +1507,20 @@ IsReadOnlyDateMethod(JS::IsAcceptableThis test, JS::NativeImpl method); extern JS_FRIEND_API(bool) IsTypedArrayThisCheck(JS::IsAcceptableThis test); +/* + * If the embedder has registered a default JSContext callback, returns the + * result of the callback. Otherwise, asserts that |rt| has exactly one + * JSContext associated with it, and returns that context. + */ +extern JS_FRIEND_API(JSContext *) +DefaultJSContext(JSRuntime *rt); + +typedef JSContext* +(* DefaultJSContextCallback)(JSRuntime *rt); + +JS_FRIEND_API(void) +SetDefaultJSContextCallback(JSRuntime *rt, DefaultJSContextCallback cb); + enum CTypesActivityType { CTYPES_CALL_BEGIN, CTYPES_CALL_END, diff --git a/js/src/vm/Runtime.cpp b/js/src/vm/Runtime.cpp index 898900f34cb..cbf9095e8c0 100644 --- a/js/src/vm/Runtime.cpp +++ b/js/src/vm/Runtime.cpp @@ -272,6 +272,7 @@ JSRuntime::JSRuntime(JSUseHelperThreads useHelperThreads) jitSupportsFloatingPoint(false), ionPcScriptCache(NULL), threadPool(this), + defaultJSContextCallback(NULL), ctypesActivityCallback(NULL), parallelWarmup(0), ionReturnOverride_(MagicValue(JS_ARG_POISON)), diff --git a/js/src/vm/Runtime.h b/js/src/vm/Runtime.h index 9acc8074e49..db6ff9ca578 100644 --- a/js/src/vm/Runtime.h +++ b/js/src/vm/Runtime.h @@ -1478,6 +1478,8 @@ struct JSRuntime : public JS::shadow::Runtime, js::ThreadPool threadPool; + js::DefaultJSContextCallback defaultJSContextCallback; + js::CTypesActivityCallback ctypesActivityCallback; // Non-zero if this is a parallel warmup execution. See diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 967af11362e..6ffc83a145b 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1209,7 +1209,15 @@ xpc::SimulateActivityCallback(bool aActive) XPCJSRuntime::ActivityCallback(XPCJSRuntime::Get(), aActive); } -//static +// static +JSContext* +XPCJSRuntime::DefaultJSContextCallback(JSRuntime *rt) +{ + MOZ_ASSERT(rt == Get()->Runtime()); + return Get()->GetJSContextStack()->GetSafeJSContext(); +} + +// static void XPCJSRuntime::ActivityCallback(void *arg, bool active) { @@ -3019,6 +3027,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) stack->sampleRuntime(runtime); #endif JS_SetAccumulateTelemetryCallback(runtime, AccumulateTelemetryCallback); + js::SetDefaultJSContextCallback(runtime, DefaultJSContextCallback); js::SetActivityCallback(runtime, ActivityCallback, this); js::SetCTypesActivityCallback(runtime, CTypesActivityCallback); JS_SetOperationCallback(runtime, OperationCallback); diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 72d58bf0b42..7523b931ffb 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -764,6 +764,7 @@ public: void AddContextCallback(xpcContextCallback cb); void RemoveContextCallback(xpcContextCallback cb); + static JSContext* DefaultJSContextCallback(JSRuntime *rt); static void ActivityCallback(void *arg, bool active); static void CTypesActivityCallback(JSContext *cx, js::CTypesActivityType type);