1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef js_Stack_h
#define js_Stack_h
#include "mozilla/Assertions.h" // MOZ_ASSERT
#include "mozilla/Maybe.h" // mozilla::Maybe
#include "mozilla/Variant.h" // mozilla::Variant
#include <stddef.h> // size_t
#include <stdint.h> // uint32_t, uintptr_t, UINTPTR_MAX
#include <utility> // std::move
#include "jstypes.h" // JS_PUBLIC_API
#include "js/NativeStackLimits.h"
#include "js/Principals.h" // JSPrincipals, JS_HoldPrincipals, JS_DropPrincipals
#include "js/RootingAPI.h"
/**
* Set the size of the native stack that should not be exceed. To disable
* stack size checking pass 0.
*
* SpiderMonkey allows for a distinction between system code (such as GCs, which
* may incidentally be triggered by script but are not strictly performed on
* behalf of such script), trusted script (as determined by
* JS_SetTrustedPrincipals), and untrusted script. Each kind of code may have a
* different stack quota, allowing embedders to keep higher-priority machinery
* running in the face of scripted stack exhaustion by something else.
*
* The stack quotas for each kind of code should be monotonically descending,
* and may be specified with this function. If 0 is passed for a given kind
* of code, it defaults to the value of the next-highest-priority kind.
*
* This function may only be called immediately after the runtime is initialized
* and before any code is executed and/or interrupts requested.
*/
extern JS_PUBLIC_API void JS_SetNativeStackQuota(
JSContext* cx, JS::NativeStackSize systemCodeStackSize,
JS::NativeStackSize trustedScriptStackSize = 0,
JS::NativeStackSize untrustedScriptStackSize = 0);
namespace js {
enum class StackFormat { SpiderMonkey, V8, Default };
/*
* Sets the format used for stringifying Error stacks.
*
* The default format is StackFormat::SpiderMonkey. Use StackFormat::V8
* in order to emulate V8's stack formatting. StackFormat::Default can't be
* used here.
*/
extern JS_PUBLIC_API void SetStackFormat(JSContext* cx, StackFormat format);
extern JS_PUBLIC_API StackFormat GetStackFormat(JSContext* cx);
} // namespace js
namespace JS {
/**
* Capture all frames.
*/
struct AllFrames {};
/**
* Capture at most this many frames.
*/
struct MaxFrames {
uint32_t maxFrames;
explicit MaxFrames(uint32_t 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 JS_PUBLIC_API 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) {
if (principals) {
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& operator=(FirstSubsumedFrame&&) = delete;
FirstSubsumedFrame(FirstSubsumedFrame&& rhs)
: principals(rhs.principals), ignoreSelfHosted(rhs.ignoreSelfHosted) {
MOZ_ASSERT(this != &rhs, "self move disallowed");
rhs.principals = nullptr;
}
~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.
*
* 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()),
HandleObject startAfter = nullptr);
/**
* Returns true if capturing stack trace data to associate with an asynchronous
* operation is currently enabled for the current context realm.
*
* Users should check this state before capturing a stack that will be passed
* back to AutoSetAsyncStackForNewCalls later, in order to avoid capturing a
* stack for async use when we don't actually want to capture it.
*/
extern JS_PUBLIC_API bool IsAsyncStackCaptureEnabledForRealm(JSContext* cx);
/*
* 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.
*
* Here |asyncStack| is the async stack to prepare. It is copied into
* |cx|'s current compartment, and the newest frame is given
* |asyncCause| as its asynchronous cause. If |maxFrameCount| is
* |Some(n)|, capture at most the youngest |n| frames. The
* new stack object is written to |stackp|. Returns true on success,
* or sets an exception and returns |false| on error.
*/
extern JS_PUBLIC_API bool CopyAsyncStack(
JSContext* cx, HandleObject asyncStack, HandleString asyncCause,
MutableHandleObject stackp, const mozilla::Maybe<size_t>& maxFrameCount);
/**
* Given a SavedFrame JSObject stack, stringify it in the same format as
* Error.prototype.stack. The stringified stack out parameter is placed in the
* cx's compartment. Defaults to the empty string.
*
* The same notes above about SavedFrame accessors applies here as well: cx
* doesn't need to be in stack's compartment, and stack can be null, a
* SavedFrame object, or a wrapper (CCW or Xray) around a SavedFrame object.
* SavedFrames not subsumed by |principals| are skipped.
*
* Optional indent parameter specifies the number of white spaces to indent
* each line.
*/
extern JS_PUBLIC_API bool BuildStackString(
JSContext* cx, JSPrincipals* principals, HandleObject stack,
MutableHandleString stringp, size_t indent = 0,
js::StackFormat stackFormat = js::StackFormat::Default);
} // namespace JS
#endif // js_Stack_h
|