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"
/***************************************************************************/
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
XPCJSRuntime::AddVariantRoot(XPCTraceableVariant* variant)

View File

@ -256,22 +256,27 @@ CompartmentDestroyedCallback(JSFreeOp *fop, JSCompartment *compartment)
XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
if (!self)
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>
priv(static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment)));
if (!priv)
return;
priv(static_cast<xpc::CompartmentPrivate*>(JS_GetCompartmentPrivate(compartment)));
JS_SetCompartmentPrivate(compartment, nsnull);
xpc::PtrAndPrincipalHashKey *key = priv->key;
XPCCompartmentMap &map = self->GetCompartmentMap();
#ifdef DEBUG
JSCompartment *current = NULL;
NS_ASSERTION(map.Get(key, &current), "no compartment?");
NS_ASSERTION(current == compartment, "compartment mismatch");
#endif
map.Remove(key);
// JSD creates compartments in our runtime without going through our creation
// code. This means that those compartments aren't in our set, and don't have
// compartment privates. JSD is on the way out, so let's just handle that
// case for now.
if (!priv) {
MOZ_ASSERT(!set.has(compartment));
return;
}
// Remove the compartment from the set.
MOZ_ASSERT(set.has(compartment));
set.remove(compartment);
return;
}
struct ObjectHolder : public JSDHashEntryHdr
@ -395,18 +400,6 @@ TraceDOMExpandos(nsPtrHashKey<JSObject> *expando, void *aClosure)
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)
{
JSContext *iter = nsnull;
@ -430,7 +423,15 @@ void XPCJSRuntime::TraceXPConnectRoots(JSTracer *trc)
JS_DHashTableEnumerate(&mJSHolders, TraceJSHolder, trc);
// 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
@ -523,18 +524,6 @@ SuspectDOMExpandos(nsPtrHashKey<JSObject> *key, void *arg)
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
XPCJSRuntime::AddXPConnectRoots(nsCycleCollectionTraversalCallback &cb)
{
@ -590,7 +579,15 @@ XPCJSRuntime::AddXPConnectRoots(nsCycleCollectionTraversalCallback &cb)
}
// 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
@ -643,19 +640,6 @@ SweepExpandos(XPCWrappedNative *wn, JSObject *&expando, void *arg)
: 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
XPCJSRuntime::GCCallback(JSRuntime *rt, JSGCStatus status)
{
@ -732,8 +716,15 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp *fop, JSFinalizeStatus status)
XPCWrappedNativeScope::StartFinalizationPhaseOfGC(fop, self);
// Sweep compartments.
self->GetCompartmentMap().EnumerateRead((XPCCompartmentMap::EnumReadFunction)
SweepCompartment, nsnull);
XPCCompartmentSet &set = self->GetCompartmentSet();
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;
break;
@ -2038,7 +2029,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
sizeof(ObjectHolder), 512))
mJSHolders.ops = nsnull;
mCompartmentMap.Init();
mCompartmentSet.init();
// Install a JavaScript 'debugger' keyword handler in debug builds only
#ifdef DEBUG
@ -2072,17 +2063,18 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
XPCJSRuntime* self = new XPCJSRuntime(aXPConnect);
if (self &&
self->GetJSRuntime() &&
self->GetWrappedJSMap() &&
self->GetWrappedJSClassMap() &&
self->GetIID2NativeInterfaceMap() &&
self->GetClassInfo2NativeSetMap() &&
self->GetNativeSetMap() &&
self->GetThisTranslatorMap() &&
self->GetNativeScriptableSharedMap() &&
self->GetDyingWrappedNativeProtoMap() &&
self->GetMapLock() &&
if (self &&
self->GetJSRuntime() &&
self->GetWrappedJSMap() &&
self->GetWrappedJSClassMap() &&
self->GetIID2NativeInterfaceMap() &&
self->GetClassInfo2NativeSetMap() &&
self->GetNativeSetMap() &&
self->GetThisTranslatorMap() &&
self->GetNativeScriptableSharedMap() &&
self->GetDyingWrappedNativeProtoMap() &&
self->GetMapLock() &&
self->GetCompartmentSet().initialized() &&
self->mWatchdogThread) {
return self;
}

View File

@ -82,26 +82,6 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Unlink(void *p)
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_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(void *p,
nsCycleCollectionTraversalCallback &cb)
@ -144,8 +124,14 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrappedNative)::Traverse(void *p,
if (tmp->MightHaveExpandoObject()) {
XPCJSRuntime *rt = tmp->GetRuntime();
TraverseExpandoObjectClosure closure = { tmp, cb };
rt->GetCompartmentMap().EnumerateRead(TraverseExpandoObjects, &closure);
XPCCompartmentSet &set = rt->GetCompartmentSet();
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.

View File

@ -1135,28 +1135,14 @@ xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp,
CheckTypeInference(cx, clasp, principal);
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::PtrAndPrincipalHashKey key(ptr, principal);
if (!map.Get(&key, compartment)) {
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);
}
xpc::CompartmentPrivate *priv = new xpc::CompartmentPrivate(wantXrays);
if (!CreateNewCompartment(cx, clasp, principal, priv, global, compartment))
return UnexpectedFailure(NS_ERROR_FAILURE);
map.Put(&key, *compartment);
} else {
js::AutoSwitchCompartment sc(cx, *compartment);
JSObject *tempGlobal = JS_NewGlobalObject(cx, clasp);
if (!tempGlobal)
return UnexpectedFailure(NS_ERROR_FAILURE);
*global = tempGlobal;
}
XPCCompartmentSet& set = nsXPConnect::GetRuntimeInstance()->GetCompartmentSet();
if (!set.put(*compartment))
return UnexpectedFailure(NS_ERROR_FAILURE);
#ifdef DEBUG
// Verify that the right trace hook is called. Note that this doesn't

