Bug 1083359 - Part 1 - Add the asyncCause and asyncParent properties to the native SavedFrame object. r=jimb

This commit is contained in:
Paolo Amadini 2015-02-21 11:56:00 +00:00
Родитель 77e821b69f
Коммит 14addff448
5 изменённых файлов: 183 добавлений и 20 удалений

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

@ -59,10 +59,27 @@ as the content compartment sees it, waive the xray wrapper with
: Either SpiderMonkey's inferred name for this stack frame's function, or
`null`.
`parent`
: Either this stack frame's parent stack frame (the next older frame), or
`null` if this is the oldest frame in the captured stack.
`asyncCause`
: If this stack frame is the `asyncParent` of other stack frames, then this is
a string representing the type of asynchronous call by which this frame
invoked its children. For example, if this frame's children are calls to
handlers for a promise this frame created, this frame's `asyncCause` would
be `"Promise"`. If the asynchronous call was started in a descendant frame
to which the requester of the property does not have access, this will be
the generic string `"Async"`. If this is not an asynchronous call point,
this will be `null`.
`asyncParent`
: If this stack frame was called as a result of an asynchronous operation, for
example if the function referenced by this frame is a promise handler, this
property points to the stack frame responsible for the asynchronous call,
for example where the promise was created. If the frame responsible for the
call is not accessible to the caller, this points to the youngest accessible
ancestor of the real frame, if any. In all other cases, this is `null`.
`parent`
: This stack frame's caller, or `null` if this is the oldest frame on the
stack. In this case, there might be an `asyncParent` instead.
## Function Properties of the `SavedFrame.prototype` Object

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

