Bug 650353 - Implement Compartment-Per-Global in XPConnect. r=mrbkap

This commit is contained in:
Luke Wagner 2012-05-03 09:10:12 +02:00
parent 8cef559f81
commit b1d0aeeace
6 changed files with 86 additions and 200 deletions

View File

@ -47,22 +47,6 @@
#include "jsfriendapi.h" #include "jsfriendapi.h"
/***************************************************************************/ /***************************************************************************/
bool
xpc::PtrAndPrincipalHashKey::KeyEquals(const PtrAndPrincipalHashKey* aKey) const
{
if (aKey->mPtr != mPtr)
return false;
if (aKey->mPrincipal == mPrincipal)
return true;
bool equals;
if (NS_FAILED(mPrincipal->EqualsIgnoringDomain(aKey->mPrincipal, &equals))) {
NS_ERROR("we failed, guessing!");
return false;
}
return equals;
}
inline void inline void
XPCJSRuntime::AddVariantRoot(XPCTraceableVariant* variant) XPCJSRuntime::AddVariantRoot(XPCTraceableVariant* variant)

View File

@ -256,22 +256,27 @@ CompartmentDestroyedCallback(JSFreeOp *fop, JSCompartment *compartment)
XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance(); XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
if (!self) if (!self)
return; return;
XPCCompartmentSet &set = self->GetCompartmentSet();
// Get the current compartment private into an AutoPtr (which will do the
// cleanup for us), and null out the private (which may already be null).
nsAutoPtr<xpc::CompartmentPrivate> nsAutoPtr<xpc::CompartmentPrivate>
priv(static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment))); priv(static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment)));
if (!priv)
return;
JS_SetCompartmentPrivate(compartment, nsnull); JS_SetCompartmentPrivate(compartment, nsnull);
xpc::PtrAndPrincipalHashKey *key = priv->key; // JSD creates compartments in our runtime without going through our creation
XPCCompartmentMap &map = self->GetCompartmentMap(); // code. This means that those compartments aren't in our set, and don't have
#ifdef DEBUG // compartment privates. JSD is on the way out, so let's just handle that
JSCompartment *current = NULL; // case for now.
NS_ASSERTION(map.Get(key, &current), "no compartment?"); if (!priv) {
NS_ASSERTION(current == compartment, "compartment mismatch"); MOZ_ASSERT(!set.has(compartment));
#endif return;
map.Remove(key); }
// Remove the compartment from the set.
MOZ_ASSERT(set.has(compartment));
set.remove(compartment);
return;
} }
struct ObjectHolder : public JSDHashEntryHdr struct ObjectHolder : public JSDHashEntryHdr
@ -395,18 +400,6 @@ TraceDOMExpandos(nsPtrHashKey<JSObject> *expando, void *aClosure)
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
static PLDHashOperator
TraceCompartment(xpc::PtrAndPrincipalHashKey *aKey, JSCompartment *compartment, void *aClosure)
{
xpc::CompartmentPrivate *priv =
static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment));
if (priv->expandoMap)
priv->expandoMap->Enumerate(TraceExpandos, aClosure);
if (priv->domExpandoMap)
priv->domExpandoMap->EnumerateEntries(TraceDOMExpandos, aClosure);
return PL_DHASH_NEXT;
}
void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc) void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc)
{ {
JSContext *iter = nsnull; JSContext *iter = nsnull;
@ -430,7 +423,15 @@ void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc)
JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc); JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc);
// Trace compartments. // Trace compartments.
GetCompartmentMap().EnumerateRead(TraceCompartment, trc); XPCCompartmentSet &set = GetCompartmentSet();
for (XPCCompartmentRange r = set.all(); !r.empty(); r.popFront()) {
xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
JS_GetCompartmentPrivate(r.front());
if (priv->expandoMap)
priv->expandoMap->Enumerate(TraceExpandos, trc);
if (priv->domExpandoMap)
priv->domExpandoMap->EnumerateEntries(TraceDOMExpandos, trc);
}
} }
struct Closure struct Closure
@ -523,18 +524,6 @@ SuspectDOMExpandos(nsPtrHashKey<JSObject> *key, void *arg)
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
static PLDHashOperator
SuspectCompartment(xpc::PtrAndPrincipalHashKey *key, JSCompartment *compartment, void *arg)
{
xpc::CompartmentPrivate *priv =
static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment));
if (priv->expandoMap)
priv->expandoMap->EnumerateRead(SuspectExpandos, arg);
if (priv->domExpandoMap)
priv->domExpandoMap->EnumerateEntries(SuspectDOMExpandos, arg);
return PL_DHASH_NEXT;
}
void void
XPCJSRuntime::AddXPConnectRoots(nsCycleCollectionTraversalCallback &cb) XPCJSRuntime::AddXPConnectRoots(nsCycleCollectionTraversalCallback &cb)
{ {
@ -590,7 +579,15 @@ XPCJSRuntime::AddXPConnectRoots(nsCycleCollectionTraversalCallback &cb)
} }
// Suspect wrapped natives with expando objects. // Suspect wrapped natives with expando objects.
GetCompartmentMap().EnumerateRead(SuspectCompartment, &closure); XPCCompartmentSet &set = GetCompartmentSet();
for (XPCCompartmentRange r = set.all(); !r.empty(); r.popFront()) {
xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
JS_GetCompartmentPrivate(r.front());
if (priv->expandoMap)
priv->expandoMap->EnumerateRead(SuspectExpandos, &closure);
if (priv->domExpandoMap)
priv->domExpandoMap->EnumerateEntries(SuspectDOMExpandos, &closure);
}
} }
static JSDHashOperator static JSDHashOperator
@ -643,19 +640,6 @@ SweepExpandos(XPCWrappedNative *wn, JSObject *&expando, void *arg)
: PL_DHASH_NEXT; : PL_DHASH_NEXT;
} }
static PLDHashOperator
SweepCompartment(nsCStringHashKey& aKey, JSCompartment *compartment, void *aClosure)
{
MOZ_ASSERT(!aClosure);
xpc::CompartmentPrivate *priv =
static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment));
if (priv->waiverWrapperMap)
priv->waiverWrapperMap->Sweep();
if (priv->expandoMap)
priv->expandoMap->Enumerate(SweepExpandos, nsnull);
return PL_DHASH_NEXT;
}
/* static */ void /* static */ void
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status) XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
{ {
@ -732,8 +716,15 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status)
XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self); XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self);
// Sweep compartments. // Sweep compartments.
self->GetCompartmentMap().EnumerateRead((XPCCompartmentMap::EnumReadFunction) XPCCompartmentSet &set = self->GetCompartmentSet();
SweepCompartment, nsnull); for (XPCCompartmentRange r = set.all(); !r.empty(); r.popFront()) {
xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
JS_GetCompartmentPrivate(r.front());
if (priv->waiverWrapperMap)
priv->waiverWrapperMap->Sweep();
if (priv->expandoMap)
priv->expandoMap->Enumerate(SweepExpandos, NULL);
}
self->mDoingFinalization = true; self->mDoingFinalization = true;
break; break;
@ -2038,7 +2029,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
sizeof(ObjectHolder), 512)) sizeof(ObjectHolder), 512))
mJSHolders.ops = nsnull; mJSHolders.ops = nsnull;
mCompartmentMap.Init(); mCompartmentSet.init();
// Install a JavaScript 'debugger' keyword handler in debug builds only // Install a JavaScript 'debugger' keyword handler in debug builds only
#ifdef DEBUG #ifdef DEBUG
@ -2072,17 +2063,18 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
XPCJSRuntime* self = new XPCJSRuntime(aXPConnect); XPCJSRuntime* self = new XPCJSRuntime(aXPConnect);
if (self && if (self &&
self->GetJSRuntime() && self->GetJSRuntime() &&
self->GetWrappedJSMap() && self->GetWrappedJSMap() &&
self->GetWrappedJSClassMap() && self->GetWrappedJSClassMap() &&
self->GetIID2NativeInterfaceMap() && self->GetIID2NativeInterfaceMap() &&
self->GetClassInfo2NativeSetMap() && self->GetClassInfo2NativeSetMap() &&
self->GetNativeSetMap() && self->GetNativeSetMap() &&
self->GetThisTranslatorMap() && self->GetThisTranslatorMap() &&
self->GetNativeScriptableSharedMap() && self->GetNativeScriptableSharedMap() &&
self->GetDyingWrappedNativeProtoMap() && self->GetDyingWrappedNativeProtoMap() &&
self->GetMapLock() && self->GetMapLock() &&
self->GetCompartmentSet().initialized() &&
self->mWatchdogThread) { self->mWatchdogThread) {
return self; return self;
} }

