Bug 1492011 store raw JS arguments on Console separately from ConsoleCallData r=baku

so that all ConsoleCallData members can be destroyed on either thread.

ArgumentData::mArguments hold the same references that
ConsoleCallData::mCopiedArguments held previously.  The name change is because
the references are merely stored rather than any deep copy of objects.

Differential Revision: https://phabricator.services.mozilla.com/D67989

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Karl Tomlinson 2020-03-25 00:47:15 +00:00
Родитель f49d456f4a
Коммит ddd9e31d2f
2 изменённых файлов: 101 добавлений и 77 удалений

Просмотреть файл

@ -88,8 +88,8 @@ class ConsoleCallData final {
public:
NS_INLINE_DECL_REFCOUNTING(ConsoleCallData)
ConsoleCallData()
: mMethodName(Console::MethodLog),
ConsoleCallData(Console::MethodName aName, const nsAString& aString)
: mMethodName(aName),
mTimeStamp(JS_Now() / PR_USEC_PER_MSEC),
mStartTimerValue(0),
mStartTimerStatus(Console::eTimerUnknown),
@ -99,33 +99,9 @@ class ConsoleCallData final {
mIDType(eUnknown),
mOuterIDNumber(0),
mInnerIDNumber(0),
mMethodString(aString),
mStatus(eUnused) {}
bool Initialize(JSContext* aCx, Console::MethodName aName,
const nsAString& aString,
const Sequence<JS::Value>& aArguments, Console* aConsole) {
AssertIsOnOwningThread();
MOZ_ASSERT(aConsole);
// We must be registered before doing any JS operation otherwise it can
// happen that mCopiedArguments are not correctly traced.
aConsole->StoreCallData(this);
mMethodName = aName;
mMethodString = aString;
mGlobal = JS::CurrentGlobalOrNull(aCx);
for (uint32_t i = 0; i < aArguments.Length(); ++i) {
if (NS_WARN_IF(!mCopiedArguments.AppendElement(aArguments[i]))) {
aConsole->UnstoreCallData(this);
return false;
}
}
return true;
}
void SetIDs(uint64_t aOuterID, uint64_t aInnerID) {
MOZ_ASSERT(mIDType == eUnknown);
@ -153,39 +129,11 @@ class ConsoleCallData final {
mAddonId = addonId;
}
bool PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const {
AssertIsOnOwningThread();
for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
if (NS_WARN_IF(!aSequence.AppendElement(mCopiedArguments[i], fallible))) {
return false;
}
}
return true;
}
void Trace(const TraceCallbacks& aCallbacks, void* aClosure) {
ConsoleCallData* tmp = this;
for (uint32_t i = 0; i < mCopiedArguments.Length(); ++i) {
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCopiedArguments[i])
}
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal);
}
void AssertIsOnOwningThread() const {
NS_ASSERT_OWNINGTHREAD(ConsoleCallData);
}
JS::Heap<JSObject*> mGlobal;
// This is a copy of the arguments we received from the DOM bindings. Console
// object traces them because this ConsoleCallData calls
// RegisterConsoleCallData() in the Initialize().
nsTArray<JS::Heap<JS::Value>> mCopiedArguments;
Console::MethodName mMethodName;
const Console::MethodName mMethodName;
int64_t mTimeStamp;
// These values are set in the owning thread and they contain the timestamp of
@ -235,7 +183,7 @@ class ConsoleCallData final {
nsString mAddonId;
nsString mMethodString;
const nsString mMethodString;
// Stack management is complicated, because we want to do it as
// lazily as possible. Therefore, we have the following behavior:
@ -881,14 +829,9 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Console)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Console)
for (uint32_t i = 0; i < tmp->mCallDataStorage.Length(); ++i) {
tmp->mCallDataStorage[i]->Trace(aCallbacks, aClosure);
for (uint32_t i = 0; i < tmp->mArgumentStorage.length(); ++i) {
tmp->mArgumentStorage[i].Trace(aCallbacks, aClosure);
}
for (uint32_t i = 0; i < tmp->mCallDataStoragePending.Length(); ++i) {
tmp->mCallDataStoragePending[i]->Trace(aCallbacks, aClosure);
}
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Console)
@ -1023,8 +966,8 @@ void Console::Shutdown() {
mTimerRegistry.Clear();
mCounterRegistry.Clear();
ClearStorage();
mCallDataStorage.Clear();
mCallDataStoragePending.Clear();
mStatus = eShuttingDown;
}
@ -1057,7 +1000,10 @@ Console::Observe(nsISupports* aSubject, const char* aTopic,
return NS_OK;
}
void Console::ClearStorage() { mCallDataStorage.Clear(); }
void Console::ClearStorage() {
mCallDataStorage.Clear();
mArgumentStorage.clearAndFree();
}
#define METHOD(name, string) \
/* static */ void Console::name(const GlobalObject& aGlobal, \
@ -1377,12 +1323,11 @@ void Console::MethodInternal(JSContext* aCx, MethodName aMethodName,
AssertIsOnOwningThread();
RefPtr<ConsoleCallData> callData(new ConsoleCallData());
ConsoleCommon::ClearException ce(aCx);
if (NS_WARN_IF(!callData->Initialize(aCx, aMethodName, aMethodString, aData,
this))) {
RefPtr<ConsoleCallData> callData =
new ConsoleCallData(aMethodName, aMethodString);
if (!StoreCallData(aCx, callData, aData)) {
return;
}
@ -2434,18 +2379,30 @@ JSObject* Console::GetOrCreateSandbox(JSContext* aCx,
return mSandbox->GetJSObject();
}
void Console::StoreCallData(ConsoleCallData* aCallData) {
bool Console::StoreCallData(JSContext* aCx, ConsoleCallData* aCallData,
const Sequence<JS::Value>& aArguments) {
AssertIsOnOwningThread();
if (NS_WARN_IF(!mArgumentStorage.growBy(1))) {
return false;
}
if (!mArgumentStorage.end()[-1].Initialize(aCx, aArguments)) {
mArgumentStorage.shrinkBy(1);
return false;
}
MOZ_ASSERT(aCallData);
MOZ_ASSERT(!mCallDataStorage.Contains(aCallData));
MOZ_ASSERT(!mCallDataStoragePending.Contains(aCallData));
mCallDataStorage.AppendElement(aCallData);
MOZ_ASSERT(mCallDataStorage.Length() == mArgumentStorage.length());
if (mCallDataStorage.Length() > STORAGE_MAX_EVENTS) {
RefPtr<ConsoleCallData> callData = mCallDataStorage[0];
mCallDataStorage.RemoveElementAt(0);
mArgumentStorage.erase(&mArgumentStorage[0]);
MOZ_ASSERT(callData->mStatus != ConsoleCallData::eToBeDeleted);
@ -2457,20 +2414,26 @@ void Console::StoreCallData(ConsoleCallData* aCallData) {
mCallDataStoragePending.AppendElement(callData);
}
}
return true;
}
void Console::UnstoreCallData(ConsoleCallData* aCallData) {
AssertIsOnOwningThread();
MOZ_ASSERT(aCallData);
MOZ_ASSERT(mCallDataStorage.Length() == mArgumentStorage.length());
MOZ_ASSERT(!mCallDataStoragePending.Contains(aCallData));
size_t index = mCallDataStorage.IndexOf(aCallData);
// It can be that mCallDataStorage has been already cleaned in case the
// processing of the argument of some Console methods triggers the
// window.close().
if (index == mCallDataStorage.NoIndex) {
return;
}
mCallDataStorage.RemoveElement(aCallData);
mCallDataStorage.RemoveElementAt(index);
mArgumentStorage.erase(&mArgumentStorage[index]);
}
void Console::ReleaseCallData(ConsoleCallData* aCallData) {
@ -2525,16 +2488,16 @@ void Console::RetrieveConsoleEvents(JSContext* aCx,
JS::Rooted<JSObject*> targetScope(aCx, JS::CurrentGlobalOrNull(aCx));
for (uint32_t i = 0; i < mCallDataStorage.Length(); ++i) {
for (uint32_t i = 0; i < mArgumentStorage.length(); ++i) {
JS::Rooted<JS::Value> value(aCx);
JS::Rooted<JSObject*> sequenceScope(aCx, mCallDataStorage[i]->mGlobal);
JS::Rooted<JSObject*> sequenceScope(aCx, mArgumentStorage[i].Global());
JSAutoRealm ar(aCx, sequenceScope);
Sequence<JS::Value> sequence;
SequenceRooter<JS::Value> arguments(aCx, &sequence);
if (!mCallDataStorage[i]->PopulateArgumentsSequence(sequence)) {
if (!mArgumentStorage[i].PopulateArgumentsSequence(sequence)) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
@ -2967,5 +2930,41 @@ uint32_t Console::InternalLogLevelToInteger(MethodName aName) const {
return 0;
}
bool Console::ArgumentData::Initialize(JSContext* aCx,
const Sequence<JS::Value>& aArguments) {
mGlobal = JS::CurrentGlobalOrNull(aCx);
for (uint32_t i = 0; i < aArguments.Length(); ++i) {
if (NS_WARN_IF(!mArguments.AppendElement(aArguments[i]))) {
return false;
}
}
return true;
}
void Console::ArgumentData::Trace(const TraceCallbacks& aCallbacks,
void* aClosure) {
ArgumentData* tmp = this;
for (uint32_t i = 0; i < mArguments.Length(); ++i) {
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mArguments[i])
}
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mGlobal)
}
bool Console::ArgumentData::PopulateArgumentsSequence(
Sequence<JS::Value>& aSequence) const {
AssertIsOnOwningThread();
for (uint32_t i = 0; i < mArguments.Length(); ++i) {
if (NS_WARN_IF(!aSequence.AppendElement(mArguments[i], fallible))) {
return false;
}
}
return true;
}
} // namespace dom
} // namespace mozilla

Просмотреть файл

@ -226,7 +226,9 @@ class Console final : public nsIObserver, public nsSupportsWeakReference {
void ProcessCallData(JSContext* aCx, ConsoleCallData* aData,
const Sequence<JS::Value>& aArguments);
void StoreCallData(ConsoleCallData* aData);
// Returns true on success; otherwise false.
bool StoreCallData(JSContext* aCx, ConsoleCallData* aCallData,
const Sequence<JS::Value>& aArguments);
void UnstoreCallData(ConsoleCallData* aData);
@ -427,6 +429,23 @@ class Console final : public nsIObserver, public nsSupportsWeakReference {
uint32_t InternalLogLevelToInteger(MethodName aName) const;
class ArgumentData {
public:
bool Initialize(JSContext* aCx, const Sequence<JS::Value>& aArguments);
void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
bool PopulateArgumentsSequence(Sequence<JS::Value>& aSequence) const;
JSObject* Global() const { return mGlobal; }
private:
void AssertIsOnOwningThread() const {
NS_ASSERT_OWNINGTHREAD(ArgumentData);
}
NS_DECL_OWNINGTHREAD;
JS::Heap<JSObject*> mGlobal;
nsTArray<JS::Heap<JS::Value>> mArguments;
};
// Owning/CC thread only
nsCOMPtr<nsIGlobalObject> mGlobal;
// These nsCOMPtr are touched on main thread only.
@ -449,6 +468,12 @@ class Console final : public nsIObserver, public nsSupportsWeakReference {
// its JSValues also if 'officially' this ConsoleCallData must be removed from
// the storage.
nsTArray<RefPtr<ConsoleCallData>> mCallDataStoragePending;
// These are references to the arguments we received in each call
// from the DOM bindings.
// Vector<T> supports non-memmovable types such as ArgumentData
// (without any need to jump through hoops like
// MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR_FOR_TEMPLATE for nsTArray).
Vector<ArgumentData> mArgumentStorage;
RefPtr<AnyCallback> mConsoleEventNotifier;