summaryrefslogtreecommitdiffstats
path: root/js/src/debugger/NoExecute.h
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/debugger/NoExecute.h')
-rw-r--r--js/src/debugger/NoExecute.h96
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 */