diff options
Diffstat (limited to 'js/src/debugger/NoExecute.h')
-rw-r--r-- | js/src/debugger/NoExecute.h | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/js/src/debugger/NoExecute.h b/js/src/debugger/NoExecute.h new file mode 100644 index 0000000000..49278eacf6 --- /dev/null +++ b/js/src/debugger/NoExecute.h @@ -0,0 +1,96 @@ +/* -*- 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 debugger_NoExecute_h +#define debugger_NoExecute_h + +#include "mozilla/Assertions.h" // for AssertionConditionType, MOZ_ASSERT +#include "mozilla/Attributes.h" // for MOZ_RAII + +#include "jsapi.h" + +#include "NamespaceImports.h" // for HandleScript +#include "js/Promise.h" // for JS::AutoDebuggerJobQueueInterruption + +namespace js { + +class Debugger; +class LeaveDebuggeeNoExecute; + +// Prevents all the debuggeee compartments of a given Debugger from executing +// scripts. Attempts to run script will throw an +// instance of Debugger.DebuggeeWouldRun from the topmost locked Debugger's +// compartment. +class MOZ_RAII EnterDebuggeeNoExecute { + friend class LeaveDebuggeeNoExecute; + + Debugger& dbg_; + EnterDebuggeeNoExecute** stack_; + EnterDebuggeeNoExecute* prev_; + + // Non-nullptr when unlocked temporarily by a LeaveDebuggeeNoExecute. + LeaveDebuggeeNoExecute* unlocked_; + + // When DebuggeeWouldRun is a warning instead of an error, whether we've + // reported a warning already. + bool reported_; + + public: + // Mark execution in dbg's debuggees as forbidden, for the lifetime of this + // object. Require an AutoDebuggerJobQueueInterruption in scope. + explicit EnterDebuggeeNoExecute( + JSContext* cx, Debugger& dbg, + const JS::AutoDebuggerJobQueueInterruption& adjqiProof); + + ~EnterDebuggeeNoExecute() { + MOZ_ASSERT(*stack_ == this); + *stack_ = prev_; + } + + Debugger& debugger() const { return dbg_; } + +#ifdef DEBUG + static bool isLockedInStack(JSContext* cx, Debugger& dbg); +#endif + + // Given a JSContext entered into a debuggee realm, find the lock + // that locks it. Returns nullptr if not found. + static EnterDebuggeeNoExecute* findInStack(JSContext* cx); + + // Given a JSContext entered into a debuggee compartment, report a + // warning or an error if there is a lock that locks it. + static bool reportIfFoundInStack(JSContext* cx, HandleScript script); +}; + +// Given a JSContext entered into a debuggee compartment, if it is in +// an NX section, unlock the topmost EnterDebuggeeNoExecute instance. +// +// Does nothing if debuggee is not in an NX section. For example, this +// situation arises when invocation functions are called without entering +// Debugger code, e.g., calling D.O.p.executeInGlobal or D.O.p.apply. +class MOZ_RAII LeaveDebuggeeNoExecute { + EnterDebuggeeNoExecute* prevLocked_; + + public: + explicit LeaveDebuggeeNoExecute(JSContext* cx) + : prevLocked_(EnterDebuggeeNoExecute::findInStack(cx)) { + if (prevLocked_) { + MOZ_ASSERT(!prevLocked_->unlocked_); + prevLocked_->unlocked_ = this; + } + } + + ~LeaveDebuggeeNoExecute() { + if (prevLocked_) { + MOZ_ASSERT(prevLocked_->unlocked_ == this); + prevLocked_->unlocked_ = nullptr; + } + } +}; + +} /* namespace js */ + +#endif /* debugger_NoExecute_h */ |