зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1513118 Part 2 - Add support for virtual console logs to ReplayDebuggerScript, r=lsmyth.
--HG-- extra : rebase_source : e9b85495fdf2a5e695b6a1031eb1890061703a1d extra : source : 5ce216ffcb1da139187cf8c62473d150df35dc5e
This commit is contained in:
Родитель
63524d8a48
Коммит
0af6ba1fa7
|
@ -691,6 +691,9 @@ function flushRecording() {
|
|||
}
|
||||
}
|
||||
|
||||
// After flushing the recording there may be more search results.
|
||||
maybeResumeSearch();
|
||||
|
||||
gLastRecordingCheckpoint = gActiveChild.lastCheckpoint();
|
||||
|
||||
// We now have a usable recording for replaying children.
|
||||
|
@ -988,16 +991,92 @@ const gControl = {
|
|||
timeWarp,
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function ConnectDebugger(dbg) {
|
||||
gDebugger = dbg;
|
||||
dbg._control = gControl;
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Search Operations
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
let gSearchChild;
|
||||
let gSearchRestartNeeded;
|
||||
|
||||
function maybeRestartSearch() {
|
||||
if (gSearchRestartNeeded && gSearchChild.paused) {
|
||||
if (gSearchChild.lastPausePoint.checkpoint != FirstCheckpointId ||
|
||||
gSearchChild.lastPausePoint.position) {
|
||||
gSearchChild.sendRestoreCheckpoint(FirstCheckpointId);
|
||||
gSearchChild.waitUntilPaused();
|
||||
}
|
||||
gSearchChild.sendClearBreakpoints();
|
||||
gDebugger._forEachSearch(pos => gSearchChild.sendAddBreakpoint(pos));
|
||||
gSearchRestartNeeded = false;
|
||||
gSearchChild.sendResume({ forward: true });
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function ChildRoleSearch() {}
|
||||
|
||||
ChildRoleSearch.prototype = {
|
||||
name: "Search",
|
||||
|
||||
initialize(child, { startup }) {
|
||||
this.child = child;
|
||||
},
|
||||
|
||||
hitExecutionPoint({ point, recordingEndpoint }) {
|
||||
if (maybeRestartSearch()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (point.position) {
|
||||
gDebugger._onSearchPause(point);
|
||||
}
|
||||
|
||||
if (!recordingEndpoint) {
|
||||
this.poke();
|
||||
}
|
||||
},
|
||||
|
||||
poke() {
|
||||
if (!gSearchRestartNeeded && !this.child.pauseNeeded) {
|
||||
this.child.sendResume({ forward: true });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function ensureHasSearchChild() {
|
||||
if (!gSearchChild) {
|
||||
gSearchChild = spawnReplayingChild(new ChildRoleSearch());
|
||||
}
|
||||
}
|
||||
|
||||
function maybeResumeSearch() {
|
||||
if (gSearchChild && gSearchChild.paused) {
|
||||
gSearchChild.sendResume({ forward: true });
|
||||
}
|
||||
}
|
||||
|
||||
const gSearchControl = {
|
||||
reset() {
|
||||
ensureHasSearchChild();
|
||||
gSearchRestartNeeded = true;
|
||||
maybeRestartSearch();
|
||||
},
|
||||
|
||||
sendRequest(request) { return gSearchChild.sendDebuggerRequest(request); },
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Utilities
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function ConnectDebugger(dbg) {
|
||||
gDebugger = dbg;
|
||||
dbg._control = gControl;
|
||||
dbg._searchControl = gSearchControl;
|
||||
}
|
||||
|
||||
function dumpv(str) {
|
||||
//dump("[ReplayControl] " + str + "\n");
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ function ReplayDebugger() {
|
|||
|
||||
// We should have been connected to control.js by the call above.
|
||||
assert(this._control);
|
||||
assert(this._searchControl);
|
||||
|
||||
// Preferred direction of travel when not explicitly resumed.
|
||||
this._direction = Direction.NONE;
|
||||
|
@ -75,6 +76,9 @@ function ReplayDebugger() {
|
|||
// After we are done pausing, callback describing how to resume.
|
||||
this._resumeCallback = null;
|
||||
|
||||
// Information about all searches that exist.
|
||||
this._searches = [];
|
||||
|
||||
// Handler called when hitting the beginning/end of the recording, or when
|
||||
// a time warp target has been reached.
|
||||
this.replayingOnForcedPause = null;
|
||||
|
@ -348,6 +352,47 @@ ReplayDebugger.prototype = {
|
|||
this._objects.length = 0;
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Search management
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
_forEachSearch(callback) {
|
||||
for (const { position } of this._searches) {
|
||||
callback(position);
|
||||
}
|
||||
},
|
||||
|
||||
_virtualConsoleLog(position, text, callback) {
|
||||
this._searches.push({ position, text, callback, results: [] });
|
||||
this._searchControl.reset();
|
||||
},
|
||||
|
||||
_onSearchPause(point) {
|
||||
for (const { position, text, callback, results } of this._searches) {
|
||||
if (RecordReplayControl.positionSubsumes(position, point.position)) {
|
||||
if (!results.some(existing => point.progress == existing.progress)) {
|
||||
let evaluateResult;
|
||||
if (text) {
|
||||
const frameData = this._searchControl.sendRequest({
|
||||
type: "getFrame",
|
||||
index: NewestFrameIndex,
|
||||
});
|
||||
if ("index" in frameData) {
|
||||
const rv = this._searchControl.sendRequest({
|
||||
type: "frameEvaluate",
|
||||
index: frameData.index,
|
||||
text,
|
||||
});
|
||||
evaluateResult = this._convertCompletionValue(rv, { forSearch: true });
|
||||
}
|
||||
}
|
||||
results.push(point);
|
||||
callback(point, evaluateResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// Breakpoint management
|
||||
/////////////////////////////////////////////////////////
|
||||
|
@ -485,14 +530,20 @@ ReplayDebugger.prototype = {
|
|||
// Object methods
|
||||
/////////////////////////////////////////////////////////
|
||||
|
||||
// Objects which |forConsole| is set are objects that were logged in console
|
||||
// messages, and had their properties recorded so that they can be inspected
|
||||
// without switching to a replaying child.
|
||||
_getObject(id, forConsole) {
|
||||
_getObject(id, options) {
|
||||
if (options && options.forSearch) {
|
||||
// Returning objects through searches is NYI.
|
||||
return "<UnknownSearchObject>";
|
||||
}
|
||||
const forConsole = options && options.forConsole;
|
||||
|
||||
if (id && !this._objects[id]) {
|
||||
const data = this._sendRequest({ type: "getObject", id });
|
||||
switch (data.kind) {
|
||||
case "Object":
|
||||
// Objects which |forConsole| is set are objects that were logged in
|
||||
// console messages, and had their properties recorded so that they can
|
||||
// be inspected without switching to a replaying child.
|
||||
this._objects[id] = new ReplayDebuggerObject(this, data, forConsole);
|
||||
break;
|
||||
case "Environment":
|
||||
|
@ -509,10 +560,10 @@ ReplayDebugger.prototype = {
|
|||
return rv;
|
||||
},
|
||||
|
||||
_convertValue(value, forConsole) {
|
||||
_convertValue(value, options) {
|
||||
if (isNonNullObject(value)) {
|
||||
if (value.object) {
|
||||
return this._getObject(value.object, forConsole);
|
||||
return this._getObject(value.object, options);
|
||||
} else if (value.special == "undefined") {
|
||||
return undefined;
|
||||
} else if (value.special == "NaN") {
|
||||
|
@ -526,12 +577,12 @@ ReplayDebugger.prototype = {
|
|||
return value;
|
||||
},
|
||||
|
||||
_convertCompletionValue(value) {
|
||||
_convertCompletionValue(value, options) {
|
||||
if ("return" in value) {
|
||||
return { return: this._convertValue(value.return) };
|
||||
return { return: this._convertValue(value.return, options) };
|
||||
}
|
||||
if ("throw" in value) {
|
||||
return { throw: this._convertValue(value.throw) };
|
||||
return { throw: this._convertValue(value.throw, options) };
|
||||
}
|
||||
ThrowError("Unexpected completion value");
|
||||
return null; // For eslint
|
||||
|
@ -582,7 +633,7 @@ ReplayDebugger.prototype = {
|
|||
if (message.messageType == "ConsoleAPI" && message.arguments) {
|
||||
for (let i = 0; i < message.arguments.length; i++) {
|
||||
message.arguments[i] = this._convertValue(message.arguments[i],
|
||||
/* forConsole = */ true);
|
||||
{ forConsole: true });
|
||||
}
|
||||
}
|
||||
return message;
|
||||
|
@ -662,6 +713,7 @@ ReplayDebuggerScript.prototype = {
|
|||
get format() { return this._data.format; },
|
||||
|
||||
_forward(type, value) {
|
||||
this._dbg._ensurePaused();
|
||||
return this._dbg._sendRequest({ type, id: this._data.id, value });
|
||||
},
|
||||
|
||||
|
@ -683,6 +735,11 @@ ReplayDebuggerScript.prototype = {
|
|||
});
|
||||
},
|
||||
|
||||
replayVirtualConsoleLog(offset, text, callback) {
|
||||
this._dbg._virtualConsoleLog({ kind: "Break", script: this._data.id, offset },
|
||||
text, callback);
|
||||
},
|
||||
|
||||
get isGeneratorFunction() { NYI(); },
|
||||
get isAsyncFunction() { NYI(); },
|
||||
getChildScripts: NYI,
|
||||
|
|
Загрузка…
Ссылка в новой задаче