Bug 857648 part 3. Switch from using JS::DescribeStack to JS::CaptureCurrentStack for producing JSStackFrames. r=khuey
authorBoris Zbarsky <bzbarsky@mit.edu>
Fri, 04 Jul 2014 01:25:16 -0400
changeset 215736 842f6ec98e187df7f1b59644456b7a9a189bd3f4
parent 215735 ea43709c6675a4044cb73329abbbd6bf2b666438
child 215737 f33a93527a75cc352375884580fa473753b3575b
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey
bugs857648
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 857648 part 3. Switch from using JS::DescribeStack to JS::CaptureCurrentStack for producing JSStackFrames. r=khuey
dom/bindings/Exceptions.cpp
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -183,79 +183,16 @@ GetCurrentJSStack()
   }
 
   // Note that CreateStack only returns JS frames, so we're done here.
   return stack.forget();
 }
 
 namespace exceptions {
 
-class StackDescriptionOwner {
-public:
-  StackDescriptionOwner(JS::StackDescription* aDescription)
-    : mDescription(aDescription)
-  {
-    mozilla::HoldJSObjects(this);
-  }
-
-protected:
-  ~StackDescriptionOwner()
-  {
-    // Make sure to set mDescription to null before calling DropJSObjects, since
-    // in debug builds DropJSObjects try to trace us and we don't want to trace
-    // a dead StackDescription.
-    if (mDescription) {
-      JS::FreeStackDescription(nullptr, mDescription);
-      mDescription = nullptr;
-    }
-    mozilla::DropJSObjects(this);
-  }
-
-public:
-  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(StackDescriptionOwner)
-  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(StackDescriptionOwner)
-
-  JS::FrameDescription& FrameAt(size_t aIndex)
-  {
-    MOZ_ASSERT(aIndex < mDescription->nframes);
-    return mDescription->frames[aIndex];
-  }
-
-  unsigned NumFrames()
-  {
-    return mDescription->nframes;
-  }
-
-private:
-  JS::StackDescription* mDescription;
-};
-
-NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(StackDescriptionOwner, AddRef)
-NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(StackDescriptionOwner, Release)
-
-NS_IMPL_CYCLE_COLLECTION_CLASS(StackDescriptionOwner)
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(StackDescriptionOwner)
-  if (tmp->mDescription) {
-    JS::FreeStackDescription(nullptr, tmp->mDescription);
-    tmp->mDescription = nullptr;
-  }
-NS_IMPL_CYCLE_COLLECTION_UNLINK_END
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(StackDescriptionOwner)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
-NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(StackDescriptionOwner)
-  JS::StackDescription* desc = tmp->mDescription;
-  if (tmp->mDescription) {
-    for (size_t i = 0; i < desc->nframes; ++i) {
-      NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDescription->frames[i].markedLocation1());
-      NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDescription->frames[i].markedLocation2());
-    }
-  }
-NS_IMPL_CYCLE_COLLECTION_TRACE_END
-
 class StackFrame : public nsIStackFrame
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(StackFrame)
   NS_DECL_NSISTACKFRAME
 
   StackFrame(uint32_t aLanguage,
@@ -279,19 +216,20 @@ public:
 protected:
   virtual ~StackFrame();
 
   virtual bool IsJSFrame() const
   {
     return false;
   }
 
-  virtual int32_t GetLineno()
+  virtual nsresult GetLineno(int32_t* aLineNo)
   {
-    return mLineno;
+    *aLineNo = mLineno;
+    return NS_OK;
   }
 
   nsCOMPtr<nsIStackFrame> mCaller;
   nsString mFilename;
   nsString mFunname;
   int32_t mLineno;
   uint32_t mLanguage;
 };
@@ -321,69 +259,77 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIStackFrame)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 class JSStackFrame : public StackFrame
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(JSStackFrame, StackFrame)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(JSStackFrame,
+                                                         StackFrame)
 
-  // aStackDescription must not be null.  aIndex must be a valid index
-  // into aStackDescription.
-  JSStackFrame(StackDescriptionOwner* aStackDescription, size_t aIndex);
+  // aStack must not be null.
+  JSStackFrame(JS::Handle<JSObject*> aStack);
 
   static already_AddRefed<nsIStackFrame>
   CreateStack(JSContext* aCx, int32_t aMaxDepth = -1);
 
   NS_IMETHOD GetLanguageName(nsACString& aLanguageName) MOZ_OVERRIDE;
   NS_IMETHOD GetFilename(nsAString& aFilename) MOZ_OVERRIDE;
   NS_IMETHOD GetName(nsAString& aFunction) MOZ_OVERRIDE;
   NS_IMETHOD GetCaller(nsIStackFrame** aCaller) MOZ_OVERRIDE;
 
 protected:
   virtual bool IsJSFrame() const MOZ_OVERRIDE {
     return true;
   }
 