View File

@ -82,26 +82,6 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(void *p)
return NS_OK; return NS_OK;
} }
struct TraverseExpandoObjectClosure
{
XPCWrappedNative *wn;
nsCycleCollectionTraversalCallback &cb;
};
static PLDHashOperator
TraverseExpandoObjects(xpc::PtrAndPrincipalHashKey *aKey, JSCompartment *compartment, void *aClosure)
{
TraverseExpandoObjectClosure *closure = static_cast<TraverseExpandoObjectClosure*>(aClosure);
xpc::CompartmentPrivate *priv =
static_cast<xpc::CompartmentPrivate *>(JS_GetCompartmentPrivate(compartment));
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(closure->cb, "XPCWrappedNative expando object");
closure->cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
priv->LookupExpandoObjectPreserveColor(closure->wn));
return PL_DHASH_NEXT;
}
NS_IMETHODIMP NS_IMETHODIMP
NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(void *p, NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(void *p,
nsCycleCollectionTraversalCallback &cb) nsCycleCollectionTraversalCallback &cb)
@ -144,8 +124,14 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(void *p,
if (tmp->MightHaveExpandoObject()) { if (tmp->MightHaveExpandoObject()) {
XPCJSRuntime *rt = tmp->GetRuntime(); XPCJSRuntime *rt = tmp->GetRuntime();
TraverseExpandoObjectClosure closure = { tmp, cb }; XPCCompartmentSet &set = rt->GetCompartmentSet();
rt->GetCompartmentMap().EnumerateRead(TraverseExpandoObjects, &closure); for (XPCCompartmentRange r = set.all(); !r.empty(); r.popFront()) {
xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
JS_GetCompartmentPrivate(r.front());
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "XPCWrappedNative expando object");
cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
priv->LookupExpandoObjectPreserveColor(tmp));
}
} }
// XPCWrappedNative keeps its native object alive. // XPCWrappedNative keeps its native object alive.