View File

@ -100,15 +100,15 @@
#include "nsReadableUtils.h"
#include "nsXPIDLString.h"
#include "nsAutoJSValHolder.h"
#include "js/HashTable.h"
#include "mozilla/GuardObjects.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/Mutex.h"
#include "nsDataHashtable.h"
#include "nsThreadUtils.h"
#include "nsIJSContextStack.h"
#include "nsIJSEngineTelemetryStats.h"
#include "nsDeque.h"
#include "nsIConsoleService.h"
#include "nsIScriptError.h"
@ -123,6 +123,8 @@
#include "nsHashKeys.h"
#include "nsWrapperCache.h"
#include "nsStringBuffer.h"
#include "nsDataHashtable.h"
#include "nsDeque.h"
#include "nsIScriptSecurityManager.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_XPCONNECT_CONTRACTID[];
namespace xpc {
typedef js::HashSet<JSCompartment *,
js::DefaultHasher<JSCompartment *>,
js::SystemAllocPolicy> XPCCompartmentSet;
class PtrAndPrincipalHashKey : public PLDHashEntryHdr
{
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;
typedef XPCCompartmentSet::Range XPCCompartmentRange;
/***************************************************************************/
// Useful macros...
@ -677,8 +615,8 @@ public:
XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const
{return mDetachedWrappedNativeProtoMap;}
XPCCompartmentMap& GetCompartmentMap()
{return mCompartmentMap;}
XPCCompartmentSet& GetCompartmentSet()
{return mCompartmentSet;}
XPCLock* GetMapLock() const {return mMapLock;}
@ -822,7 +760,7 @@ private:
XPCNativeScriptableSharedMap* mNativeScriptableSharedMap;
XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap;
XPCCompartmentMap mCompartmentMap;
XPCCompartmentSet mCompartmentSet;
XPCLock* mMapLock;
PRThread* mThreadRunningGC;
nsTArray<nsXPCWrappedJS*> mWrappedJSToReleaseArray;
@ -4434,16 +4372,14 @@ namespace xpc {
struct CompartmentPrivate
{
CompartmentPrivate(PtrAndPrincipalHashKey *key, bool wantXrays)
: key(key),
wantXrays(wantXrays)
CompartmentPrivate(bool wantXrays)
: wantXrays(wantXrays)
{
MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
}
~CompartmentPrivate();
nsAutoPtr<PtrAndPrincipalHashKey> key;
bool wantXrays;
nsAutoPtr<JSObject2JSObjectMap> waiverWrapperMap;
// NB: we don't want this map to hold a strong reference to the wrapper.

View File

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