-  virtual int32_t GetLineno() MOZ_OVERRIDE;
+  virtual nsresult GetLineno(int32_t* aLineNo) MOZ_OVERRIDE;
 
 private:
   virtual ~JSStackFrame();
 
-  nsRefPtr<StackDescriptionOwner> mStackDescription;
-  size_t mIndex;
+  JS::Heap<JSObject*> mStack;
 
   bool mFilenameInitialized;
   bool mFunnameInitialized;
   bool mLinenoInitialized;
   bool mCallerInitialized;
 };
 
-JSStackFrame::JSStackFrame(StackDescriptionOwner* aStackDescription,
-                           size_t aIndex)
-  : mStackDescription(aStackDescription)
-  , mIndex(aIndex)
+JSStackFrame::JSStackFrame(JS::Handle<JSObject*> aStack)
+  : mStack(aStack)
   , mFilenameInitialized(false)
   , mFunnameInitialized(false)
   , mLinenoInitialized(false)
   , mCallerInitialized(false)
 {
-  MOZ_ASSERT(aStackDescription && aIndex < aStackDescription->NumFrames());
+  MOZ_ASSERT(mStack);
 
+  mozilla::HoldJSObjects(this);
   mLineno = 0;
   mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
 }
 
 JSStackFrame::~JSStackFrame()
 {
+  mozilla::DropJSObjects(this);
 }
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(JSStackFrame, StackFrame, mStackDescription)
+NS_IMPL_CYCLE_COLLECTION_CLASS(JSStackFrame)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(JSStackFrame, StackFrame)
+  tmp->mStack = nullptr;
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(JSStackFrame, StackFrame)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSStackFrame, StackFrame)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mStack)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_ADDREF_INHERITED(JSStackFrame, StackFrame)
 NS_IMPL_RELEASE_INHERITED(JSStackFrame, StackFrame)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(JSStackFrame)
 NS_INTERFACE_MAP_END_INHERITING(StackFrame)
 
 /* readonly attribute uint32_t language; */
