Bug 1083851 part 2. Capture stacks at promise rejection time. r=nsm
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -281,31 +281,33 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResolveCallbacks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRejectCallbacks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Promise)
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mResult)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAllocationStack)
+ NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRejectionStack)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(Promise)
NS_IMPL_CYCLE_COLLECTING_RELEASE(Promise)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Promise)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
Promise::Promise(nsIGlobalObject* aGlobal)
: mGlobal(aGlobal)
, mResult(JS::UndefinedValue())
, mAllocationStack(nullptr)
+ , mRejectionStack(nullptr)
, mState(Pending)
, mTaskPending(false)
, mHadRejectCallback(false)
, mResolvePending(false)
{
MOZ_ASSERT(mGlobal);
mozilla::HoldJSObjects(this);
@@ -392,16 +394,19 @@ Promise::JSCallback(JSContext* aCx, unsi
v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA);
PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
if (task == PromiseCallback::Resolve) {
promise->MaybeResolveInternal(aCx, args.get(0));
} else {
promise->MaybeRejectInternal(aCx, args.get(0));
+ if (!promise->CaptureStack(aCx, promise->mRejectionStack)) {
+ return false;
+ }
}
return true;
}
/*
* Common bits of (JSCallbackThenableResolver/JSCallbackThenableRejecter).
* Resolves/rejects the Promise if it is ok to do so, based on whether either of
@@ -602,17 +607,21 @@ Promise::Reject(const GlobalObject& aGlo
{
nsCOMPtr<nsIGlobalObject> global =
do_QueryInterface(aGlobal.GetAsSupports());
if (!global) {
aRv.Throw(NS_ERROR_UNEXPECTED);
return nullptr;
}
- return Reject(global, aGlobal.Context(), aValue, aRv);
+ nsRefPtr<Promise> p = Reject(global, aGlobal.Context(), aValue, aRv);
+ if (p) {
+ p->mRejectionStack = p->mAllocationStack;
+ }
+ return p.forget();
}
/* static */ already_AddRefed<Promise>
Promise::Reject(nsIGlobalObject* aGlobal, JSContext* aCx,
JS::Handle<JS::Value> aValue, ErrorResult& aRv)
{
nsRefPtr<Promise> promise = Create(aGlobal, aRv);
if (aRv.Failed()) {
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -294,16 +294,21 @@ private:
nsTArray<nsRefPtr<PromiseCallback> > mResolveCallbacks;
nsTArray<nsRefPtr<PromiseCallback> > mRejectCallbacks;
JS::Heap<JS::Value> mResult;
// A stack that shows where this promise was allocated, if there was
// JS running at the time. Otherwise null.
JS::Heap<JSObject*> mAllocationStack;
+ // mRejectionStack is only set when the promise is rejected directly from
+ // script, by calling Promise.reject() or the rejection callback we pass to
+ // the PromiseInit function. Promises that are rejected internally do not
+ // have a rejection stack.
+ JS::Heap<JSObject*> mRejectionStack;
PromiseState mState;
bool mTaskPending;
bool mHadRejectCallback;
bool mResolvePending;
// If a rejected promise on a worker has no reject callbacks attached, it
// needs to know when the worker is shutting down, to report the error on the