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
This commit is contained in:
parent
05a56bf1ea
commit
6b4d372696
128
src/d8.js
128
src/d8.js
@ -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]);
|
||||
}
|
||||
|
118
test/mjsunit/debug-references.js
Normal file
118
test/mjsunit/debug-references.js
Normal file
@ -0,0 +1,118 @@
|
||||
// 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");
|
Loading…
Reference in New Issue
Block a user