Bug 1083851 part 2. Capture stacks at promise rejection time. r=nsm
authorBoris Zbarsky <bzbarsky@mit.edu>
Sun, 19 Oct 2014 22:27:12 -0400
changeset 211157 97ab895f34f890b5c2cf3d97ba5cf84d3e6abbd1
parent 211156 f79c624883f999d8587e1f6641d369dfa768ec73
child 211158 49b03421e1cf56d37db28bca6a0c818101d75ab5
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersnsm
bugs1083851
milestone36.0a1
Bug 1083851 part 2. Capture stacks at promise rejection time. r=nsm
dom/promise/Promise.cpp
dom/promise/Promise.h
--- 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