Bug 971673: Replace ScriptSettingsStack's array with a C++-stack-allocated linked list. r=bholley

This commit is contained in:
Jim Blandy 2014-06-12 23:24:13 -07:00
parent 6635575960
commit b546e7c699
2 changed files with 82 additions and 104 deletions

View File

@ -20,65 +20,51 @@
namespace mozilla {
namespace dom {
class ScriptSettingsStack;
static mozilla::ThreadLocal<ScriptSettingsStack*> sScriptSettingsTLS;
ScriptSettingsStackEntry ScriptSettingsStackEntry::NoJSAPISingleton;
static mozilla::ThreadLocal<ScriptSettingsStackEntry*> sScriptSettingsTLS;
class ScriptSettingsStack {
public:
static ScriptSettingsStack& Ref() {
return *sScriptSettingsTLS.get();
}
ScriptSettingsStack() {};
void Push(ScriptSettingsStackEntry* aSettings) {
// The bottom-most entry must always be a candidate entry point.
MOZ_ASSERT_IF(mStack.Length() == 0 || mStack.LastElement()->NoJSAPI(),
aSettings->mIsCandidateEntryPoint);
mStack.AppendElement(aSettings);
static ScriptSettingsStackEntry* Top() {
return sScriptSettingsTLS.get();
}
void PushNoJSAPI() {
mStack.AppendElement(&ScriptSettingsStackEntry::NoJSAPISingleton);
static void Push(ScriptSettingsStackEntry *aEntry) {
MOZ_ASSERT(!aEntry->mOlder);
// Whenever JSAPI use is disabled, the next stack entry pushed must
// always be a candidate entry point.
MOZ_ASSERT_IF(!Top() || Top()->NoJSAPI(), aEntry->mIsCandidateEntryPoint);
aEntry->mOlder = Top();
sScriptSettingsTLS.set(aEntry);
}
void Pop() {
MOZ_ASSERT(mStack.Length() > 0);
mStack.RemoveElementAt(mStack.Length() - 1);
static void Pop(ScriptSettingsStackEntry *aEntry) {
MOZ_ASSERT(aEntry == Top());
sScriptSettingsTLS.set(aEntry->mOlder);
}
ScriptSettingsStackEntry* Incumbent() {
if (!mStack.Length()) {
return nullptr;
}
return mStack.LastElement();
}
nsIGlobalObject* IncumbentGlobal() {
ScriptSettingsStackEntry *entry = Incumbent();
static nsIGlobalObject* IncumbentGlobal() {
ScriptSettingsStackEntry *entry = Top();
return entry ? entry->mGlobalObject : nullptr;
}
ScriptSettingsStackEntry* EntryPoint() {
if (!mStack.Length())
static ScriptSettingsStackEntry* EntryPoint() {
ScriptSettingsStackEntry *entry = Top();
if (!entry) {
return nullptr;
for (int i = mStack.Length() - 1; i >= 0; --i) {
if (mStack[i]->mIsCandidateEntryPoint) {
return mStack[i];
}
}
while (entry) {
if (entry->mIsCandidateEntryPoint)
return entry;
entry = entry->mOlder;
}
MOZ_CRASH("Non-empty stack should always have an entry point");
}
nsIGlobalObject* EntryGlobal() {
static nsIGlobalObject* EntryGlobal() {
ScriptSettingsStackEntry *entry = EntryPoint();
return entry ? entry->mGlobalObject : nullptr;
}
private:
// These pointers are caller-owned.
nsTArray<ScriptSettingsStackEntry*> mStack;
};
void
@ -91,16 +77,44 @@ InitScriptSettings()
}
}
ScriptSettingsStack* ptr = new ScriptSettingsStack();
sScriptSettingsTLS.set(ptr);
sScriptSettingsTLS.set(nullptr);
}
void DestroyScriptSettings()
{
ScriptSettingsStack* ptr = sScriptSettingsTLS.get();
MOZ_ASSERT(ptr);
sScriptSettingsTLS.set(nullptr);
delete ptr;
MOZ_ASSERT(sScriptSettingsTLS.get() == nullptr);
}
ScriptSettingsStackEntry::ScriptSettingsStackEntry(nsIGlobalObject *aGlobal,
bool aCandidate)
: mGlobalObject(aGlobal)
, mIsCandidateEntryPoint(aCandidate)
, mOlder(nullptr)
{
MOZ_ASSERT(mGlobalObject);
MOZ_ASSERT(mGlobalObject->GetGlobalJSObject(),
"Must have an actual JS global for the duration on the stack");
MOZ_ASSERT(JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()),
"No outer windows allowed");
ScriptSettingsStack::Push(this);
}
// This constructor is only for use by AutoNoJSAPI.
ScriptSettingsStackEntry::ScriptSettingsStackEntry()
: mGlobalObject(nullptr)
, mIsCandidateEntryPoint(true)
, mOlder(nullptr)
{
ScriptSettingsStack::Push(this);
}
ScriptSettingsStackEntry::~ScriptSettingsStackEntry()
{
// We must have an actual JS global for the entire time this is on the stack.
MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
ScriptSettingsStack::Pop(this);
}
// This mostly gets the entry global, but doesn't entirely match the spec in
@ -115,7 +129,7 @@ BrokenGetEntryGlobal()
// must be no entry global on the stack.
JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
if (!cx) {
MOZ_ASSERT(ScriptSettingsStack::Ref().EntryGlobal() == nullptr);
MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr);
return nullptr;
}
@ -135,7 +149,7 @@ GetIncumbentGlobal()
// global either.
JSContext *cx = nsContentUtils::GetCurrentJSContextForThread();
if (!cx) {
MOZ_ASSERT(ScriptSettingsStack::Ref().EntryGlobal() == nullptr);
MOZ_ASSERT(ScriptSettingsStack::EntryGlobal() == nullptr);
return nullptr;
}
@ -149,17 +163,17 @@ GetIncumbentGlobal()
// Ok, nothing from the JS engine. Let's use whatever's on the
// explicit stack.
return ScriptSettingsStack::Ref().IncumbentGlobal();
return ScriptSettingsStack::IncumbentGlobal();
}
nsIPrincipal*
GetWebIDLCallerPrincipal()
{
MOZ_ASSERT(NS_IsMainThread());
ScriptSettingsStackEntry *entry = ScriptSettingsStack::Ref().EntryPoint();
ScriptSettingsStackEntry *entry = ScriptSettingsStack::EntryPoint();
// If we have an entry point that is not the NoJSAPI singleton, we know it
// must be an AutoEntryScript.
// If we have an entry point that is not NoJSAPI, we know it must be an
// AutoEntryScript.
if (!entry || entry->NoJSAPI()) {
return nullptr;
}
@ -248,37 +262,21 @@ AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
: AutoJSAPI(aCx ? aCx : FindJSContext(aGlobalObject), aIsMainThread, /* aSkipNullAc = */ true)
, ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
, mAc(cx(), aGlobalObject->GetGlobalJSObject())
, mStack(ScriptSettingsStack::Ref())
, mWebIDLCallerPrincipal(nullptr)
{
MOZ_ASSERT(aGlobalObject);
MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
mStack.Push(this);
}
AutoEntryScript::~AutoEntryScript()
{
MOZ_ASSERT(mStack.Incumbent() == this);
mStack.Pop();
}
AutoIncumbentScript::AutoIncumbentScript(nsIGlobalObject* aGlobalObject)
: ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ false)
, mStack(ScriptSettingsStack::Ref())
, mCallerOverride(nsContentUtils::GetCurrentJSContextForThread())
{
mStack.Push(this);
}
AutoIncumbentScript::~AutoIncumbentScript()
{
MOZ_ASSERT(mStack.Incumbent() == this);
mStack.Pop();
}
AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread)
: mStack(ScriptSettingsStack::Ref())
: ScriptSettingsStackEntry()
{
MOZ_ASSERT_IF(nsContentUtils::GetCurrentJSContextForThread(),
!JS_IsExceptionPending(nsContentUtils::GetCurrentJSContextForThread()));
@ -286,12 +284,6 @@ AutoNoJSAPI::AutoNoJSAPI(bool aIsMainThread)
mCxPusher.construct(static_cast<JSContext*>(nullptr),
/* aAllowNull = */ true);
}
mStack.PushNoJSAPI();
}
AutoNoJSAPI::~AutoNoJSAPI()
{
mStack.Pop();
}
} // namespace dom