@ -5105,6 +5105,20 @@ GetSavedFrameColumn(JSContext *cx, HandleObject savedFrame, uint32_t *columnp);
extern JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, MutableHandleString namep);
/*
* Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr.
*/
extern JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameAsyncCause(JSContext *cx, HandleObject savedFrame, MutableHandleString asyncCausep);
/*
* Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr
* if there is no asyncParent. The `asyncParentp` out parameter is _NOT_
* guaranteed to be in the cx's compartment. Defaults to nullptr.
*/
extern JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameAsyncParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject asyncParentp);
/*
* Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
* it is the oldest frame in the stack. The `parentp` out parameter is _NOT_

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

@ -21,6 +21,7 @@
macro(ArrayIteratorNext, ArrayIteratorNext, "ArrayIteratorNext") \
macro(ArrayType, ArrayType, "ArrayType") \
macro(ArrayValues, ArrayValues, "ArrayValues") \
macro(Async, Async, "Async") \
macro(buffer, buffer, "buffer") \
macro(builder, builder, "builder") \
macro(byteLength, byteLength, "byteLength") \

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

@ -39,12 +39,14 @@ using mozilla::Maybe;
namespace js {
struct SavedFrame::Lookup {
Lookup(JSAtom *source, uint32_t line, uint32_t column, JSAtom *functionDisplayName,
SavedFrame *parent, JSPrincipals *principals)
Lookup(JSAtom *source, uint32_t line, uint32_t column,
JSAtom *functionDisplayName, JSAtom *asyncCause, SavedFrame *parent,
JSPrincipals *principals)
: source(source),
line(line),
column(column),
functionDisplayName(functionDisplayName),
asyncCause(asyncCause),
parent(parent),
principals(principals)
{
@ -55,6 +57,7 @@ struct SavedFrame::Lookup {
uint32_t line;
uint32_t column;
JSAtom *functionDisplayName;
JSAtom *asyncCause;
SavedFrame *parent;
JSPrincipals *principals;
@ -64,6 +67,10 @@ struct SavedFrame::Lookup {
gc::MarkStringUnbarriered(trc, &functionDisplayName,
"SavedFrame::Lookup::functionDisplayName");
}
if (asyncCause) {
gc::MarkStringUnbarriered(trc, &asyncCause,
"SavedFrame::Lookup::asyncCause");
}
if (parent) {
gc::MarkObjectUnbarriered(trc, &parent,
"SavedFrame::Lookup::parent");
@ -102,6 +109,7 @@ SavedFrame::HashPolicy::hash(const Lookup &lookup)
lookup.column,
lookup.source,
lookup.functionDisplayName,
lookup.asyncCause,
SavedFramePtrHasher::hash(lookup.parent),
JSPrincipalsPtrHasher::hash(lookup.principals));
}
@ -129,6 +137,10 @@ SavedFrame::HashPolicy::match(SavedFrame *existing, const Lookup &lookup)
if (functionDisplayName != lookup.functionDisplayName)
return false;
JSAtom *asyncCause = existing->getAsyncCause();
if (asyncCause != lookup.asyncCause)
return false;
return true;
}
@ -197,6 +209,8 @@ SavedFrame::protoAccessors[] = {
JS_PSG("line", SavedFrame::lineProperty, 0),
JS_PSG("column", SavedFrame::columnProperty, 0),
JS_PSG("functionDisplayName", SavedFrame::functionDisplayNameProperty, 0),
JS_PSG("asyncCause", SavedFrame::asyncCauseProperty, 0),
JS_PSG("asyncParent", SavedFrame::asyncParentProperty, 0),
JS_PSG("parent", SavedFrame::parentProperty, 0),
JS_PS_END
};
@ -243,6 +257,16 @@ SavedFrame::getFunctionDisplayName()
return &s->asAtom();
}
JSAtom *
SavedFrame::getAsyncCause()
{
const Value &v = getReservedSlot(JSSLOT_ASYNCCAUSE);
if (v.isNull())
return nullptr;
JSString *s = v.toString();
return &s->asAtom();
}
SavedFrame *
SavedFrame::getParent()
{
@ -272,6 +296,10 @@ SavedFrame::initFromLookup(SavedFrame::HandleLookup lookup)
lookup->functionDisplayName
? StringValue(lookup->functionDisplayName)
: NullValue());
setReservedSlot(JSSLOT_ASYNCCAUSE,
lookup->asyncCause
? StringValue(lookup->asyncCause)
: NullValue());
setReservedSlot(JSSLOT_PARENT, ObjectOrNullValue(lookup->parent));
setReservedSlot(JSSLOT_PRIVATE_PARENT, PrivateValue(lookup->parent));
@ -312,10 +340,14 @@ SavedFrame::construct(JSContext *cx, unsigned argc, Value *vp)
// Return the first SavedFrame in the chain that starts with |frame| whose
// principals are subsumed by |principals|, according to |subsumes|. If there is
// no such frame, return nullptr.
// no such frame, return nullptr. |skippedAsync| is set to true if any of the
// skipped frames had the |asyncCause| property set, otherwise it is explicitly
// set to false.
static SavedFrame *
GetFirstSubsumedFrame(JSContext *cx, HandleSavedFrame frame)
GetFirstSubsumedFrame(JSContext *cx, HandleSavedFrame frame, bool &skippedAsync)
{
skippedAsync = false;
JSSubsumesOp subsumes = cx->runtime()->securityCallbacks->subsumes;
if (!subsumes)
return frame;
@ -323,8 +355,11 @@ GetFirstSubsumedFrame(JSContext *cx, HandleSavedFrame frame)
JSPrincipals *principals = cx->compartment()->principals;
RootedSavedFrame rootedFrame(cx, frame);
while (rootedFrame && !subsumes(principals, rootedFrame->getPrincipals()))
while (rootedFrame && !subsumes(principals, rootedFrame->getPrincipals())) {
if (rootedFrame->getAsyncCause())
skippedAsync = true;
rootedFrame = rootedFrame->getParent();
}
return rootedFrame;
}
@ -334,8 +369,9 @@ GetFirstSubsumedSavedFrame(JSContext *cx, HandleObject savedFrame)
{
if (!savedFrame)
return nullptr;
bool skippedAsync;
RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
return GetFirstSubsumedFrame(cx, frame);
return GetFirstSubsumedFrame(cx, frame, skippedAsync);
}
/* static */ bool
@ -437,7 +473,7 @@ public:
} // anonymous namespace
static inline js::SavedFrame *
UnwrapSavedFrame(JSContext *cx, HandleObject obj)
UnwrapSavedFrame(JSContext *cx, HandleObject obj, bool &skippedAsync)
{
if (!obj)
return nullptr;
@ -445,14 +481,15 @@ UnwrapSavedFrame(JSContext *cx, HandleObject obj)
MOZ_ASSERT(savedFrameObj);
MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
return GetFirstSubsumedFrame(cx, frame);
return GetFirstSubsumedFrame(cx, frame, skippedAsync);
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameSource(JSContext *cx, HandleObject savedFrame, MutableHandleString sourcep)
{
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
if (!frame) {
sourcep.set(cx->runtime()->emptyString);
return SavedFrameResult::AccessDenied;
@ -466,7 +503,8 @@ GetSavedFrameLine(JSContext *cx, HandleObject savedFrame, uint32_t *linep)
{
MOZ_ASSERT(linep);
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
if (!frame) {
*linep = 0;
return SavedFrameResult::AccessDenied;
@ -480,7 +518,8 @@ GetSavedFrameColumn(JSContext *cx, HandleObject savedFrame, uint32_t *columnp)
{
MOZ_ASSERT(columnp);
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
if (!frame) {
*columnp = 0;
return SavedFrameResult::AccessDenied;
@ -493,7 +532,8 @@ JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, MutableHandleString namep)
{
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
if (!frame) {
namep.set(nullptr);
return SavedFrameResult::AccessDenied;
@ -502,17 +542,73 @@ GetSavedFrameFunctionDisplayName(JSContext *cx, HandleObject savedFrame, Mutable
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameAsyncCause(JSContext *cx, HandleObject savedFrame, MutableHandleString asyncCausep)
{
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
if (!frame) {
asyncCausep.set(nullptr);
return SavedFrameResult::AccessDenied;
}
asyncCausep.set(frame->getAsyncCause());
if (!asyncCausep && skippedAsync)
asyncCausep.set(cx->names().Async);
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameAsyncParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject asyncParentp)
{
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
if (!frame) {
asyncParentp.set(nullptr);
return SavedFrameResult::AccessDenied;
}
js::RootedSavedFrame parent(cx, frame->getParent());
// The current value of |skippedAsync| is not interesting, because we are
// interested in whether we would cross any async parents to get from here
// to the first subsumed parent frame instead.
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
// Even if |parent| is not subsumed, we still want to return a pointer to it
// rather than |subsumedParent| so it can pick up any |asyncCause| from the
// inaccessible part of the chain.
if (subsumedParent && (subsumedParent->getAsyncCause() || skippedAsync))
asyncParentp.set(parent);
else
asyncParentp.set(nullptr);
return SavedFrameResult::Ok;
}
JS_PUBLIC_API(SavedFrameResult)
GetSavedFrameParent(JSContext *cx, HandleObject savedFrame, MutableHandleObject parentp)
{
AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame));
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
if (!frame) {
parentp.set(nullptr);
return SavedFrameResult::AccessDenied;
}
js::RootedSavedFrame parent(cx, frame->getParent());
parentp.set(js::GetFirstSubsumedFrame(cx, parent));
// The current value of |skippedAsync| is not interesting, because we are
// interested in whether we would cross any async parents to get from here
// to the first subsumed parent frame instead.
js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
// Even if |parent| is not subsumed, we still want to return a pointer to it
// rather than |subsumedParent| so it can pick up any |asyncCause| from the
// inaccessible part of the chain.
if (subsumedParent && !(subsumedParent->getAsyncCause() || skippedAsync))
parentp.set(parent);
else
parentp.set(nullptr);
return SavedFrameResult::Ok;
}
@ -520,7 +616,8 @@ JS_PUBLIC_API(bool)
StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString stringp)
{
AutoMaybeEnterFrameCompartment ac(cx, stack);
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack));
bool skippedAsync;
js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, skippedAsync));
if (!frame) {
stringp.set(cx->runtime()->emptyString);
return true;
@ -535,8 +632,13 @@ StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString
MOZ_ASSERT_IF(subsumes, (*subsumes)(principals, frame->getPrincipals()));
if (!frame->isSelfHosted()) {
RootedString asyncCause(cx, frame->getAsyncCause());
if (!asyncCause && skippedAsync) {
asyncCause.set(cx->names().Async);
}
js::RootedAtom name(cx, frame->getFunctionDisplayName());
if ((name && !sb.append(name))
if ((asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
|| (name && !sb.append(name))
|| !sb.append('@')
|| !sb.append(frame->getSource())
|| !sb.append(':')
@ -550,7 +652,7 @@ StringifySavedFrameStack(JSContext *cx, HandleObject stack, MutableHandleString
}
parent = frame->getParent();
frame = js::GetFirstSubsumedFrame(cx, parent);
frame = js::GetFirstSubsumedFrame(cx, parent, skippedAsync);
} while (frame);
JSString *str = sb.finishString();
@ -613,6 +715,29 @@ SavedFrame::functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp)
return true;
}
/* static */ bool
SavedFrame::asyncCauseProperty(JSContext *cx, unsigned argc, Value *vp)
{
THIS_SAVEDFRAME(cx, argc, vp, "(get asyncCause)", args, frame);
RootedString asyncCause(cx);
JS::SavedFrameResult result = JS::GetSavedFrameAsyncCause(cx, frame, &asyncCause);
if (result == JS::SavedFrameResult::Ok && asyncCause)
args.rval().setString(asyncCause);
else
args.rval().setNull();
return true;
}
/* static */ bool
SavedFrame::asyncParentProperty(JSContext *cx, unsigned argc, Value *vp)
{
THIS_SAVEDFRAME(cx, argc, vp, "(get asyncParent)", args, frame);
RootedObject asyncParent(cx);
(void) JS::GetSavedFrameAsyncParent(cx, frame, &asyncParent);
args.rval().setObjectOrNull(asyncParent);
return true;
}
/* static */ bool
SavedFrame::parentProperty(JSContext *cx, unsigned argc, Value *vp)
{
@ -676,6 +801,7 @@ SavedStacks::sweep(JSRuntime *rt)
frame->getLine(),
frame->getColumn(),
frame->getFunctionDisplayName(),
frame->getAsyncCause(),
frame->getParent(),
frame->getPrincipals()),
ReadBarriered<SavedFrame *>(frame));
@ -759,6 +885,7 @@ SavedStacks::insertFrames(JSContext *cx, FrameIter &iter, MutableHandleSavedFram
location->column,
iter.isNonEvalFunctionFrame() ? iter.functionDisplayAtom() : nullptr,
nullptr,
nullptr,
iter.compartment()->principals
);

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

@ -35,6 +35,8 @@ class SavedFrame : public NativeObject {
static bool lineProperty(JSContext *cx, unsigned argc, Value *vp);
static bool columnProperty(JSContext *cx, unsigned argc, Value *vp);
static bool functionDisplayNameProperty(JSContext *cx, unsigned argc, Value *vp);
static bool asyncCauseProperty(JSContext *cx, unsigned argc, Value *vp);
static bool asyncParentProperty(JSContext *cx, unsigned argc, Value *vp);
static bool parentProperty(JSContext *cx, unsigned argc, Value *vp);
static bool toStringMethod(JSContext *cx, unsigned argc, Value *vp);
@ -43,6 +45,7 @@ class SavedFrame : public NativeObject {
uint32_t getLine();
uint32_t getColumn();
JSAtom *getFunctionDisplayName();
JSAtom *getAsyncCause();
SavedFrame *getParent();
JSPrincipals *getPrincipals();
@ -86,6 +89,7 @@ class SavedFrame : public NativeObject {
JSSLOT_LINE,
JSSLOT_COLUMN,
JSSLOT_FUNCTIONDISPLAYNAME,
JSSLOT_ASYNCCAUSE,
JSSLOT_PARENT,
JSSLOT_PRINCIPALS,
JSSLOT_PRIVATE_PARENT,