--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -668,17 +668,20 @@ already_AddRefed<nsIStackFrame>
CreateStack(JSContext* aCx, int32_t aMaxDepth)
{
static const unsigned MAX_FRAMES = 100;
if (aMaxDepth < 0) {
aMaxDepth = MAX_FRAMES;
}
JS::Rooted<JSObject*> stack(aCx);
- if (!JS::CaptureCurrentStack(aCx, &stack, aMaxDepth)) {
+ JS::StackCapture capture = aMaxDepth == 0
+ ? JS::StackCapture(JS::AllFrames())
+ : JS::StackCapture(JS::MaxFrames(aMaxDepth));
+ if (!JS::CaptureCurrentStack(aCx, &stack, mozilla::Move(capture))) {
return nullptr;
}
if (!stack) {
return nullptr;
}
nsCOMPtr<nsIStackFrame> frame = new JSStackFrame(stack);
--- a/js/src/builtin/Promise.cpp
+++ b/js/src/builtin/Promise.cpp
@@ -130,17 +130,17 @@ PromiseObject::create(JSContext* cx, Han
promise->setFixedSlot(PROMISE_IS_HANDLED_SLOT,
Int32Value(PROMISE_IS_HANDLED_STATE_UNHANDLED));
// Store an allocation stack so we can later figure out what the
// control flow was for some unexpected results. Frightfully expensive,
// but oh well.
RootedObject stack(cx);
if (cx->options().asyncStack() || cx->compartment()->isDebuggee()) {
- if (!JS::CaptureCurrentStack(cx, &stack, 0))
+ if (!JS::CaptureCurrentStack(cx, &stack, JS::StackCapture(JS::AllFrames())))
return nullptr;
}
promise->setFixedSlot(PROMISE_ALLOCATION_SITE_SLOT, ObjectOrNullValue(stack));
promise->setFixedSlot(PROMISE_ALLOCATION_TIME_SLOT,
DoubleValue(MillisecondsSinceStartup()));
}
RootedValue promiseVal(cx, ObjectValue(*promise));
@@ -420,17 +420,17 @@ PromiseObject::reject(JSContext* cx, Han
}
void
PromiseObject::onSettled(JSContext* cx)
{
Rooted<PromiseObject*> promise(cx, this);
RootedObject stack(cx);
if (cx->options().asyncStack() || cx->compartment()->isDebuggee()) {
- if (!JS::CaptureCurrentStack(cx, &stack, 0)) {
+ if (!JS::CaptureCurrentStack(cx, &stack, JS::StackCapture(JS::AllFrames()))) {
cx->clearPendingException();
return;
}
}
promise->setFixedSlot(PROMISE_RESOLUTION_SITE_SLOT, ObjectOrNullValue(stack));
promise->setFixedSlot(PROMISE_RESOLUTION_TIME_SLOT, DoubleValue(MillisecondsSinceStartup()));
if (promise->state() == JS::PromiseState::Rejected &&
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -1098,28 +1098,29 @@ GetSavedFrameCount(JSContext* cx, unsign
return true;
}
static bool
SaveStack(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
- unsigned maxFrameCount = 0;
+ JS::StackCapture capture((JS::AllFrames()));
if (args.length() >= 1) {
double d;
if (!ToNumber(cx, args[0], &d))
return false;
if (d < 0) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, args[0], nullptr,
"not a valid maximum frame count", NULL);
return false;
}
- maxFrameCount = d;
+ if (d > 0)
+ capture = JS::StackCapture(JS::MaxFrames(d));
}
JSCompartment* targetCompartment = cx->compartment();
if (args.length() >= 2) {
if (!args[1].isObject()) {
ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_UNEXPECTED_TYPE,
JSDVG_SEARCH_STACK, args[0], nullptr,
"not an object", NULL);
@@ -1129,28 +1130,59 @@ SaveStack(JSContext* cx, unsigned argc,
if (!obj)
return false;
targetCompartment = obj->compartment();
}
RootedObject stack(cx);
{
AutoCompartment ac(cx, targetCompartment);
- if (!JS::CaptureCurrentStack(cx, &stack, maxFrameCount))
+ if (!JS::CaptureCurrentStack(cx, &stack, mozilla::Move(capture)))
return false;
}
if (stack && !cx->compartment()->wrap(cx, &stack))
return false;
args.rval().setObjectOrNull(stack);
return true;
}
static bool
+CaptureFirstSubsumedFrame(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (!args.requireAtLeast(cx, "captureFirstSubsumedFrame", 1))
+ return false;
+
+ if (!args[0].isObject()) {
+ JS_ReportError(cx, "The argument must be an object");
+ return false;
+ }
+
+ RootedObject obj(cx, &args[0].toObject());
+ obj = CheckedUnwrap(obj);
+ if (!obj) {
+ JS_ReportError(cx, "Denied permission to object.");
+ return false;
+ }
+
+ JS::StackCapture capture(JS::FirstSubsumedFrame(cx, obj->compartment()->principals()));
+ if (args.length() > 1)
+ capture.as<JS::FirstSubsumedFrame>().ignoreSelfHosted = JS::ToBoolean(args[1]);
+
+ JS::RootedObject capturedStack(cx);
+ if (!JS::CaptureCurrentStack(cx, &capturedStack, mozilla::Move(capture)))
+ return false;
+
+ args.rval().setObjectOrNull(capturedStack);
+ return true;
+}
+
+static bool
CallFunctionFromNativeFrame(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() != 1) {
JS_ReportError(cx, "The function takes exactly one argument.");
return false;
}
@@ -3598,16 +3630,22 @@ static const JSFunctionSpecWithHelp Test
" SavedStacks cache."),
JS_FN_HELP("saveStack", SaveStack, 0, 0,
"saveStack([maxDepth [, compartment]])",
" Capture a stack. If 'maxDepth' is given, capture at most 'maxDepth' number\n"
" of frames. If 'compartment' is given, allocate the js::SavedFrame instances\n"
" with the given object's compartment."),
+ JS_FN_HELP("captureFirstSubsumedFrame", CaptureFirstSubsumedFrame, 1, 0,
+"saveStack(object [, shouldIgnoreSelfHosted = true]])",
+" Capture a stack back to the first frame whose principals are subsumed by the\n"
+" object's compartment's principals. If 'shouldIgnoreSelfHosted' is given,\n"
+" control whether self-hosted frames are considered when checking principals."),
+
JS_FN_HELP("callFunctionFromNativeFrame", CallFunctionFromNativeFrame, 1, 0,
"callFunctionFromNativeFrame(function)",
" Call 'function' with a (C++-)native frame on stack.\n"
" Required for testing that SaveStack properly handles native frames."),
JS_FN_HELP("callFunctionWithAsyncStack", CallFunctionWithAsyncStack, 0, 0,
"callFunctionWithAsyncStack(function, stack, asyncCause)",
" Call 'function', using the provided stack as the async stack responsible\n"
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/saved-stacks/capture-first-frame-with-principals.js
@@ -0,0 +1,92 @@
+// Create two different globals whose compartments have two different
+// principals. Test getting the first frame on the stack with some given
+// principals in various configurations of JS stack and of wanting self-hosted
+// frames or not.
+
+const g1 = newGlobal({
+ principal: 0xffff
+});
+
+const g2 = newGlobal({
+ principal: 0xff
+});
+
+// Introduce everyone to themselves and each other.
+g1.g2 = g2.g2 = g2;
+g1.g1 = g2.g1 = g1;
+
+g1.g2obj = g2.eval("new Object");
+
+g1.evaluate(`
+ const global = this;
+
+ // Capture the stack back to the first frame in the g2 global.
+ function capture(shouldIgnoreSelfHosted = true) {
+ return captureFirstSubsumedFrame(global.g2obj, shouldIgnoreSelfHosted);
+ }
+`, {
+ fileName: "script1.js"
+});
+
+g2.evaluate(`
+ const capture = g1.capture;
+
+ // Use our Function.prototype.bind, not capture.bind (which is ===
+ // g1.Function.prototype.bind) so that the generated bound function is in our
+ // compartment and has our principals.
+ const boundTrue = Function.prototype.bind.call(capture, null, true);
+ const boundFalse = Function.prototype.bind.call(capture, null, false);
+
+ function getOldestFrame(stack) {
+ while (stack.parent) {
+ stack = stack.parent;
+ }
+ return stack;
+ }
+
+ function dumpStack(name, stack) {
+ print("Stack " + name + " =");
+ while (stack) {
+ print(" " + stack.functionDisplayName + " @ " + stack.source);
+ stack = stack.parent;
+ }
+ print();
+ }
+
+ // When the youngest frame is not self-hosted, it doesn't matter whether or not
+ // we specify that we should ignore self hosted frames when capturing the first
+ // frame with the given principals.
+ //
+ // Stack: iife1 (g2) <- capture (g1)
+
+ (function iife1() {
+ const captureTrueStack = capture(true);
+ dumpStack("captureTrueStack", captureTrueStack);
+ assertEq(getOldestFrame(captureTrueStack).functionDisplayName, "iife1");
+ assertEq(getOldestFrame(captureTrueStack).source, "script2.js");
+
+ const captureFalseStack = capture(false);
+ dumpStack("captureFalseStack", captureFalseStack);
+ assertEq(getOldestFrame(captureFalseStack).functionDisplayName, "iife1");
+ assertEq(getOldestFrame(captureFalseStack).source, "script2.js");
+ }());
+
+ // When the youngest frame is a self hosted frame, we get two different
+ // captured stacks depending on whether or not we ignore self-hosted frames.
+ //
+ // Stack: iife2 (g2) <- bound function (g2) <- capture (g1)
+
+ (function iife2() {
+ const boundTrueStack = boundTrue();
+ dumpStack("boundTrueStack", boundTrueStack);
+ assertEq(getOldestFrame(boundTrueStack).functionDisplayName, "iife2");
+ assertEq(getOldestFrame(boundTrueStack).source, "script2.js");
+
+ const boundFalseStack = boundFalse();
+ dumpStack("boundFalseStack", boundFalseStack);
+ assertEq(getOldestFrame(boundFalseStack).functionDisplayName !== "iife2", true);
+ assertEq(getOldestFrame(boundFalseStack).source, "self-hosted");
+ }());
+`, {
+ fileName: "script2.js"
+});
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -6531,26 +6531,32 @@ JS::SetLargeAllocationFailureCallback(JS
JS_PUBLIC_API(void)
JS::SetOutOfMemoryCallback(JSContext* cx, OutOfMemoryCallback cb, void* data)
{
cx->oomCallback = cb;
cx->oomCallbackData = data;
}
-JS_PUBLIC_API(bool)
-JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp, unsigned maxFrameCount)
+JS::FirstSubsumedFrame::FirstSubsumedFrame(JSContext* cx,
+ bool ignoreSelfHostedFrames /* = true */)
+ : JS::FirstSubsumedFrame(cx, cx->compartment()->principals(), ignoreSelfHostedFrames)
+{ }
+
+JS_PUBLIC_API(bool)
+JS::CaptureCurrentStack(JSContext* cx, JS::MutableHandleObject stackp,
+ JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
MOZ_RELEASE_ASSERT(cx->compartment());
JSCompartment* compartment = cx->compartment();
Rooted<SavedFrame*> frame(cx);
- if (!compartment->savedStacks().saveCurrentStack(cx, &frame, maxFrameCount))
+ if (!compartment->savedStacks().saveCurrentStack(cx, &frame, mozilla::Move(capture)))
return false;
stackp.set(frame.get());
return true;
}
JS_PUBLIC_API(bool)
JS::CopyAsyncStack(JSContext* cx, JS::HandleObject asyncStack,
JS::HandleString asyncCause, JS::MutableHandleObject stackp,
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5892,23 +5892,105 @@ SetLargeAllocationFailureCallback(JSCont
typedef void
(* OutOfMemoryCallback)(JSContext* cx, void* data);
extern JS_PUBLIC_API(void)
SetOutOfMemoryCallback(JSContext* cx, OutOfMemoryCallback cb, void* data);
/**
+ * Capture all frames.
+ */
+struct AllFrames { };
+
+/**
+ * Capture at most this many frames.
+ */
+struct MaxFrames
+{
+ unsigned maxFrames;
+
+ explicit MaxFrames(unsigned max)
+ : maxFrames(max)
+ {
+ MOZ_ASSERT(max > 0);
+ }
+};
+
+/**
+ * Capture the first frame with the given principals. By default, do not
+ * consider self-hosted frames with the given principals as satisfying the stack
+ * capture.
+ */
+struct FirstSubsumedFrame
+{
+ JSContext* cx;
+ JSPrincipals* principals;
+ bool ignoreSelfHosted;
+
+ /**
+ * Use the cx's current compartment's principals.
+ */
+ explicit FirstSubsumedFrame(JSContext* cx, bool ignoreSelfHostedFrames = true);
+
+ explicit FirstSubsumedFrame(JSContext* ctx, JSPrincipals* p, bool ignoreSelfHostedFrames = true)
+ : cx(ctx)
+ , principals(p)
+ , ignoreSelfHosted(ignoreSelfHostedFrames)
+ {
+ JS_HoldPrincipals(principals);
+ }
+
+ // No copying because we want to avoid holding and dropping principals
+ // unnecessarily.
+ FirstSubsumedFrame(const FirstSubsumedFrame&) = delete;
+ FirstSubsumedFrame& operator=(const FirstSubsumedFrame&) = delete;
+
+ FirstSubsumedFrame(FirstSubsumedFrame&& rhs)
+ : principals(rhs.principals)
+ , ignoreSelfHosted(rhs.ignoreSelfHosted)
+ {
+ MOZ_ASSERT(this != &rhs, "self move disallowed");
+ rhs.principals = nullptr;
+ }
+
+ FirstSubsumedFrame& operator=(FirstSubsumedFrame&& rhs) {
+ new (this) FirstSubsumedFrame(mozilla::Move(rhs));
+ return *this;
+ }
+
+ ~FirstSubsumedFrame() {
+ if (principals)
+ JS_DropPrincipals(cx, principals);
+ }
+};
+
+using StackCapture = mozilla::Variant<AllFrames, MaxFrames, FirstSubsumedFrame>;
+
+/**
* Capture the current call stack as a chain of SavedFrame JSObjects, and set
* |stackp| to the SavedFrame for the youngest stack frame, or nullptr if there
- * are no JS frames on the stack. If |maxFrameCount| is non-zero, capture at
- * most the youngest |maxFrameCount| frames.
- */
-extern JS_PUBLIC_API(bool)
-CaptureCurrentStack(JSContext* cx, MutableHandleObject stackp, unsigned maxFrameCount = 0);
+ * are no JS frames on the stack.
+ *
+ * The |capture| parameter describes the portion of the JS stack to capture:
+ *
+ * * |JS::AllFrames|: Capture all frames on the stack.
+ *
+ * * |JS::MaxFrames|: Capture no more than |JS::MaxFrames::maxFrames| from the
+ * stack.
+ *
+ * * |JS::FirstSubsumedFrame|: Capture the first frame whose principals are
+ * subsumed by |JS::FirstSubsumedFrame::principals|. By default, do not
+ * consider self-hosted frames; this can be controlled via the
+ * |JS::FirstSubsumedFrame::ignoreSelfHosted| flag. Do not capture any async
+ * stack.
+ */
+extern JS_PUBLIC_API(bool)
+CaptureCurrentStack(JSContext* cx, MutableHandleObject stackp,
+ StackCapture&& capture = StackCapture(AllFrames()));
/*
* This is a utility function for preparing an async stack to be used
* by some other object. This may be used when you need to treat a
* given stack trace as an async parent. If you just need to capture
* the current stack, async parents and all, use CaptureCurrentStack
* instead.
*
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -261,17 +261,18 @@ struct SuppressErrorsGuard
// Cut off the stack if it gets too deep (most commonly for infinite recursion
// errors).
static const size_t MAX_REPORTED_STACK_DEPTH = 1u << 7;
static bool
CaptureStack(JSContext* cx, MutableHandleObject stack)
{
- return CaptureCurrentStack(cx, stack, MAX_REPORTED_STACK_DEPTH);
+ return CaptureCurrentStack(cx, stack,
+ JS::StackCapture(JS::MaxFrames(MAX_REPORTED_STACK_DEPTH)));
}
JSString*
js::ComputeStackString(JSContext* cx)
{
SuppressErrorsGuard seg(cx);
RootedObject stack(cx);
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -1063,34 +1063,35 @@ SavedFrame::toStringMethod(JSContext* cx
bool
SavedStacks::init()
{
return frames.init() &&
pcLocationMap.init();
}
bool
-SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame, unsigned maxFrameCount)
+SavedStacks::saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
+ JS::StackCapture&& capture /* = JS::StackCapture(JS::AllFrames()) */)
{
MOZ_ASSERT(initialized());
MOZ_RELEASE_ASSERT(cx->compartment());
assertSameCompartment(cx, this);
if (creatingSavedFrame ||
cx->isExceptionPending() ||
!cx->global() ||
!cx->global()->isStandardClassResolved(JSProto_Object))
{
frame.set(nullptr);
return true;
}
AutoSPSEntry psuedoFrame(cx->runtime(), "js::SavedStacks::saveCurrentStack");
FrameIter iter(cx);
- return insertFrames(cx, iter, frame, maxFrameCount);
+ return insertFrames(cx, iter, frame, mozilla::Move(capture));
}
bool
SavedStacks::copyAsyncStack(JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
MutableHandleSavedFrame adoptedStack, unsigned maxFrameCount)
{
MOZ_ASSERT(initialized());
MOZ_RELEASE_ASSERT(cx->compartment());
@@ -1132,19 +1133,59 @@ SavedStacks::clear()
size_t
SavedStacks::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
return frames.sizeOfExcludingThis(mallocSizeOf) +
pcLocationMap.sizeOfExcludingThis(mallocSizeOf);
}
+// Given that we have captured a stqck frame with the given principals and
+// source, return true if the requested `StackCapture` has been satisfied and
+// stack walking can halt. Return false otherwise (and stack walking and frame
+// capturing should continue).
+static inline bool
+captureIsSatisfied(JSContext* cx, JSPrincipals* principals, const JSAtom* source,
+ JS::StackCapture& capture)
+{
+ class Matcher
+ {
+ JSContext* cx_;
+ JSPrincipals* framePrincipals_;
+ const JSAtom* frameSource_;
+
+ public:
+ Matcher(JSContext* cx, JSPrincipals* principals, const JSAtom* source)
+ : cx_(cx)
+ , framePrincipals_(principals)
+ , frameSource_(source)
+ { }
+
+ bool match(JS::FirstSubsumedFrame& target) {
+ auto subsumes = cx_->runtime()->securityCallbacks->subsumes;
+ return (!subsumes || subsumes(target.principals, framePrincipals_)) &&
+ (!target.ignoreSelfHosted || frameSource_ != cx_->names().selfHosted);
+ }
+
+ bool match(JS::MaxFrames& target) {
+ return target.maxFrames == 1;
+ }
+
+ bool match(JS::AllFrames&) {
+ return false;
+ }
+ };
+
+ Matcher m(cx, principals, source);
+ return capture.match(m);
+}
+
bool
SavedStacks::insertFrames(JSContext* cx, FrameIter& iter, MutableHandleSavedFrame frame,
- unsigned maxFrameCount)
+ JS::StackCapture&& capture)
{
// In order to lookup a cached SavedFrame object, we need to have its parent
// SavedFrame, which means we need to walk the stack from oldest frame to
// youngest. However, FrameIter walks the stack from youngest frame to
// oldest. The solution is to append stack frames to a vector as we walk the
// stack with FrameIter, and then do a second pass through that vector in
// reverse order after the traversal has completed and get or create the
// SavedFrame objects at that time.
@@ -1211,80 +1252,83 @@ SavedStacks::insertFrames(JSContext* cx,
{
AutoCompartment ac(cx, iter.compartment());
if (!cx->compartment()->savedStacks().getLocation(cx, iter, &location))
return false;
}
// The bit set means that the next older parent (frame, pc) pair *must*
// be in the cache.
- if (maxFrameCount == 0)
+ if (capture.is<JS::AllFrames>())
parentIsInCache = iter.hasCachedSavedFrame();
+ auto principals = iter.compartment()->principals();
auto displayAtom = iter.isFunctionFrame() ? iter.functionDisplayAtom() : nullptr;
if (!stackChain->emplaceBack(location.source(),
location.line(),
location.column(),
displayAtom,
nullptr,
nullptr,
- iter.compartment()->principals(),
+ principals,
LiveSavedFrameCache::getFramePtr(iter),
iter.pc(),
&activation))
{
ReportOutOfMemory(cx);
return false;
}
- ++iter;
-
- if (maxFrameCount == 1) {
+ if (captureIsSatisfied(cx, principals, location.source(), capture)) {
// The frame we just saved was the last one we were asked to save.
// If we had an async stack, ensure we don't use any of its frames.
asyncStack.set(nullptr);
break;
}
+ ++iter;
+
if (parentIsInCache &&
!iter.done() &&
iter.hasCachedSavedFrame())
{
auto* cache = activation.getLiveSavedFrameCache(cx);
if (!cache)
return false;
cache->find(cx, iter, &cachedFrame);
if (cachedFrame)
break;
}
- // If maxFrameCount is zero there's no limit on the number of frames.
- if (maxFrameCount == 0)
- continue;
-
- maxFrameCount--;
+ if (capture.is<JS::MaxFrames>())
+ capture.as<JS::MaxFrames>().maxFrames--;
}
// Limit the depth of the async stack, if any, and ensure that the
// SavedFrame instances we use are stored in the same compartment as the
// rest of the synchronous stack chain.
RootedSavedFrame parentFrame(cx, cachedFrame);
- if (asyncStack && !adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, maxFrameCount))
- return false;
+ if (asyncStack && !capture.is<JS::FirstSubsumedFrame>()) {
+ unsigned maxAsyncFrames = capture.is<JS::MaxFrames>()
+ ? capture.as<JS::MaxFrames>().maxFrames
+ : ASYNC_STACK_MAX_FRAME_COUNT;
+ if (!adoptAsyncStack(cx, asyncStack, asyncCause, &parentFrame, maxAsyncFrames))
+ return false;
+ }
// Iterate through |stackChain| in reverse order and get or create the
// actual SavedFrame instances.
for (size_t i = stackChain->length(); i != 0; i--) {
SavedFrame::HandleLookup lookup = stackChain[i-1];
lookup->parent = parentFrame;
parentFrame.set(getOrCreateSavedFrame(cx, lookup));
if (!parentFrame)
return false;
- if (maxFrameCount == 0 && lookup->framePtr && parentFrame != cachedFrame) {
+ if (capture.is<JS::AllFrames>() && lookup->framePtr && parentFrame != cachedFrame) {
auto* cache = lookup->activation->getLiveSavedFrameCache(cx);
if (!cache || !cache->insert(cx, *lookup->framePtr, lookup->pc, parentFrame))
return false;
}
}
frame.set(parentFrame);
return true;
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -160,17 +160,17 @@ class SavedStacks {
bernoulliSeeded(false),
bernoulli(1.0, 0x59fdad7f6b4cc573, 0x91adf38db96a9354),
creatingSavedFrame(false)
{ }
MOZ_MUST_USE bool init();
bool initialized() const { return frames.initialized(); }
MOZ_MUST_USE bool saveCurrentStack(JSContext* cx, MutableHandleSavedFrame frame,
- unsigned maxFrameCount = 0);
+ JS::StackCapture&& capture = JS::StackCapture(JS::AllFrames()));
MOZ_MUST_USE bool copyAsyncStack(JSContext* cx, HandleObject asyncStack,
HandleString asyncCause,
MutableHandleSavedFrame adoptedStack,
unsigned maxFrameCount = 0);
void sweep();
void trace(JSTracer* trc);
uint32_t count();
void clear();
@@ -216,17 +216,17 @@ class SavedStacks {
~AutoReentrancyGuard()
{
stacks.creatingSavedFrame = false;
}
};
MOZ_MUST_USE bool insertFrames(JSContext* cx, FrameIter& iter,
MutableHandleSavedFrame frame,
- unsigned maxFrameCount = 0);
+ JS::StackCapture&& capture);
MOZ_MUST_USE bool adoptAsyncStack(JSContext* cx, HandleSavedFrame asyncStack,
HandleString asyncCause,
MutableHandleSavedFrame adoptedStack,
unsigned maxFrameCount);
SavedFrame* getOrCreateSavedFrame(JSContext* cx, SavedFrame::HandleLookup lookup);
SavedFrame* createFrameFromLookup(JSContext* cx, SavedFrame::HandleLookup lookup);
// Cache for memoizing PCToLineNumber lookups.