Bug 932837 part 3. Make JSStackFrame get information from the JS stack lazily. r=khuey

This commit is contained in:
Boris Zbarsky 2014-01-07 19:53:18 -05:00
Родитель 72e2ce7440
Коммит f1fc5726d1
2 изменённых файлов: 148 добавлений и 49 удалений

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

@ -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

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

@ -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<nsIStackFrame>
@ -282,20 +292,48 @@ private:
return mLanguage == nsIProgrammingLanguage::JAVASCRIPT;
}
const char* GetFilename();
const char* GetFunname();
int32_t GetLineno();
nsRefPtr<StackDescriptionOwner> mStackDescription;
nsCOMPtr<nsIStackFrame> 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<JSFunction*> fun(cx, desc.fun());
JS::Rooted<JSString*> funid(cx, JS_GetFunctionDisplayId(fun));
if (funid) {
size_t length = JS_GetStringEncodingLength(cx, funid);
if (length != size_t(-1)) {
mFunname = static_cast<char *>(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 : "<unknown filename>";
const char* funname = mFunname ? mFunname : "<TOP_LEVEL>";
const char* filename = GetFilename();
if (!filename) {
filename = "<unknown filename>";
}
const char* funname = GetFunname();
if (!funname) {
funname = "<TOP_LEVEL>";
}
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<JSStackFrame> first = new JSStackFrame();
nsRefPtr<JSStackFrame> self = first;
JS::StackDescription* desc = JS::DescribeStack(cx, MAX_FRAMES);
if (!desc) {
return nullptr;
@ -414,36 +524,21 @@ JSStackFrame::CreateStack(JSContext* cx)
nsRefPtr<StackDescriptionOwner> descOwner = new StackDescriptionOwner(desc);
for (size_t i = 0; i < desc->nframes && self; i++) {
self->mLanguage = nsIProgrammingLanguage::JAVASCRIPT;
nsRefPtr<JSStackFrame> first;
nsRefPtr<JSStackFrame> 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<JSStackFrame> 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<JSString*> funid(cx, JS_GetFunctionDisplayId(fun));
if (funid) {
size_t length = JS_GetStringEncodingLength(cx, funid);
if (length != size_t(-1)) {
self->mFunname = static_cast<char *>(nsMemory::Alloc(length + 1));
if (self->mFunname) {
JS_EncodeStringToBuffer(cx, funid, self->mFunname, length);
self->mFunname[length] = '\0';
}
}
}
}
nsRefPtr<JSStackFrame> 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<JSStackFrame> self = new JSStackFrame();
nsRefPtr<JSStackFrame> self = new JSStackFrame(nullptr, 0);
self->mLanguage = aLanguage;
self->mLineno = aLineNumber;