mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
a193ae25e8
There was a bug in this code that was caught and fixed in review, but we accidentally landed a previous version of the patch. Then it sat unnoticed for eight years because it was never called. We could land the fixed version, but it's simpler to just delete it. Differential Revision: https://phabricator.services.mozilla.com/D204132
231 lines
8.1 KiB
C++
231 lines
8.1 KiB
C++
/* -*- 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/Principals.h" // JSPrincipals, JS_HoldPrincipals, JS_DropPrincipals
|
|
#include "js/TypeDecls.h" // JSContext, Handle*, MutableHandle*
|
|
|
|
namespace JS {
|
|
|
|
using NativeStackSize = size_t;
|
|
|
|
using NativeStackBase = uintptr_t;
|
|
|
|
using NativeStackLimit = uintptr_t;
|
|
|
|
#if JS_STACK_GROWTH_DIRECTION > 0
|
|
constexpr NativeStackLimit NativeStackLimitMin = 0;
|
|
constexpr NativeStackLimit NativeStackLimitMax = UINTPTR_MAX;
|
|
#else
|
|
constexpr NativeStackLimit NativeStackLimitMin = UINTPTR_MAX;
|
|
constexpr NativeStackLimit NativeStackLimitMax = 0;
|
|
#endif
|
|
|
|
#ifdef __wasi__
|
|
// We build with the "stack-first" wasm-ld option, so the stack grows downward
|
|
// toward zero. Let's set a limit just a bit above this so that we catch an
|
|
// overflow before a Wasm trap occurs.
|
|
constexpr NativeStackLimit WASINativeStackLimit = 1024;
|
|
#endif // __wasi__
|
|
|
|
inline NativeStackLimit GetNativeStackLimit(NativeStackBase base,
|
|
NativeStackSize size) {
|
|
#if JS_STACK_GROWTH_DIRECTION > 0
|
|
MOZ_ASSERT(base <= size_t(-1) - size);
|
|
return base + size - 1;
|
|
#else // stack grows up
|
|
MOZ_ASSERT(base >= size);
|
|
return base - (size - 1);
|
|
#endif // stack grows down
|
|
}
|
|
|
|
} // namespace JS
|
|
|
|
/**
|
|
* 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()));
|
|
|
|
/**
|
|
* 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
|