summaryrefslogtreecommitdiffstats
path: root/devtools/server/actors/utils/watchpoint-map.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/server/actors/utils/watchpoint-map.js')
-rw-r--r--devtools/server/actors/utils/watchpoint-map.js163
1 files changed, 163 insertions, 0 deletions
diff --git a/devtools/server/actors/utils/watchpoint-map.js b/devtools/server/actors/utils/watchpoint-map.js
new file mode 100644
index 0000000000..b1a2dd75c9
--- /dev/null
+++ b/devtools/server/actors/utils/watchpoint-map.js
@@ -0,0 +1,163 @@
+/* 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/. */
+
+"use strict";
+
+class WatchpointMap {
+ constructor(threadActor) {
+ this.threadActor = threadActor;
+ this._watchpoints = new Map();
+ }
+
+ _setWatchpoint(objActor, data) {
+ const { property, label, watchpointType } = data;
+ const obj = objActor.rawValue();
+
+ const desc = objActor.obj.getOwnPropertyDescriptor(property);
+
+ if (this.has(obj, property) || desc.set || desc.get || !desc.configurable) {
+ return null;
+ }
+
+ function getValue() {
+ return typeof desc.value === "object" && desc.value
+ ? desc.value.unsafeDereference()
+ : desc.value;
+ }
+
+ function setValue(v) {
+ desc.value = objActor.obj.makeDebuggeeValue(v);
+ }
+
+ const maybeHandlePause = type => {
+ const frame = this.threadActor.dbg.getNewestFrame();
+
+ if (
+ !this.threadActor.hasMoved(frame, type) ||
+ this.threadActor.skipBreakpointsOption ||
+ this.threadActor.sourcesManager.isFrameBlackBoxed(frame)
+ ) {
+ return;
+ }
+
+ this.threadActor._pauseAndRespond(frame, {
+ type,
+ message: label,
+ });
+ };
+
+ if (watchpointType === "get") {
+ objActor.obj.defineProperty(property, {
+ configurable: desc.configurable,
+ enumerable: desc.enumerable,
+ set: objActor.obj.makeDebuggeeValue(v => {
+ setValue(v);
+ }),
+ get: objActor.obj.makeDebuggeeValue(() => {
+ maybeHandlePause("getWatchpoint");
+ return getValue();
+ }),
+ });
+ }
+
+ if (watchpointType === "set") {
+ objActor.obj.defineProperty(property, {
+ configurable: desc.configurable,
+ enumerable: desc.enumerable,
+ set: objActor.obj.makeDebuggeeValue(v => {
+ maybeHandlePause("setWatchpoint");
+ setValue(v);
+ }),
+ get: objActor.obj.makeDebuggeeValue(() => {
+ return getValue();
+ }),
+ });
+ }
+
+ if (watchpointType === "getorset") {
+ objActor.obj.defineProperty(property, {
+ configurable: desc.configurable,
+ enumerable: desc.enumerable,
+ set: objActor.obj.makeDebuggeeValue(v => {
+ maybeHandlePause("setWatchpoint");
+ setValue(v);
+ }),
+ get: objActor.obj.makeDebuggeeValue(() => {
+ maybeHandlePause("getWatchpoint");
+ return getValue();
+ }),
+ });
+ }
+
+ return desc;
+ }
+
+ add(objActor, data) {
+ // Get the object's description before calling setWatchpoint,
+ // otherwise we'll get the modified property descriptor instead
+ const desc = this._setWatchpoint(objActor, data);
+ if (!desc) {
+ return;
+ }
+
+ const objWatchpoints =
+ this._watchpoints.get(objActor.rawValue()) || new Map();
+
+ objWatchpoints.set(data.property, { ...data, desc });
+ this._watchpoints.set(objActor.rawValue(), objWatchpoints);
+ }
+
+ has(obj, property) {
+ const objWatchpoints = this._watchpoints.get(obj);
+ return objWatchpoints && objWatchpoints.has(property);
+ }
+
+ get(obj, property) {
+ const objWatchpoints = this._watchpoints.get(obj);
+ return objWatchpoints && objWatchpoints.get(property);
+ }
+
+ remove(objActor, property) {
+ const obj = objActor.rawValue();
+
+ // This should remove watchpoints on all of the object's properties if
+ // a property isn't passed in as an argument
+ if (!property) {
+ for (const objProperty in obj) {
+ this.remove(objActor, objProperty);
+ }
+ }
+
+ if (!this.has(obj, property)) {
+ return;
+ }
+
+ const objWatchpoints = this._watchpoints.get(obj);
+ const { desc } = objWatchpoints.get(property);
+
+ objWatchpoints.delete(property);
+ this._watchpoints.set(obj, objWatchpoints);
+
+ // We should stop keeping track of an object if it no longer
+ // has a watchpoint
+ if (objWatchpoints.size == 0) {
+ this._watchpoints.delete(obj);
+ }
+
+ objActor.obj.defineProperty(property, desc);
+ }
+
+ removeAll(objActor) {
+ const objWatchpoints = this._watchpoints.get(objActor.rawValue());
+ if (!objWatchpoints) {
+ return;
+ }
+
+ for (const objProperty in objWatchpoints) {
+ this.remove(objActor, objProperty);
+ }
+ }
+}
+
+exports.WatchpointMap = WatchpointMap;