watchpoint-map.js (4428B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 class WatchpointMap { 8 constructor(threadActor) { 9 this.threadActor = threadActor; 10 this._watchpoints = new Map(); 11 } 12 13 _setWatchpoint(objActor, data) { 14 const { property, label, watchpointType } = data; 15 const { rawObj } = objActor; 16 17 const desc = objActor.obj.getOwnPropertyDescriptor(property); 18 19 if ( 20 this.has(rawObj, property) || 21 desc.set || 22 desc.get || 23 !desc.configurable 24 ) { 25 return null; 26 } 27 28 function getValue() { 29 return typeof desc.value === "object" && desc.value 30 ? desc.value.unsafeDereference() 31 : desc.value; 32 } 33 34 function setValue(v) { 35 desc.value = objActor.obj.makeDebuggeeValue(v); 36 } 37 38 const maybeHandlePause = type => { 39 const frame = this.threadActor.dbg.getNewestFrame(); 40 41 if ( 42 this.threadActor.shouldSkipAnyBreakpoint || 43 !this.threadActor.hasMoved(frame, type) || 44 this.threadActor.sourcesManager.isFrameBlackBoxed(frame) 45 ) { 46 return; 47 } 48 49 this.threadActor._pauseAndRespond(frame, { 50 type, 51 message: label, 52 }); 53 }; 54 55 if (watchpointType === "get") { 56 objActor.obj.defineProperty(property, { 57 configurable: desc.configurable, 58 enumerable: desc.enumerable, 59 set: objActor.obj.makeDebuggeeValue(v => { 60 setValue(v); 61 }), 62 get: objActor.obj.makeDebuggeeValue(() => { 63 maybeHandlePause("getWatchpoint"); 64 return getValue(); 65 }), 66 }); 67 } 68 69 if (watchpointType === "set") { 70 objActor.obj.defineProperty(property, { 71 configurable: desc.configurable, 72 enumerable: desc.enumerable, 73 set: objActor.obj.makeDebuggeeValue(v => { 74 maybeHandlePause("setWatchpoint"); 75 setValue(v); 76 }), 77 get: objActor.obj.makeDebuggeeValue(() => { 78 return getValue(); 79 }), 80 }); 81 } 82 83 if (watchpointType === "getorset") { 84 objActor.obj.defineProperty(property, { 85 configurable: desc.configurable, 86 enumerable: desc.enumerable, 87 set: objActor.obj.makeDebuggeeValue(v => { 88 maybeHandlePause("setWatchpoint"); 89 setValue(v); 90 }), 91 get: objActor.obj.makeDebuggeeValue(() => { 92 maybeHandlePause("getWatchpoint"); 93 return getValue(); 94 }), 95 }); 96 } 97 98 return desc; 99 } 100 101 add(objActor, data) { 102 // Get the object's description before calling setWatchpoint, 103 // otherwise we'll get the modified property descriptor instead 104 const desc = this._setWatchpoint(objActor, data); 105 if (!desc) { 106 return; 107 } 108 109 const objWatchpoints = this._watchpoints.get(objActor.rawObj) || new Map(); 110 111 objWatchpoints.set(data.property, { ...data, desc }); 112 this._watchpoints.set(objActor.rawObj, objWatchpoints); 113 } 114 115 has(obj, property) { 116 const objWatchpoints = this._watchpoints.get(obj); 117 return objWatchpoints && objWatchpoints.has(property); 118 } 119 120 get(obj, property) { 121 const objWatchpoints = this._watchpoints.get(obj); 122 return objWatchpoints && objWatchpoints.get(property); 123 } 124 125 remove(objActor, property) { 126 const { rawObj } = objActor; 127 128 // This should remove watchpoints on all of the object's properties if 129 // a property isn't passed in as an argument 130 if (!property) { 131 for (const objProperty in rawObj) { 132 this.remove(objActor, objProperty); 133 } 134 } 135 136 if (!this.has(rawObj, property)) { 137 return; 138 } 139 140 const objWatchpoints = this._watchpoints.get(rawObj); 141 const { desc } = objWatchpoints.get(property); 142 143 objWatchpoints.delete(property); 144 this._watchpoints.set(rawObj, objWatchpoints); 145 146 // We should stop keeping track of an object if it no longer 147 // has a watchpoint 148 if (objWatchpoints.size == 0) { 149 this._watchpoints.delete(rawObj); 150 } 151 152 objActor.obj.defineProperty(property, desc); 153 } 154 155 removeAll(objActor) { 156 const objWatchpoints = this._watchpoints.get(objActor.rawObj); 157 if (!objWatchpoints) { 158 return; 159 } 160 161 for (const objProperty in objWatchpoints) { 162 this.remove(objActor, objProperty); 163 } 164 } 165 } 166 167 exports.WatchpointMap = WatchpointMap;