Source code

Revision control

Copy as Markdown

Other Tools

/* 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.shouldSkipAnyBreakpoint ||
!this.threadActor.hasMoved(frame, type) ||
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;