View File

@ -1135,28 +1135,14 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp,
CheckTypeInference(cx, clasp, principal); CheckTypeInference(cx, clasp, principal);
NS_ABORT_IF_FALSE(NS_IsMainThread(), "using a principal off the main thread?"); NS_ABORT_IF_FALSE(NS_IsMainThread(), "using a principal off the main thread?");
NS_ABORT_IF_FALSE(principal, "bad key");
XPCCompartmentMap& map = nsXPConnect::GetRuntimeInstance()->GetCompartmentMap(); xpc::CompartmentPrivate *priv = new xpc::CompartmentPrivate(wantXrays);
xpc::PtrAndPrincipalHashKey key(ptr, principal); if (!CreateNewCompartment(cx, clasp, principal, priv, global, compartment))
if (!map.Get(&key, compartment)) { return UnexpectedFailure(NS_ERROR_FAILURE);
xpc::PtrAndPrincipalHashKey *priv_key =
new xpc::PtrAndPrincipalHashKey(ptr, principal);
xpc::CompartmentPrivate *priv = new xpc::CompartmentPrivate(priv_key, wantXrays);
if (!CreateNewCompartment(cx, clasp, principal, priv,
global, compartment)) {
return UnexpectedFailure(NS_ERROR_FAILURE);
}
map.Put(&key, *compartment); XPCCompartmentSet& set = nsXPConnect::GetRuntimeInstance()->GetCompartmentSet();
} else { if (!set.put(*compartment))
js::AutoSwitchCompartment sc(cx, *compartment); return UnexpectedFailure(NS_ERROR_FAILURE);
JSObject *tempGlobal = JS_NewGlobalObject(cx, clasp);
if (!tempGlobal)
return UnexpectedFailure(NS_ERROR_FAILURE);
*global = tempGlobal;
}
#ifdef DEBUG #ifdef DEBUG
// Verify that the right trace hook is called. Note that this doesn't // Verify that the right trace hook is called. Note that this doesn't

