Bug 724228 - Use getVariable instead of getVariableDescriptor until the latter lands; r=dcamp

This commit is contained in:
Panos Astithas 2012-03-21 15:49:23 +00:00
parent 794ec49e06
commit dfb60bd8de
6 changed files with 237 additions and 93 deletions

View File

@ -470,6 +470,38 @@ StackFrames.prototype = {
this._addExpander(thisVar, frame.this);
}
if (frame.environment) {
// Add nodes for every argument.
let variables = frame.environment.bindings.arguments;
for each (let variable in variables) {
let name = Object.getOwnPropertyNames(variable)[0];
let paramVar = localScope.addVar(name);
let paramVal = variable[name].value;
paramVar.setGrip(paramVal);
this._addExpander(paramVar, paramVal);
}
// Add nodes for every other variable in scope.
variables = frame.environment.bindings.variables;
for (let variable in variables) {
let paramVar = localScope.addVar(variable);
let paramVal = variables[variable].value;
paramVar.setGrip(paramVal);
this._addExpander(paramVar, paramVal);
}
// If we already found 'arguments', we are done here.
if ("arguments" in frame.environment.bindings.variables) {
// Signal that variables have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
return;
}
}
// Sometimes in call frames with arguments we don't get 'arguments' in the
// environment (bug 746601) and we have to construct it manually. Note, that
// in this case arguments.callee will be absent, even in the cases where it
// shouldn't be.
if (frame.arguments && frame.arguments.length > 0) {
// Add "arguments".
let argsVar = localScope.addVar("arguments");
@ -479,33 +511,20 @@ StackFrames.prototype = {
});
this._addExpander(argsVar, frame.arguments);
// Add variables for every argument.
let objClient = this.activeThread.pauseGrip(frame.callee);
objClient.getSignature(function SF_getSignature(aResponse) {
for (let i = 0, l = aResponse.parameters.length; i < l; i++) {
let param = aResponse.parameters[i];
let paramVar = localScope.addVar(param);
let paramVal = frame.arguments[i];
paramVar.setGrip(paramVal);
this._addExpander(paramVar, paramVal);
}
// Signal that call parameters have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedParameters");
}.bind(this));
// Signal that variables have been fetched.
DebuggerController.dispatchEvent("Debugger:FetchedVariables");
}
},
/**
* Adds a onexpand callback for a variable, lazily handling the addition of
* Adds an 'onexpand' callback for a variable, lazily handling the addition of
* new properties.
*/
_addExpander: function SF__addExpander(aVar, aObject) {
// No need for expansion for null and undefined values, but we do need them
// for frame.arguments which is a regular array.
if (!aObject || typeof aObject !== "object" ||
if (!aVar || !aObject || typeof aObject !== "object" ||
(aObject.type !== "object" && !Array.isArray(aObject))) {
return;
}

View File

@ -27,10 +27,10 @@ function testFrameParameters()
{
dump("Started testFrameParameters!\n");
gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
dump("Entered Debugger:FetchedParameters!\n");
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
dump("Entered Debugger:FetchedVariables!\n");
gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
dump("After currentThread.dispatch!\n");
@ -52,33 +52,42 @@ function testFrameParameters()
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
"Should have three frames.");
is(localNodes.length, 8,
is(localNodes.length, 11,
"The localScope should contain all the created variable elements.");
is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
"Should have the right property value for 'this'.");
is(localNodes[1].querySelector(".info").textContent, "[object Arguments]",
"Should have the right property value for 'arguments'.");
is(localNodes[2].querySelector(".info").textContent, "[object Object]",
is(localNodes[1].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'aArg'.");
is(localNodes[3].querySelector(".info").textContent, '"beta"',
is(localNodes[2].querySelector(".info").textContent, '"beta"',
"Should have the right property value for 'bArg'.");
is(localNodes[4].querySelector(".info").textContent, "3",
is(localNodes[3].querySelector(".info").textContent, "3",
"Should have the right property value for 'cArg'.");
is(localNodes[5].querySelector(".info").textContent, "false",
is(localNodes[4].querySelector(".info").textContent, "false",
"Should have the right property value for 'dArg'.");
is(localNodes[6].querySelector(".info").textContent, "null",
is(localNodes[5].querySelector(".info").textContent, "null",
"Should have the right property value for 'eArg'.");
is(localNodes[7].querySelector(".info").textContent, "undefined",
is(localNodes[6].querySelector(".info").textContent, "undefined",
"Should have the right property value for 'fArg'.");
is(localNodes[7].querySelector(".info").textContent, "1",
"Should have the right property value for 'a'.");
is(localNodes[8].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'b'.");
is(localNodes[9].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'c'.");
is(localNodes[10].querySelector(".info").textContent, "[object Arguments]",
"Should have the right property value for 'arguments'.");
resumeAndFinish();
}}, 0);
}, false);

View File

@ -27,10 +27,10 @@ function testFrameParameters()
{
dump("Started testFrameParameters!\n");
gDebugger.addEventListener("Debugger:FetchedParameters", function test() {
dump("Entered Debugger:FetchedParameters!\n");
gDebugger.addEventListener("Debugger:FetchedVariables", function test() {
dump("Entered Debugger:FetchedVariables!\n");
gDebugger.removeEventListener("Debugger:FetchedParameters", test, false);
gDebugger.removeEventListener("Debugger:FetchedVariables", test, false);
Services.tm.currentThread.dispatch({ run: function() {
dump("After currentThread.dispatch!\n");
@ -50,16 +50,17 @@ function testFrameParameters()
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
"Should have three frames.");
is(localNodes.length, 8,
is(localNodes.length, 11,
"The localScope should contain all the created variable elements.");
is(localNodes[0].querySelector(".info").textContent, "[object Proxy]",
"Should have the right property value for 'this'.");
// Expand the __proto__ and arguments tree nodes. This causes their
// properties to be retrieved and displayed.
// Expand the '__proto__', 'arguments' and 'a' tree nodes. This causes
// their properties to be retrieved and displayed.
localNodes[0].expand();
localNodes[1].expand();
localNodes[9].expand();
localNodes[10].expand();
// Poll every few milliseconds until the properties are retrieved.
// It's important to set the timer in the chrome window, because the
@ -70,7 +71,9 @@ function testFrameParameters()
ok(false, "Timed out while polling for the properties.");
resumeAndFinish();
}
if (!localNodes[0].fetched || !localNodes[1].fetched) {
if (!localNodes[0].fetched ||
!localNodes[9].fetched ||
!localNodes[10].fetched) {
return;
}
window.clearInterval(intervalID);
@ -82,14 +85,26 @@ function testFrameParameters()
.textContent.search(/object/) != -1,
"__proto__ should be an object.");
is(localNodes[1].querySelector(".info").textContent, "[object Arguments]",
is(localNodes[9].querySelector(".info").textContent, "[object Object]",
"Should have the right property value for 'c'.");
is(localNodes[9].querySelectorAll(".property > .title > .key")[1]
.textContent, "a",
"Should have the right property name for 'a'.");
is(localNodes[9].querySelectorAll(".property > .title > .value")[1]
.textContent, 1,
"Should have the right value for 'c.a'.");
is(localNodes[10].querySelector(".info").textContent,
"[object Arguments]",
"Should have the right property value for 'arguments'.");
is(localNodes[1].querySelector(".property > .title > .key")
is(localNodes[10].querySelector(".property > .title > .key")
.textContent, "length",
"Should have the right property name for length.");
"Should have the right property name for 'length'.");
is(localNodes[1].querySelector(".property > .title > .value")
is(localNodes[10].querySelector(".property > .title > .value")
.textContent, 5,
"Should have the right argument length.");
@ -104,7 +119,8 @@ function testFrameParameters()
}
function resumeAndFinish() {
gDebugger.DebuggerController.activeThread.addOneTimeListener("framescleared", function() {
let thread = gDebugger.DebuggerController.activeThread;
thread.addOneTimeListener("framescleared", function() {
Services.tm.currentThread.dispatch({ run: function() {
var frames = gDebugger.DebuggerView.StackFrames._frames;
@ -115,7 +131,7 @@ function resumeAndFinish() {
}}, 0);
});
gDebugger.DebuggerController.activeThread.resume();
thread.resume();
}
registerCleanupFunction(function() {

View File

@ -714,29 +714,29 @@ ThreadActor.prototype = {
},
/**
* Create and return an environment actor that corresponds to the
* Debugger.Environment for the provided object.
* @param Debugger.Object aObject
* The object whose lexical environment we want to extract.
* Create and return an environment actor that corresponds to the provided
* Debugger.Environment.
* @param Debugger.Environment aEnvironment
* The lexical environment we want to extract.
* @param object aPool
* The pool where the newly-created actor will be placed.
* @return The EnvironmentActor for aObject or undefined for host functions or
* functions scoped to a non-debuggee global.
* @return The EnvironmentActor for aEnvironment or undefined for host
* functions or functions scoped to a non-debuggee global.
*/
createEnvironmentActor: function TA_createEnvironmentActor(aObject, aPool) {
let environment = aObject.environment;
if (!environment) {
createEnvironmentActor:
function TA_createEnvironmentActor(aEnvironment, aPool) {
if (!aEnvironment) {
return undefined;
}
if (environment.actor) {
return environment.actor;
if (aEnvironment.actor) {
return aEnvironment.actor;
}
let actor = new EnvironmentActor(aObject, this);
let actor = new EnvironmentActor(aEnvironment, this);
this._environmentActors.push(actor);
aPool.addActor(actor);
environment.actor = actor;
aEnvironment.actor = actor;
return actor;
},
@ -1054,7 +1054,7 @@ ObjectActor.prototype = {
let descriptor = {};
descriptor.configurable = aObject.configurable;
descriptor.enumerable = aObject.enumerable;
if (aObject.value) {
if (aObject.value !== undefined) {
descriptor.writable = aObject.writable;
descriptor.value = this.threadActor.createValueGrip(aObject.value);
} else {
@ -1102,7 +1102,7 @@ ObjectActor.prototype = {
" 'Function' class." };
}
let envActor = this.threadActor.createEnvironmentActor(this.obj,
let envActor = this.threadActor.createEnvironmentActor(this.obj.environment,
this.registeredPool);
if (!envActor) {
return { error: "notDebuggee",
@ -1110,7 +1110,7 @@ ObjectActor.prototype = {
}
return { name: this.obj.name || null,
scope: envActor.form() };
scope: envActor.form(this.obj) };
},
/**
@ -1239,9 +1239,9 @@ FrameActor.prototype = {
}
let envActor = this.threadActor
.createEnvironmentActor(this.frame,
.createEnvironmentActor(this.frame.environment,
this.frameLifetimePool);
form.environment = envActor ? envActor.form() : envActor;
form.environment = envActor ? envActor.form(this.frame) : envActor;
form.this = this.threadActor.createValueGrip(this.frame.this);
form.arguments = this._args();
if (this.frame.script) {
@ -1350,14 +1350,14 @@ BreakpointActor.prototype.requestTypes = {
* the bindings introduced by a lexical environment and assigning new values to
* those identifier bindings.
*
* @param Debugger.Object aObject
* The object whose lexical environment will be used to create the actor.
* @param Debugger.Environment aEnvironment
* The lexical environment that will be used to create the actor.
* @param ThreadActor aThreadActor
* The parent thread actor that contains this environment.
*/
function EnvironmentActor(aObject, aThreadActor)
function EnvironmentActor(aEnvironment, aThreadActor)
{
this.obj = aObject;
this.obj = aEnvironment;
this.threadActor = aThreadActor;
}
@ -1365,41 +1365,47 @@ EnvironmentActor.prototype = {
actorPrefix: "environment",
/**
* Returns an environment form for use in a protocol message.
* Returns an environment form for use in a protocol message. Note that the
* requirement of passing the frame or function as a parameter is only
* temporary, since when bug 747514 lands, the environment will have a callee
* property that will contain it.
*
* @param object aObject
* The stack frame or function object whose environment bindings are
* being generated.
*/
form: function EA_form() {
form: function EA_form(aObject) {
// Debugger.Frame might be dead by the time we get here, which will cause
// accessing its properties to throw.
if (!this.obj.live) {
if (!aObject.live) {
return undefined;
}
let parent;
if (this.obj.environment.parent) {
parent = this.threadActor
.createEnvironmentActor(this.obj.environment.parent,
this.registeredPool);
if (this.obj.parent) {
let thread = this.threadActor;
parent = thread.createEnvironmentActor(this.obj.parent.environment,
this.registeredPool);
}
let form = { actor: this.actorID,
parent: parent ? parent.form() : parent };
parent: parent ? parent.form(this.obj.parent) : parent };
if (this.obj.environment.type == "object") {
if (this.obj.environment.parent) {
if (aObject.type == "object") {
if (this.obj.parent) {
form.type = "with";
} else {
form.type = "object";
}
form.object = this.threadActor.createValueGrip(this.obj.environment.object);
form.object = this.threadActor.createValueGrip(aObject.object);
} else {
if (this.obj.class == "Function") {
if (aObject.class == "Function") {
form.type = "function";
form.function = this.threadActor.createValueGrip(this.obj);
form.functionName = this.obj.name;
form.function = this.threadActor.createValueGrip(aObject);
form.functionName = aObject.name;
} else {
form.type = "block";
}
form.bindings = this._bindings();
form.bindings = this._bindings(aObject);
}
return form;
@ -1407,19 +1413,41 @@ EnvironmentActor.prototype = {
/**
* Return the identifier bindings object as required by the remote protocol
* specification.
* specification. Note that the requirement of passing the frame or function
* as a parameter is only temporary, since when bug 747514 lands, the
* environment will have a callee property that will contain it.
*
* @param object aObject [optional]
* The stack frame or function object whose environment bindings are
* being generated. When left unspecified, the bindings do not contain
* an 'arguments' property.
*/
_bindings: function EA_bindings() {
_bindings: function EA_bindings(aObject) {
let bindings = { arguments: [], variables: {} };
// TODO: this will be redundant after bug 692984 is fixed.
if (typeof this.obj.environment.getVariableDescriptor != "function") {
// TODO: this part should be removed in favor of the commented-out part
// below when getVariableDescriptor lands (bug 725815).
if (typeof this.obj.getVariable != "function") {
//if (typeof this.obj.getVariableDescriptor != "function") {
return bindings;
}
for (let name in this.obj.parameterNames) {
let parameterNames;
if (aObject && aObject.callee) {
parameterNames = aObject.callee.parameterNames;
}
for each (let name in parameterNames) {
let arg = {};
let desc = this.obj.environment.getVariableDescriptor(name);
// TODO: this part should be removed in favor of the commented-out part
// below when getVariableDescriptor lands (bug 725815).
let desc = {
value: this.obj.getVariable(name),
configurable: false,
writable: true,
enumerable: true
};
// let desc = this.obj.getVariableDescriptor(name);
let descForm = {
enumerable: true,
configurable: desc.configurable
@ -1435,14 +1463,22 @@ EnvironmentActor.prototype = {
bindings.arguments.push(arg);
}
for (let name in this.obj.environment.names()) {
for each (let name in this.obj.names()) {
if (bindings.arguments.some(function exists(element) {
return !!element[name];
})) {
continue;
}
let desc = this.obj.environment.getVariableDescriptor(name);
// TODO: this part should be removed in favor of the commented-out part
// below when getVariableDescriptor lands.
let desc = {
value: this.obj.getVariable(name),
configurable: false,
writable: true,
enumerable: true
};
//let desc = this.obj.getVariableDescriptor(name);
let descForm = {
enumerable: true,
configurable: desc.configurable
@ -1468,7 +1504,7 @@ EnvironmentActor.prototype = {
* The protocol request object.
*/
onAssign: function EA_onAssign(aRequest) {
let desc = this.obj.environment.getVariableDescriptor(aRequest.name);
let desc = this.obj.getVariableDescriptor(aRequest.name);
if (!desc.writable) {
return { error: "immutableBinding",
@ -1477,7 +1513,7 @@ EnvironmentActor.prototype = {
}
try {
this.obj.environment.setVariable(aRequest.name, aRequest.value);
this.obj.setVariable(aRequest.name, aRequest.value);
} catch (e) {
if (e instanceof Debugger.DebuggeeWouldRun) {
return { error: "threadWouldRun",

View File

@ -0,0 +1,63 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* Check a frame actor's bindings property.
*/
var gDebuggee;
var gClient;
var gThreadClient;
function run_test()
{
initTestDebuggerServer();
gDebuggee = addTestGlobal("test-stack");
gClient = new DebuggerClient(DebuggerServer.connectPipe());
gClient.connect(function() {
attachTestGlobalClientAndResume(gClient, "test-stack", function(aResponse, aThreadClient) {
gThreadClient = aThreadClient;
test_pause_frame();
});
});
do_test_pending();
}
function test_pause_frame()
{
gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
let bindings = aPacket.frame.environment.bindings;
let args = bindings.arguments;
let vars = bindings.variables;
do_check_eq(args.length, 6);
do_check_eq(args[0].aNumber.value, 42);
do_check_eq(args[1].aBool.value, true);
do_check_eq(args[2].aString.value, "nasu");
do_check_eq(args[3].aNull.value.type, "null");
do_check_eq(args[4].aUndefined.value.type, "undefined");
do_check_eq(args[5].aObject.value.type, "object");
do_check_eq(args[5].aObject.value.class, "Object");
do_check_true(!!args[5].aObject.value.actor);
do_check_eq(vars.a.value, 1);
do_check_eq(vars.b.value, true);
do_check_eq(vars.c.value.type, "object");
do_check_eq(vars.c.value.class, "Object");
do_check_true(!!vars.c.value.actor);
gThreadClient.resume(function() {
finishClient(gClient);
});
});
gDebuggee.eval("(" + function() {
function stopMe(aNumber, aBool, aString, aNull, aUndefined, aObject) {
var a = 1;
var b = true;
var c = { a: "a" };
debugger;
};
stopMe(42, true, "nasu", null, undefined, { foo: "bar" });
} + ")()");
}

View File

@ -53,3 +53,4 @@ tail =
[test_stepping-02.js]
[test_stepping-03.js]
[test_stepping-04.js]
[test_framebindings-01.js]