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/. */
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");
11 const Debugger = require("Debugger");
12 const { assert } = require("resource://devtools/shared/DevToolsUtils.js");
15 } = require("resource://devtools/server/actors/object/utils.js");
17 function formatDisplayName(frame) {
18 if (frame.type === "call") {
19 const callee = frame.callee;
20 return callee.name || callee.userDisplayName || callee.displayName;
23 return `(${frame.type})`;
26 function isDeadSavedFrame(savedFrame) {
27 return Cu && Cu.isDeadWrapper(savedFrame);
29 function isValidSavedFrame(threadActor, savedFrame) {
31 !isDeadSavedFrame(savedFrame) &&
32 // If the frame's source is unknown to the debugger, then we ignore it
33 // since the frame likely does not belong to a realm that is marked
35 // This check will also fail if the frame would have been known but was
36 // GCed before the debugger was opened on the page.
37 // TODO: Use SavedFrame's security principal to limit non-debuggee frames
38 // and pass all unknown frames to the debugger as a URL with no sourceID.
39 getSavedFrameSource(threadActor, savedFrame)
42 function getSavedFrameSource(threadActor, savedFrame) {
43 return threadActor.sourcesManager.getSourceActorByInternalSourceId(
47 function getSavedFrameParent(threadActor, savedFrame) {
48 if (isDeadSavedFrame(savedFrame)) {
53 savedFrame = savedFrame.parent || savedFrame.asyncParent;
55 // If the saved frame is a dead wrapper, we don't have any way to keep
56 // stepping through parent frames.
57 if (!savedFrame || isDeadSavedFrame(savedFrame)) {
62 if (isValidSavedFrame(threadActor, savedFrame)) {
70 * An actor for a specified stack frame.
72 class FrameActor extends Actor {
74 * Creates the Frame actor.
76 * @param frame Debugger.Frame|SavedFrame
78 * @param threadActor ThreadActor
79 * The parent thread actor for this frame.
81 constructor(frame, threadActor, depth) {
82 super(threadActor.conn, frameSpec);
85 this.threadActor = threadActor;
90 * A pool that contains frame-lifetime objects, like the environment.
92 _frameLifetimePool = null;
93 get frameLifetimePool() {
94 if (!this._frameLifetimePool) {
95 this._frameLifetimePool = new Pool(this.conn, "frame");
97 return this._frameLifetimePool;
101 * Finalization handler that is called when the actor is being evicted from
105 if (this._frameLifetimePool) {
106 this._frameLifetimePool.destroy();
107 this._frameLifetimePool = null;
114 if (!this.frame.environment) {
118 // |this.frame| might not be live. FIXME Bug 1477030 we shouldn't be
119 // using frames we derived from a point where we are not currently
124 const envActor = this.threadActor.createEnvironmentActor(
125 this.frame.environment,
126 this.frameLifetimePool
129 return envActor.form();
133 * Returns a frame form for use in a protocol message.
136 // SavedFrame actors have their own frame handling.
137 if (!(this.frame instanceof Debugger.Frame)) {
138 // The Frame actor shouldn't be used after evaluation is resumed, so
139 // there shouldn't be an easy way for the saved frame to be referenced
141 assert(!isDeadSavedFrame(this.frame));
145 // TODO: Bug 1610418 - Consider updating SavedFrame to have a type.
147 asyncCause: this.frame.asyncCause,
149 displayName: this.frame.functionDisplayName,
152 // The frame's source should always be known because
153 // getSavedFrameParent will skip over frames with unknown sources.
154 actor: getSavedFrameSource(this.threadActor, this.frame).actorID,
155 line: this.frame.line,
156 // SavedFrame objects have a 1-based column number, but this API and
157 // Debugger API objects use a 0-based column value.
158 column: this.frame.column - 1,
160 oldest: !getSavedFrameParent(this.threadActor, this.frame),
166 const threadActor = this.threadActor;
169 type: this.frame.type,
170 asyncCause: this.frame.onStack ? null : "await",
171 state: this.frame.onStack ? "on-stack" : "suspended",
175 form.depth = this.depth;
178 if (this.frame.type != "wasmcall") {
179 form.this = createValueGrip(
181 threadActor._pausePool,
182 threadActor.objectGrip
186 form.displayName = formatDisplayName(this.frame);
187 form.arguments = this._args();
189 if (this.frame.script) {
190 const location = this.threadActor.sourcesManager.getFrameLocation(
194 actor: location.sourceActor.actorID,
196 column: location.column,
200 if (!this.frame.older) {
208 if (!this.frame.onStack || !this.frame.arguments) {
212 return this.frame.arguments.map(arg =>
215 this.threadActor._pausePool,
216 this.threadActor.objectGrip
222 exports.FrameActor = FrameActor;
223 exports.formatDisplayName = formatDisplayName;
224 exports.getSavedFrameParent = getSavedFrameParent;
225 exports.isValidSavedFrame = isValidSavedFrame;