frame.js (6116B)
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 const { Actor } = require("resource://devtools/shared/protocol/Actor.js"); 8 const { Pool } = require("resource://devtools/shared/protocol/Pool.js"); 9 const { frameSpec } = require("resource://devtools/shared/specs/frame.js"); 10 11 const Debugger = require("Debugger"); 12 const { assert } = require("resource://devtools/shared/DevToolsUtils.js"); 13 14 function formatDisplayName(frame) { 15 if (frame.type === "call") { 16 const callee = frame.callee; 17 return callee.name || callee.userDisplayName || callee.displayName; 18 } 19 20 return `(${frame.type})`; 21 } 22 23 function isDeadSavedFrame(savedFrame) { 24 return Cu && Cu.isDeadWrapper(savedFrame); 25 } 26 function isValidSavedFrame(threadActor, savedFrame) { 27 return ( 28 !isDeadSavedFrame(savedFrame) && 29 // If the frame's source is unknown to the debugger, then we ignore it 30 // since the frame likely does not belong to a realm that is marked 31 // as a debuggee. 32 // This check will also fail if the frame would have been known but was 33 // GCed before the debugger was opened on the page. 34 // TODO: Use SavedFrame's security principal to limit non-debuggee frames 35 // and pass all unknown frames to the debugger as a URL with no sourceID. 36 getSavedFrameSource(threadActor, savedFrame) 37 ); 38 } 39 function getSavedFrameSource(threadActor, savedFrame) { 40 return threadActor.sourcesManager.getSourceActorByInternalSourceId( 41 savedFrame.sourceId 42 ); 43 } 44 function getSavedFrameParent(threadActor, savedFrame) { 45 if (isDeadSavedFrame(savedFrame)) { 46 return null; 47 } 48 49 while (true) { 50 savedFrame = savedFrame.parent || savedFrame.asyncParent; 51 52 // If the saved frame is a dead wrapper, we don't have any way to keep 53 // stepping through parent frames. 54 if (!savedFrame || isDeadSavedFrame(savedFrame)) { 55 savedFrame = null; 56 break; 57 } 58 59 if (isValidSavedFrame(threadActor, savedFrame)) { 60 break; 61 } 62 } 63 return savedFrame; 64 } 65 66 /** 67 * An actor for a specified stack frame. 68 */ 69 class FrameActor extends Actor { 70 /** 71 * Creates the Frame actor. 72 * 73 * @param frame Debugger.Frame|SavedFrame 74 * The debuggee frame. 75 * @param threadActor ThreadActor 76 * The parent thread actor for this frame. 77 */ 78 constructor(frame, threadActor, depth) { 79 super(threadActor.conn, frameSpec); 80 81 this.frame = frame; 82 this.threadActor = threadActor; 83 this.depth = depth; 84 } 85 86 /** 87 * A pool that contains frame-lifetime objects, like the environment. 88 */ 89 _frameLifetimePool = null; 90 get frameLifetimePool() { 91 if (!this._frameLifetimePool) { 92 this._frameLifetimePool = new Pool(this.conn, "frame"); 93 } 94 return this._frameLifetimePool; 95 } 96 97 /** 98 * Finalization handler that is called when the actor is being evicted from 99 * the pool. 100 */ 101 destroy() { 102 if (this._frameLifetimePool) { 103 this._frameLifetimePool.destroy(); 104 this._frameLifetimePool = null; 105 } 106 super.destroy(); 107 } 108 109 getEnvironment() { 110 try { 111 if (!this.frame.environment) { 112 return {}; 113 } 114 } catch (e) { 115 // |this.frame| might not be live. FIXME Bug 1477030 we shouldn't be 116 // using frames we derived from a point where we are not currently 117 // paused at. 118 return {}; 119 } 120 121 const envActor = this.threadActor.createEnvironmentActor( 122 this.frame.environment, 123 this.frameLifetimePool 124 ); 125 126 return envActor.form(); 127 } 128 129 /** 130 * Returns a frame form for use in a protocol message. 131 */ 132 form() { 133 // SavedFrame actors have their own frame handling. 134 if (!(this.frame instanceof Debugger.Frame)) { 135 // The Frame actor shouldn't be used after evaluation is resumed, so 136 // there shouldn't be an easy way for the saved frame to be referenced 137 // once it has died. 138 assert(!isDeadSavedFrame(this.frame)); 139 140 const obj = { 141 actor: this.actorID, 142 // TODO: Bug 1610418 - Consider updating SavedFrame to have a type. 143 type: "dead", 144 asyncCause: this.frame.asyncCause, 145 state: "dead", 146 displayName: this.frame.functionDisplayName, 147 arguments: [], 148 where: { 149 // The frame's source should always be known because 150 // getSavedFrameParent will skip over frames with unknown sources. 151 actor: getSavedFrameSource(this.threadActor, this.frame).actorID, 152 line: this.frame.line, 153 // SavedFrame objects have a 1-based column number, but this API and 154 // Debugger API objects use a 0-based column value. 155 column: this.frame.column - 1, 156 }, 157 oldest: !getSavedFrameParent(this.threadActor, this.frame), 158 }; 159 160 return obj; 161 } 162 163 const threadActor = this.threadActor; 164 const form = { 165 actor: this.actorID, 166 type: this.frame.type, 167 asyncCause: this.frame.onStack ? null : "await", 168 state: this.frame.onStack ? "on-stack" : "suspended", 169 }; 170 171 if (this.depth) { 172 form.depth = this.depth; 173 } 174 175 if (this.frame.type != "wasmcall") { 176 form.this = threadActor.createValueGrip(this.frame.this); 177 } 178 179 form.displayName = formatDisplayName(this.frame); 180 form.arguments = this._args(); 181 182 if (this.frame.script) { 183 const location = this.threadActor.sourcesManager.getFrameLocation( 184 this.frame 185 ); 186 form.where = { 187 actor: location.sourceActor.actorID, 188 line: location.line, 189 column: location.column, 190 }; 191 } 192 193 if (!this.frame.older) { 194 form.oldest = true; 195 } 196 197 return form; 198 } 199 200 _args() { 201 if (!this.frame.onStack || !this.frame.arguments) { 202 return []; 203 } 204 205 return this.frame.arguments.map(arg => 206 this.threadActor.createValueGrip(arg) 207 ); 208 } 209 } 210 211 exports.FrameActor = FrameActor; 212 exports.formatDisplayName = formatDisplayName; 213 exports.getSavedFrameParent = getSavedFrameParent; 214 exports.isValidSavedFrame = isValidSavedFrame;