View File

@ -66,33 +66,25 @@ inline JSObject& IncumbentJSGlobal()
}
class ScriptSettingsStack;
struct ScriptSettingsStackEntry {
class ScriptSettingsStackEntry {
friend class ScriptSettingsStack;
public:
ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate);
~ScriptSettingsStackEntry();
bool NoJSAPI() { return !mGlobalObject; }
protected:
nsCOMPtr<nsIGlobalObject> mGlobalObject;
bool mIsCandidateEntryPoint;
ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate)
: mGlobalObject(aGlobal)
, mIsCandidateEntryPoint(aCandidate)
{
MOZ_ASSERT(mGlobalObject);
MOZ_ASSERT(mGlobalObject->GetGlobalJSObject(),
"Must have an actual JS global for the duration on the stack");
MOZ_ASSERT(JS_IsGlobalObject(mGlobalObject->GetGlobalJSObject()),
"No outer windows allowed");
}
~ScriptSettingsStackEntry() {
// We must have an actual JS global for the entire time this is on the stack.
MOZ_ASSERT_IF(mGlobalObject, mGlobalObject->GetGlobalJSObject());
}
bool NoJSAPI() { return this == &NoJSAPISingleton; }
static ScriptSettingsStackEntry NoJSAPISingleton;
private:
ScriptSettingsStackEntry() : mGlobalObject(nullptr)
, mIsCandidateEntryPoint(true)
{}
// This constructor is only for use by AutoNoJSAPI.
friend class AutoNoJSAPI;
ScriptSettingsStackEntry();
ScriptSettingsStackEntry *mOlder;
};
/*
@ -172,7 +164,6 @@ public:
bool aIsMainThread = NS_IsMainThread(),
// Note: aCx is mandatory off-main-thread.
JSContext* aCx = nullptr);
~AutoEntryScript();
void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) {
mWebIDLCallerPrincipal = aPrincipal;
@ -180,7 +171,6 @@ public:
private:
JSAutoCompartment mAc;
dom::ScriptSettingsStack& mStack;
// It's safe to make this a weak pointer, since it's the subject principal
// when we go on the stack, so can't go away until after we're gone. In
// particular, this is only used from the CallSetup constructor, and only in
@ -198,9 +188,7 @@ private:
class AutoIncumbentScript : protected ScriptSettingsStackEntry {
public:
AutoIncumbentScript(nsIGlobalObject* aGlobalObject);
~AutoIncumbentScript();
private:
dom::ScriptSettingsStack& mStack;
JS::AutoHideScriptedCaller mCallerOverride;
};
@ -212,12 +200,10 @@ private:
*
* This class may not be instantiated if an exception is pending.
*/
class AutoNoJSAPI {
class AutoNoJSAPI : protected ScriptSettingsStackEntry {
public:
AutoNoJSAPI(bool aIsMainThread = NS_IsMainThread());
~AutoNoJSAPI();
private:
dom::ScriptSettingsStack& mStack;
mozilla::Maybe<AutoCxPusher> mCxPusher;
};