View File

@ -100,15 +100,15 @@
#include "nsReadableUtils.h" #include "nsReadableUtils.h"
#include "nsXPIDLString.h" #include "nsXPIDLString.h"
#include "nsAutoJSValHolder.h" #include "nsAutoJSValHolder.h"
#include "js/HashTable.h"
#include "mozilla/GuardObjects.h" #include "mozilla/GuardObjects.h"
#include "mozilla/ReentrantMonitor.h" #include "mozilla/ReentrantMonitor.h"
#include "mozilla/Mutex.h" #include "mozilla/Mutex.h"
#include "nsDataHashtable.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "nsIJSContextStack.h" #include "nsIJSContextStack.h"
#include "nsIJSEngineTelemetryStats.h" #include "nsIJSEngineTelemetryStats.h"
#include "nsDeque.h"
#include "nsIConsoleService.h" #include "nsIConsoleService.h"
#include "nsIScriptError.h" #include "nsIScriptError.h"
@ -123,6 +123,8 @@
#include "nsHashKeys.h" #include "nsHashKeys.h"
#include "nsWrapperCache.h" #include "nsWrapperCache.h"
#include "nsStringBuffer.h" #include "nsStringBuffer.h"
#include "nsDataHashtable.h"
#include "nsDeque.h"
#include "nsIScriptSecurityManager.h" #include "nsIScriptSecurityManager.h"
#include "nsNetUtil.h" #include "nsNetUtil.h"
@ -235,75 +237,11 @@ extern const char XPC_SCRIPT_ERROR_CONTRACTID[];
extern const char XPC_ID_CONTRACTID[]; extern const char XPC_ID_CONTRACTID[];
extern const char XPC_XPCONNECT_CONTRACTID[]; extern const char XPC_XPCONNECT_CONTRACTID[];
namespace xpc { typedef js::HashSet<JSCompartment *,
js::DefaultHasher<JSCompartment *>,
js::SystemAllocPolicy> XPCCompartmentSet;
class PtrAndPrincipalHashKey : public PLDHashEntryHdr typedef XPCCompartmentSet::Range XPCCompartmentRange;
{
public:
typedef PtrAndPrincipalHashKey *KeyType;
typedef const PtrAndPrincipalHashKey *KeyTypePointer;
PtrAndPrincipalHashKey(const PtrAndPrincipalHashKey *aKey)
: mPtr(aKey->mPtr), mPrincipal(aKey->mPrincipal),
mSavedHash(aKey->mSavedHash)
{
MOZ_COUNT_CTOR(PtrAndPrincipalHashKey);
}
PtrAndPrincipalHashKey(nsISupports *aPtr, nsIPrincipal *aPrincipal)
: mPtr(aPtr), mPrincipal(aPrincipal)
{
MOZ_COUNT_CTOR(PtrAndPrincipalHashKey);
nsCOMPtr<nsIURI> uri;
aPrincipal->GetURI(getter_AddRefs(uri));
mSavedHash = uri
? NS_SecurityHashURI(uri)
: (NS_PTR_TO_UINT32(mPtr.get()) >> 2);
}
~PtrAndPrincipalHashKey()
{
MOZ_COUNT_DTOR(PtrAndPrincipalHashKey);
}
PtrAndPrincipalHashKey* GetKey() const
{
return const_cast<PtrAndPrincipalHashKey*>(this);
}
const PtrAndPrincipalHashKey* GetKeyPointer() const { return this; }
inline bool KeyEquals(const PtrAndPrincipalHashKey* aKey) const;
static const PtrAndPrincipalHashKey*
KeyToPointer(PtrAndPrincipalHashKey* aKey) { return aKey; }
static PLDHashNumber HashKey(const PtrAndPrincipalHashKey* aKey)
{
return aKey->mSavedHash;
}
nsISupports* GetPtr()
{
return mPtr;
}
enum { ALLOW_MEMMOVE = true };
protected:
nsCOMPtr<nsISupports> mPtr;
nsCOMPtr<nsIPrincipal> mPrincipal;
// During shutdown, when we GC, we need to remove these keys from the hash
// table. However, computing the saved hash, NS_SecurityHashURI calls back
// into XPCOM (which is illegal during shutdown). In order to avoid this,
// we compute the hash up front, so when we're in GC during shutdown, we
// don't have to call into XPCOM.
PLDHashNumber mSavedHash;
};
}
// This map is only used on the main thread.
typedef nsDataHashtable<xpc::PtrAndPrincipalHashKey, JSCompartment *> XPCCompartmentMap;
/***************************************************************************/ /***************************************************************************/
// Useful macros... // Useful macros...
@ -677,8 +615,8 @@ public:
XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const
{return mDetachedWrappedNativeProtoMap;} {return mDetachedWrappedNativeProtoMap;}
XPCCompartmentMap& GetCompartmentMap() XPCCompartmentSet& GetCompartmentSet()
{return mCompartmentMap;} {return mCompartmentSet;}
XPCLock* GetMapLock() const {return mMapLock;} XPCLock* GetMapLock() const {return mMapLock;}
@ -822,7 +760,7 @@ private:
XPCNativeScriptableSharedMap* mNativeScriptableSharedMap; XPCNativeScriptableSharedMap* mNativeScriptableSharedMap;
XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap; XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap; XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap;
XPCCompartmentMap mCompartmentMap; XPCCompartmentSet mCompartmentSet;
XPCLock* mMapLock; XPCLock* mMapLock;
PRThread* mThreadRunningGC; PRThread* mThreadRunningGC;
nsTArray<nsXPCWrappedJS*> mWrappedJSToReleaseArray; nsTArray<nsXPCWrappedJS*> mWrappedJSToReleaseArray;
@ -4434,16 +4372,14 @@ namespace xpc {
struct CompartmentPrivate struct CompartmentPrivate
{ {
CompartmentPrivate(PtrAndPrincipalHashKey *key, bool wantXrays) CompartmentPrivate(bool wantXrays)
: key(key), : wantXrays(wantXrays)
wantXrays(wantXrays)
{ {
MOZ_COUNT_CTOR(xpc::CompartmentPrivate); MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
} }
~CompartmentPrivate(); ~CompartmentPrivate();
nsAutoPtr<PtrAndPrincipalHashKey> key;
bool wantXrays; bool wantXrays;
nsAutoPtr<JSObject2JSObjectMap> waiverWrapperMap; nsAutoPtr<JSObject2JSObjectMap> waiverWrapperMap;
// NB: we don't want this map to hold a strong reference to the wrapper. // NB: we don't want this map to hold a strong reference to the wrapper.

View File

@ -90,6 +90,8 @@ struct DebugOnly
value--; value--;
} }
T *operator&() { return &value; }
operator T&() { return value; } operator T&() { return value; }
operator const T&() const { return value; } operator const T&() const { return value; }