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