Commit 6b4d3726 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Added the 'references' command to the debugger protocol to provide access to...

Added the 'references' command to the debugger protocol to provide access to the mirror features of retreiving all the objects referencing a given object and all objects instantiated by a given function.

Added commands 'references' and 'instances' to the developer shell for using this new debugger protocol command.
Review URL: http://codereview.chromium.org/21080

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1234 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 05a56bf1
......@@ -282,6 +282,14 @@ function DebugRequest(cmd_line) {
this.request_ = this.dirCommandToJSONRequest_(args);
break;
case 'references':
this.request_ = this.referencesCommandToJSONRequest_(args);
break;
case 'instances':
this.request_ = this.instancesCommandToJSONRequest_(args);
break;
case 'source':
this.request_ = this.sourceCommandToJSONRequest_(args);
break;
......@@ -363,7 +371,7 @@ DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
// Check if the expression is a handle id in the form #<handle>#.
var handle_match = expression.match(/^#([0-9]*)#$/);
if (handle_match) {
// Build an evaluate request.
// Build a lookup request.
var request = this.createRequest('lookup');
request.arguments = {};
request.arguments.handle = parseInt(handle_match[1]);
......@@ -378,6 +386,21 @@ DebugRequest.prototype.makeEvaluateJSONRequest_ = function(expression) {
};
// Create a JSON request for the references/instances command.
DebugRequest.prototype.makeReferencesJSONRequest_ = function(handle, type) {
// Build a references request.
var handle_match = handle.match(/^#([0-9]*)#$/);
if (handle_match) {
var request = this.createRequest('references');
request.arguments = {};
request.arguments.type = type;
request.arguments.handle = parseInt(handle_match[1]);
return request.toJSONProtocol();
} else {
throw new Error('Invalid object id.');
}
};
// Create a JSON request for the continue command.
DebugRequest.prototype.continueCommandToJSONRequest_ = function(args) {
......@@ -505,6 +528,29 @@ DebugRequest.prototype.dirCommandToJSONRequest_ = function(args) {
};
// Create a JSON request for the references command.
DebugRequest.prototype.referencesCommandToJSONRequest_ = function(args) {
// Build an evaluate request from the text command.
if (args.length == 0) {
throw new Error('Missing object id.');
}
return this.makeReferencesJSONRequest_(args, 'referencedBy');
};
// Create a JSON request for the instances command.
DebugRequest.prototype.instancesCommandToJSONRequest_ = function(args) {
// Build an evaluate request from the text command.
if (args.length == 0) {
throw new Error('Missing object id.');
}
// Build a references request.
return this.makeReferencesJSONRequest_(args, 'constructedBy');
};
// Create a JSON request for the source command.
DebugRequest.prototype.sourceCommandToJSONRequest_ = function(args) {
// Build a evaluate request from the text command.
......@@ -660,6 +706,40 @@ function formatHandleReference_(value) {
}
function formatObject_(value, include_properties) {
var result = '';
result += formatHandleReference_(value);
result += ', type: object'
result += ', constructor ';
var ctor = value.constructorFunctionValue();
result += formatHandleReference_(ctor);
result += ', __proto__ ';
var proto = value.protoObjectValue();
result += formatHandleReference_(proto);
result += ', ';
result += value.propertyCount();
result += ' properties.';
if (include_properties) {
result += '\n';
for (var i = 0; i < value.propertyCount(); i++) {
result += ' ';
result += value.propertyName(i);
result += ': ';
var property_value = value.propertyValue(i);
if (property_value && property_value.type()) {
result += property_value.type();
} else {
result += '<no type>';
}
result += ' ';
result += formatHandleReference_(property_value);
result += '\n';
}
}
return result;
}
// Convert a JSON response to text for display in a text based debugger.
function DebugResponseDetails(json_response) {
details = {text:'', running:false}
......@@ -719,31 +799,7 @@ function DebugResponseDetails(json_response) {
} else {
var value = response.bodyValue();
if (value.isObject()) {
result += formatHandleReference_(value);
result += ', type: object'
result += ', constructor ';
var ctor = value.constructorFunctionValue();
result += formatHandleReference_(ctor);
result += ', __proto__ ';
var proto = value.protoObjectValue();
result += formatHandleReference_(proto);
result += ', ';
result += value.propertyCount();
result += ' properties.\n';
for (var i = 0; i < value.propertyCount(); i++) {
result += ' ';
result += value.propertyName(i);
result += ': ';
var property_value = value.propertyValue(i);
if (property_value && property_value.type()) {
result += property_value.type();
} else {
result += '<no type>';
}
result += ' ';
result += formatHandleReference_(property_value);
result += '\n';
}
result += formatObject_(value, true);
} else {
result += 'type: ';
result += value.type();
......@@ -762,6 +818,18 @@ function DebugResponseDetails(json_response) {
}
details.text = result;
break;
case 'references':
var count = body.length;
result += 'found ' + count + ' objects';
result += '\n';
for (var i = 0; i < count; i++) {
var value = response.bodyValue(i);
result += formatObject_(value, false);
result += '\n';
}
details.text = result;
break;
case 'source':
// Get the source from the response.
......@@ -915,8 +983,12 @@ ProtocolPackage.prototype.body = function() {
}
ProtocolPackage.prototype.bodyValue = function() {
return new ProtocolValue(this.packet_.body, this);
ProtocolPackage.prototype.bodyValue = function(index) {
if (IS_UNDEFINED(index)) {
return new ProtocolValue(this.packet_.body, this);
} else {
return new ProtocolValue(this.packet_.body[index], this);
}
}
......
......@@ -1078,6 +1078,8 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
this.evaluateRequest_(request, response);
} else if (request.command == 'lookup') {
this.lookupRequest_(request, response);
} else if (request.command == 'references') {
this.referencesRequest_(request, response);
} else if (request.command == 'source') {
this.sourceRequest_(request, response);
} else if (request.command == 'scripts') {
......@@ -1461,6 +1463,41 @@ DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
};
DebugCommandProcessor.prototype.referencesRequest_ =
function(request, response) {
if (!request.arguments) {
return response.failed('Missing arguments');
}
// Pull out arguments.
var type = request.arguments.type;
var handle = request.arguments.handle;
// Check for legal arguments.
if (IS_UNDEFINED(type)) {
return response.failed('Argument "type" missing');
}
if (IS_UNDEFINED(handle)) {
return response.failed('Argument "handle" missing');
}
if (type != 'referencedBy' && type != 'constructedBy') {
return response.failed('Invalid type "' + type + '"');
}
// Lookup handle and return objects with references the object.
var mirror = LookupMirror(handle);
if (mirror) {
if (type == 'referencedBy') {
response.body = mirror.referencedBy();
} else {
response.body = mirror.constructedBy();
}
} else {
return response.failed('Object #' + handle + '# not found');
}
};
DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
// No frames no source.
if (this.exec_state_.frameCount() == 0) {
......
......@@ -690,15 +690,16 @@ ObjectMirror.prototype.lookupProperty = function(value) {
/**
* Returns objects which has direct references to this object
* @param {number} opt_max_instances Optional parameter specifying the maximum
* number of instances to return.
* @param {number} opt_max_objects Optional parameter specifying the maximum
* number of referencing objects to return.
* @return {Array} The objects which has direct references to this object.
*/
ObjectMirror.prototype.referencedBy = function(opt_max_instances) {
// Find all objects constructed from this function.
var result = %DebugReferencedBy(this.value_, Mirror.prototype, opt_max_instances || 0);
ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
// Find all objects with direct references to this object.
var result = %DebugReferencedBy(this.value_,
Mirror.prototype, opt_max_objects || 0);
// Make mirrors for all the instances found.
// Make mirrors for all the references found.
for (var i = 0; i < result.length; i++) {
result[i] = MakeMirror(result[i]);
}
......
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Get the Debug object exposed from the debug context global object.
Debug = debug.Debug
listenerComplete = false;
exception = false;
// The base part of all evaluate requests.
var base_request = '"seq":0,"type":"request","command":"references"'
function safeEval(code) {
try {
return eval('(' + code + ')');
} catch (e) {
assertEquals(void 0, e);
return undefined;
}
}
function testRequest(dcp, arguments, success, count) {
// Generate request with the supplied arguments.
var request;
if (arguments) {
request = '{' + base_request + ',"arguments":' + arguments + '}';
} else {
request = '{' + base_request + '}'
}
// Process the request and check expectation.
var response = safeEval(dcp.processDebugJSONRequest(request));
if (success) {
assertTrue(response.success, request + ' -> ' + response.message);
assertTrue(response.body instanceof Array);
if (count) {
assertEquals(count, response.body.length);
} else {
assertTrue(response.body.length > 0);
}
} else {
assertFalse(response.success, request + ' -> ' + response.message);
}
assertFalse(response.running, request + ' -> expected not running');
}
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break) {
// Get the debug command processor.
var dcp = exec_state.debugCommandProcessor();
// Test some illegal references requests.
testRequest(dcp, void 0, false);
testRequest(dcp, '{"handle":"a"}', false);
testRequest(dcp, '{"handle":1}', false);
testRequest(dcp, '{"type":"referencedBy"}', false);
testRequest(dcp, '{"type":"constructedBy"}', false);
// Evaluate Point.
var evaluate_point = '{"seq":0,"type":"request","command":"evaluate",' +
'"arguments":{"expression":"Point"}}';
var response = safeEval(dcp.processDebugJSONRequest(evaluate_point));
assertTrue(response.success, "Evaluation of Point failed");
var handle = response.body.handle;
// Test some legal references requests.
testRequest(dcp, '{"handle":' + handle + ',"type":"referencedBy"}', true);
testRequest(dcp, '{"handle":' + handle + ',"type":"constructedBy"}',
true, 2);
// Indicate that all was processed.
listenerComplete = true;
}
} catch (e) {
exception = e
};
};
// Add the debug event listener.
Debug.setListener(listener);
// Test constructor and objects.
function Point(x, y) { this.x_ = x; this.y_ = y;}
p = new Point(0,0);
q = new Point(1,2);
// Enter debugger causing the event listener to be called.
debugger;
// Make sure that the debug event listener vas invoked.
assertFalse(exception, "exception in listener")
assertTrue(listenerComplete, "listener did not run to completion");
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment