From f1fc5726d1acfde155c551eba17c933f998952b7 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 7 Jan 2014 19:53:18 -0500 Subject: [PATCH] Bug 932837 part 3. Make JSStackFrame get information from the JS stack lazily. r=khuey --- dom/base/DOMException.cpp | 4 + dom/bindings/Exceptions.cpp | 193 +++++++++++++++++++++++++++--------- 2 files changed, 148 insertions(+), 49 deletions(-) diff --git a/dom/base/DOMException.cpp b/dom/base/DOMException.cpp index 11f53083db00..da7d20a853f9 100644 --- a/dom/base/DOMException.cpp +++ b/dom/base/DOMException.cpp @@ -151,6 +151,8 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(Exception) NS_IMPL_CYCLE_COLLECTION_CLASS(Exception) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Exception) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInner) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -160,6 +162,8 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Exception) NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Exception) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mInner) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER tmp->mThrownJSVal.setNull(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/dom/bindings/Exceptions.cpp b/dom/bindings/Exceptions.cpp index 774c3e0b79e9..51734c99f07c 100644 --- a/dom/bindings/Exceptions.cpp +++ b/dom/bindings/Exceptions.cpp @@ -232,6 +232,11 @@ public: return mDescription->frames[aIndex]; } + unsigned NumFrames() + { + return mDescription->nframes; + } + private: JS::StackDescription* mDescription; }; @@ -262,10 +267,15 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END class JSStackFrame : public nsIStackFrame { public: - NS_DECL_ISUPPORTS + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_CLASS(JSStackFrame) NS_DECL_NSISTACKFRAME - JSStackFrame(); + // A null aStackDescription or an aIndex that's out of range for the + // number of frames aStackDescription has will mean that the + // JSStackFrame will never look at the stack description. Instead, + // it is expected to be initialized by the caller as needed. + JSStackFrame(StackDescriptionOwner* aStackDescription, size_t aIndex); virtual ~JSStackFrame(); static already_AddRefed @@ -282,20 +292,48 @@ private: return mLanguage == nsIProgrammingLanguage::JAVASCRIPT; } + const char* GetFilename(); + const char* GetFunname(); + int32_t GetLineno(); + + nsRefPtr mStackDescription; nsCOMPtr mCaller; + // Cached values char* mFilename; char* mFunname; int32_t mLineno; uint32_t mLanguage; + + size_t mIndex; + + bool mFilenameInitialized; + bool mFunnameInitialized; + bool mLinenoInitialized; }; -JSStackFrame::JSStackFrame() +JSStackFrame::JSStackFrame(StackDescriptionOwner* aStackDescription, + size_t aIndex) : mFilename(nullptr), mFunname(nullptr), - mLineno(0), - mLanguage(nsIProgrammingLanguage::UNKNOWN) -{} + mLineno(0) +{ + if (aStackDescription && aIndex < aStackDescription->NumFrames()) { + mStackDescription = aStackDescription; + mIndex = aIndex; + mFilenameInitialized = false; + mFunnameInitialized = false; + mLinenoInitialized = false; + mLanguage = nsIProgrammingLanguage::JAVASCRIPT; + } else { + MOZ_ASSERT(!mStackDescription); + mIndex = 0; + mFilenameInitialized = true; + mFunnameInitialized = true; + mLinenoInitialized = true; + mLanguage = nsIProgrammingLanguage::UNKNOWN; + } +} JSStackFrame::~JSStackFrame() { @@ -307,7 +345,15 @@ JSStackFrame::~JSStackFrame() } } -NS_IMPL_ISUPPORTS1(JSStackFrame, nsIStackFrame) +NS_IMPL_CYCLE_COLLECTION_2(JSStackFrame, mStackDescription, mCaller) + +NS_IMPL_CYCLE_COLLECTING_ADDREF(JSStackFrame) +NS_IMPL_CYCLE_COLLECTING_RELEASE(JSStackFrame) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSStackFrame) + NS_INTERFACE_MAP_ENTRY(nsIStackFrame) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END /* readonly attribute uint32_t language; */ NS_IMETHODIMP JSStackFrame::GetLanguage(uint32_t* aLanguage) @@ -331,14 +377,35 @@ NS_IMETHODIMP JSStackFrame::GetLanguageName(char** aLanguageName) return NS_OK; } +const char* +JSStackFrame::GetFilename() +{ + if (!mFilenameInitialized) { + JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex); + if (desc.script()) { + AutoJSContext cx; + JSAutoCompartment ac(cx, desc.script()); + const char* filename = JS_GetScriptFilename(cx, desc.script()); + if (filename) { + mFilename = + (char*)nsMemory::Clone(filename, sizeof(char)*(strlen(filename)+1)); + } + } + mFilenameInitialized = true; + } + + return mFilename; +} + /* readonly attribute string filename; */ NS_IMETHODIMP JSStackFrame::GetFilename(char** aFilename) { NS_ENSURE_ARG_POINTER(aFilename); - if (mFilename) { - *aFilename = (char*) nsMemory::Clone(mFilename, - sizeof(char)*(strlen(mFilename)+1)); + const char* filename = GetFilename(); + if (filename) { + *aFilename = (char*) nsMemory::Clone(filename, + sizeof(char)*(strlen(filename)+1)); } else { *aFilename = nullptr; } @@ -346,14 +413,42 @@ NS_IMETHODIMP JSStackFrame::GetFilename(char** aFilename) return NS_OK; } +const char* +JSStackFrame::GetFunname() +{ + if (!mFunnameInitialized) { + JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex); + if (desc.fun() && desc.script()) { + AutoJSContext cx; + JSAutoCompartment ac(cx, desc.script()); + JS::Rooted fun(cx, desc.fun()); + JS::Rooted funid(cx, JS_GetFunctionDisplayId(fun)); + if (funid) { + size_t length = JS_GetStringEncodingLength(cx, funid); + if (length != size_t(-1)) { + mFunname = static_cast(nsMemory::Alloc(length + 1)); + if (mFunname) { + JS_EncodeStringToBuffer(cx, funid, mFunname, length); + mFunname[length] = '\0'; + } + } + } + } + mFunnameInitialized = true; + } + + return mFunname; +} + /* readonly attribute string name; */ NS_IMETHODIMP JSStackFrame::GetName(char** aFunction) { NS_ENSURE_ARG_POINTER(aFunction); - if (mFunname) { - *aFunction = (char*) nsMemory::Clone(mFunname, - sizeof(char)*(strlen(mFunname)+1)); + const char* funname = GetFunname(); + if (funname) { + *aFunction = (char*) nsMemory::Clone(funname, + sizeof(char)*(strlen(funname)+1)); } else { *aFunction = nullptr; } @@ -361,10 +456,22 @@ NS_IMETHODIMP JSStackFrame::GetName(char** aFunction) return NS_OK; } +int32_t +JSStackFrame::GetLineno() +{ + if (!mLinenoInitialized) { + JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex); + mLineno = desc.lineno(); + mLinenoInitialized = true; + } + + return mLineno; +} + /* readonly attribute int32_t lineNumber; */ NS_IMETHODIMP JSStackFrame::GetLineNumber(int32_t* aLineNumber) { - *aLineNumber = mLineno; + *aLineNumber = GetLineno(); return NS_OK; } @@ -386,15 +493,21 @@ NS_IMETHODIMP JSStackFrame::GetCaller(nsIStackFrame** aCaller) NS_IMETHODIMP JSStackFrame::ToString(char** _retval) { const char* frametype = IsJSFrame() ? "JS" : "native"; - const char* filename = mFilename ? mFilename : ""; - const char* funname = mFunname ? mFunname : ""; + const char* filename = GetFilename(); + if (!filename) { + filename = ""; + } + const char* funname = GetFunname(); + if (!funname) { + funname = ""; + } static const char format[] = "%s frame :: %s :: %s :: line %d"; int len = sizeof(char)* (strlen(frametype) + strlen(filename) + strlen(funname)) + sizeof(format) + 3 * sizeof(mLineno); char* buf = (char*) nsMemory::Alloc(len); - JS_snprintf(buf, len, format, frametype, filename, funname, mLineno); + JS_snprintf(buf, len, format, frametype, filename, funname, GetLineno()); *_retval = buf; return NS_OK; } @@ -404,9 +517,6 @@ JSStackFrame::CreateStack(JSContext* cx) { static const unsigned MAX_FRAMES = 100; - nsRefPtr first = new JSStackFrame(); - nsRefPtr self = first; - JS::StackDescription* desc = JS::DescribeStack(cx, MAX_FRAMES); if (!desc) { return nullptr; @@ -414,36 +524,21 @@ JSStackFrame::CreateStack(JSContext* cx) nsRefPtr descOwner = new StackDescriptionOwner(desc); - for (size_t i = 0; i < desc->nframes && self; i++) { - self->mLanguage = nsIProgrammingLanguage::JAVASCRIPT; + nsRefPtr first; + nsRefPtr last; - JSAutoCompartment ac(cx, desc->frames[i].script()); - const char* filename = JS_GetScriptFilename(cx, desc->frames[i].script()); - if (filename) { - self->mFilename = - (char*)nsMemory::Clone(filename, sizeof(char)*(strlen(filename)+1)); + // We create one dummy frame at the end (why?), so go up through + // desc->nframes+1 + for (size_t i = 0; i < desc->nframes+1; i++) { + nsRefPtr frame = new JSStackFrame(descOwner, i); + + if (last) { + last->mCaller = frame; + } else { + MOZ_ASSERT(!first, "How can we have a first but not a last?"); + first = frame; } - - self->mLineno = desc->frames[i].lineno(); - - JSFunction* fun = desc->frames[i].fun(); - if (fun) { - JS::Rooted funid(cx, JS_GetFunctionDisplayId(fun)); - if (funid) { - size_t length = JS_GetStringEncodingLength(cx, funid); - if (length != size_t(-1)) { - self->mFunname = static_cast(nsMemory::Alloc(length + 1)); - if (self->mFunname) { - JS_EncodeStringToBuffer(cx, funid, self->mFunname, length); - self->mFunname[length] = '\0'; - } - } - } - } - - nsRefPtr frame = new JSStackFrame(); - self->mCaller = frame; - self.swap(frame); + last.swap(frame); } return first.forget(); @@ -456,7 +551,7 @@ JSStackFrame::CreateStackFrameLocation(uint32_t aLanguage, int32_t aLineNumber, nsIStackFrame* aCaller) { - nsRefPtr self = new JSStackFrame(); + nsRefPtr self = new JSStackFrame(nullptr, 0); self->mLanguage = aLanguage; self->mLineno = aLineNumber;