167 lines
4.3 KiB
JavaScript
167 lines
4.3 KiB
JavaScript
/* 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 { rawObj } = objActor;
|
|
|
|
const desc = objActor.obj.getOwnPropertyDescriptor(property);
|
|
|
|
if (
|
|
this.has(rawObj, 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.rawObj) || new Map();
|
|
|
|
objWatchpoints.set(data.property, { ...data, desc });
|
|
this._watchpoints.set(objActor.rawObj, 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 { rawObj } = objActor;
|
|
|
|
// 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 rawObj) {
|
|
this.remove(objActor, objProperty);
|
|
}
|
|
}
|
|
|
|
if (!this.has(rawObj, property)) {
|
|
return;
|
|
}
|
|
|
|
const objWatchpoints = this._watchpoints.get(rawObj);
|
|
const { desc } = objWatchpoints.get(property);
|
|
|
|
objWatchpoints.delete(property);
|
|
this._watchpoints.set(rawObj, objWatchpoints);
|
|
|
|
// We should stop keeping track of an object if it no longer
|
|
// has a watchpoint
|
|
if (objWatchpoints.size == 0) {
|
|
this._watchpoints.delete(rawObj);
|
|
}
|
|
|
|
objActor.obj.defineProperty(property, desc);
|
|
}
|
|
|
|
removeAll(objActor) {
|
|
const objWatchpoints = this._watchpoints.get(objActor.rawObj);
|
|
if (!objWatchpoints) {
|
|
return;
|
|
}
|
|
|
|
for (const objProperty in objWatchpoints) {
|
|
this.remove(objActor, objProperty);
|
|
}
|
|
}
|
|
}
|
|
|
|
exports.WatchpointMap = WatchpointMap;
|