@@ -405,20 +351,30 @@ NS_IMETHODIMP JSStackFrame::GetLanguageN
   aLanguageName.AssignLiteral("JavaScript");
   return NS_OK;
 }
 
 /* readonly attribute AString filename; */
 NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
 {
   if (!mFilenameInitialized) {
-    JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
-    if (const char *filename = desc.filename()) {
-      CopyUTF8toUTF16(filename, mFilename);
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> filenameVal(cx);
+    if (!JS_GetProperty(cx, stack, "source", &filenameVal) ||
+        !filenameVal.isString()) {
+      return NS_ERROR_UNEXPECTED;
     }
+    nsAutoJSString str;
+    if (!str.init(cx, filenameVal.toString())) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    mFilename = str;
     mFilenameInitialized = true;
   }
 
   return StackFrame::GetFilename(aFilename);
 }
 
 NS_IMETHODIMP StackFrame::GetFilename(nsAString& aFilename)
 {
@@ -431,19 +387,32 @@ NS_IMETHODIMP StackFrame::GetFilename(ns
 
   return NS_OK;
 }
 
 /* readonly attribute AString name; */
 NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
 {
   if (!mFunnameInitialized) {
-    JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
-    if (JSFlatString *name = desc.funDisplayName()) {
-      AssignJSFlatString(mFunname, name);
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> nameVal(cx);
+    // functionDisplayName can be null
+    if (!JS_GetProperty(cx, stack, "functionDisplayName", &nameVal) ||
+        (!nameVal.isString() && !nameVal.isNull())) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    if (nameVal.isString()) {
+      nsAutoJSString str;
+      if (!str.init(cx, nameVal.toString())) {
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      mFunname = str;
     }
     mFunnameInitialized = true;
   }
 
   return StackFrame::GetName(aFunction);
 }
 
 NS_IMETHODIMP StackFrame::GetName(nsAString& aFunction)
@@ -454,48 +423,66 @@ NS_IMETHODIMP StackFrame::GetName(nsAStr
   } else {
     aFunction.Assign(mFunname);
   }
 
   return NS_OK;
 }
 
 // virtual
-int32_t
-JSStackFrame::GetLineno()
+nsresult
+JSStackFrame::GetLineno(int32_t* aLineNo)
 {
   if (!mLinenoInitialized) {
-    JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
-    mLineno = desc.lineno();
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> lineVal(cx);
+    if (!JS_GetProperty(cx, stack, "line", &lineVal) ||
+        !lineVal.isNumber()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    mLineno = lineVal.toNumber();
     mLinenoInitialized = true;
   }
 
-  return StackFrame::GetLineno();
+  return StackFrame::GetLineno(aLineNo);
 }
 
 /* readonly attribute int32_t lineNumber; */
 NS_IMETHODIMP StackFrame::GetLineNumber(int32_t* aLineNumber)
 {
-  *aLineNumber = GetLineno();
-  return NS_OK;
+  return GetLineno(aLineNumber);
 }
 
 /* readonly attribute AUTF8String sourceLine; */
 NS_IMETHODIMP StackFrame::GetSourceLine(nsACString& aSourceLine)
 {
   aSourceLine.Truncate();
   return NS_OK;
 }
 
 /* readonly attribute nsIStackFrame caller; */
 NS_IMETHODIMP JSStackFrame::GetCaller(nsIStackFrame** aCaller)
 {
   if (!mCallerInitialized) {
-    if (mIndex + 1 < mStackDescription->NumFrames()) {
-      mCaller = new JSStackFrame(mStackDescription, mIndex+1);
+    ThreadsafeAutoJSContext cx;
+    JS::Rooted<JSObject*> stack(cx, mStack);
+    JS::ExposeObjectToActiveJS(mStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::Rooted<JS::Value> callerVal(cx);
+    if (!JS_GetProperty(cx, stack, "parent", &callerVal) ||
+        !callerVal.isObjectOrNull()) {
+      return NS_ERROR_UNEXPECTED;
+    }
+
+    if (callerVal.isObject()) {
+      JS::Rooted<JSObject*> caller(cx, &callerVal.toObject());
+      mCaller = new JSStackFrame(caller);
     } else {
       // Do we really need this dummy frame?  If so, we should document why... I
       // guess for symmetry with the "nothing on the stack" case, which returns
       // a single dummy frame?
       mCaller = new StackFrame();
     }
     mCallerInitialized = true;
   }
@@ -525,43 +512,47 @@ NS_IMETHODIMP StackFrame::ToString(nsACS
 
   nsString funname;
   rv = GetName(funname);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (funname.IsEmpty()) {
     funname.AssignLiteral("<TOP_LEVEL>");
   }
+
+  int32_t lineno;
+  rv = GetLineno(&lineno);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   static const char format[] = "%s frame :: %s :: %s :: line %d";
   _retval.AppendPrintf(format, frametype,
                        NS_ConvertUTF16toUTF8(filename).get(),
                        NS_ConvertUTF16toUTF8(funname).get(),
-                       GetLineno());
+                       lineno);
   return NS_OK;
 }
 
 /* static */ already_AddRefed<nsIStackFrame>
 JSStackFrame::CreateStack(JSContext* aCx, int32_t aMaxDepth)
 {
   static const unsigned MAX_FRAMES = 100;
   if (aMaxDepth < 0) {
     aMaxDepth = MAX_FRAMES;
   }
 
-  JS::StackDescription* desc = JS::DescribeStack(aCx, aMaxDepth);
-  if (!desc) {
+  JS::Rooted<JSObject*> stack(aCx);
+  if (!JS::CaptureCurrentStack(aCx, &stack, aMaxDepth)) {
     return nullptr;
   }
 
   nsCOMPtr<nsIStackFrame> first;
-  if (desc->nframes == 0) {
+  if (!stack) {
     first = new StackFrame();
   } else {
-    nsRefPtr<StackDescriptionOwner> descOwner = new StackDescriptionOwner(desc);
-    first = new JSStackFrame(descOwner, 0);
+    first = new JSStackFrame(stack);
   }
   return first.forget();
 }
 
 /* static */ already_AddRefed<nsIStackFrame>
 StackFrame::CreateStackFrameLocation(uint32_t aLanguage,
                                      const char* aFilename,
                                      const char* aFunctionName,