Commit 80c02907 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Added a debugger call to run a JavaScript function in the debugger. When...

Added a debugger call to run a JavaScript function in the debugger. When called the debugger will be entered and the JavaScript function will be called with the debugger ExecutionState object as its first parameter.

This makes it possible to get information like current line number, current script resource, backtrace information etc. which is not part of the normal API.
Review URL: http://codereview.chromium.org/12472

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@854 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 75eda476
......@@ -128,6 +128,26 @@ class EXPORT Debug {
// Message based interface. The message protocol is JSON.
static void SetMessageHandler(DebugMessageHandler handler, void* data = NULL);
static void SendCommand(const uint16_t* command, int length);
/**
* Run a JavaScript function in the debugger.
* \param fun the function to call
* \param data passed as second argument to the function
* With this call the debugger is entered and the function specified is called
* with the execution state as the first argument. This makes it possible to
* get access to information otherwise not available during normal JavaScript
* execution e.g. details on stack frames. The following example show a
* JavaScript function which when passed to v8::Debug::Call will return the
* current line of JavaScript execution.
*
* \code
* function frame_source_line(exec_state) {
* return exec_state.frame(0).sourceLine();
* }
* \endcode
*/
static Handle<Value> Call(v8::Handle<v8::Function> fun,
Handle<Value> data = Handle<Value>());
};
......
......@@ -2902,6 +2902,26 @@ void Debug::SendCommand(const uint16_t* command, int length) {
}
Handle<Value> Debug::Call(v8::Handle<v8::Function> fun,
v8::Handle<v8::Value> data) {
if (!i::V8::HasBeenSetup()) return Handle<Value>();
ON_BAILOUT("v8::Debug::Call()", return Handle<Value>());
i::Handle<i::Object> result;
EXCEPTION_PREAMBLE();
if (data.IsEmpty()) {
result = i::Debugger::Call(Utils::OpenHandle(*fun),
i::Factory::undefined_value(),
&has_pending_exception);
} else {
result = i::Debugger::Call(Utils::OpenHandle(*fun),
Utils::OpenHandle(*data),
&has_pending_exception);
}
EXCEPTION_BAILOUT_CHECK(Local<Value>());
return Utils::ToLocal(result);
}
namespace internal {
......
......@@ -1650,6 +1650,30 @@ void Debugger::UpdateActiveDebugger() {
}
Handle<Object> Debugger::Call(Handle<JSFunction> fun,
Handle<Object> data,
bool* pending_exception) {
// Enter the debugger.
EnterDebugger debugger;
if (debugger.FailedToEnter() || !debugger.HasJavaScriptFrames()) {
return Factory::undefined_value();
}
// Create the execution state.
bool caught_exception = false;
Handle<Object> exec_state = MakeExecutionState(&caught_exception);
if (caught_exception) {
return Factory::undefined_value();
}
static const int kArgc = 2;
Object** argv[kArgc] = { exec_state.location(), data.location() };
Handle<Object> result = Execution::Call(fun, Factory::undefined_value(),
kArgc, argv, pending_exception);
return result;
}
DebugMessageThread::DebugMessageThread()
: host_running_(true),
command_queue_(kQueueInitialSize),
......
......@@ -358,6 +358,10 @@ class Debugger {
static void SendMessage(Vector<uint16_t> message);
static void ProcessCommand(Vector<const uint16_t> command);
static void UpdateActiveDebugger();
static Handle<Object> Call(Handle<JSFunction> fun,
Handle<Object> data,
bool* pending_exception);
inline static bool EventActive(v8::DebugEvent event) {
// Currently argument event is not used.
return !Debugger::compiling_natives_ && Debugger::debugger_active_;
......@@ -504,6 +508,9 @@ class EnterDebugger BASE_EMBEDDED {
// Check whether the debugger could be entered.
inline bool FailedToEnter() { return load_failed_; }
// Check whether there are any JavaScript frames on the stack.
inline bool HasJavaScriptFrames() { return set_; }
private:
JavaScriptFrameIterator it_;
const bool set_; // Was the break actually set?
......
......@@ -3166,3 +3166,155 @@ TEST(SendCommandToUninitializedVM) {
int dummy_length = AsciiToUtf16(dummy_command, dummy_buffer);
v8::Debug::SendCommand(dummy_buffer, dummy_length);
}
// Source for The JavaScript function which returns the number of frames.
static const char* frame_count_source =
"function frame_count(exec_state) {"
" return exec_state.frameCount();"
"}";
v8::Handle<v8::Function> frame_count;
// Source for a JavaScript function which returns the source line for the top
// frame.
static const char* frame_source_line_source =
"function frame_source_line(exec_state) {"
" return exec_state.frame(0).sourceLine();"
"}";
v8::Handle<v8::Function> frame_source_line;
// Source for a JavaScript function which returns the data parameter of a
// function called in the context of the debugger. If no data parameter is
// passed it throws an exception.
static const char* debugger_call_with_data_source =
"function debugger_call_with_data(exec_state, data) {"
" if (data) return data;"
" throw 'No data!'"
"}";
v8::Handle<v8::Function> debugger_call_with_data;
// Source for a JavaScript function which returns the data parameter of a
// function called in the context of the debugger. If no data parameter is
// passed it throws an exception.
static const char* debugger_call_with_closure_source =
"var x = 3;"
"function (exec_state) {"
" if (exec_state.y) return x - 1;"
" exec_state.y = x;"
" return exec_state.y"
"}";
v8::Handle<v8::Function> debugger_call_with_closure;
// Function to retrieve the number of JavaScript frames by calling a JavaScript
// in the debugger.
static v8::Handle<v8::Value> CheckFrameCount(const v8::Arguments& args) {
CHECK(v8::Debug::Call(frame_count)->IsNumber());
CHECK_EQ(args[0]->Int32Value(),
v8::Debug::Call(frame_count)->Int32Value());
return v8::Undefined();
}
// Function to retrieve the source line of the top JavaScript frame by calling a
// JavaScript function in the debugger.
static v8::Handle<v8::Value> CheckSourceLine(const v8::Arguments& args) {
CHECK(v8::Debug::Call(frame_source_line)->IsNumber());
CHECK_EQ(args[0]->Int32Value(),
v8::Debug::Call(frame_source_line)->Int32Value());
return v8::Undefined();
}
// Function to test passing an additional parameter to a JavaScript function
// called in the debugger. It also tests that functions called in the debugger
// can throw exceptions.
static v8::Handle<v8::Value> CheckDataParameter(const v8::Arguments& args) {
v8::Handle<v8::String> data = v8::String::New("Test");
CHECK(v8::Debug::Call(debugger_call_with_data, data)->IsString());
CHECK(v8::Debug::Call(debugger_call_with_data).IsEmpty());
CHECK(v8::Debug::Call(debugger_call_with_data).IsEmpty());
v8::TryCatch catcher;
v8::Debug::Call(debugger_call_with_data);
CHECK(catcher.HasCaught());
CHECK(catcher.Exception()->IsString());
return v8::Undefined();
}
// Function to test using a JavaScript with closure in the debugger.
static v8::Handle<v8::Value> CheckClosure(const v8::Arguments& args) {
CHECK(v8::Debug::Call(debugger_call_with_closure)->IsNumber());
CHECK_EQ(3, v8::Debug::Call(debugger_call_with_closure)->Int32Value());
return v8::Undefined();
}
// Test functions called through the debugger.
TEST(CallFunctionInDebugger) {
// Create and enter a context with the functions CheckFrameCount,
// CheckSourceLine and CheckDataParameter installed.
v8::HandleScope scope;
v8::Handle<v8::ObjectTemplate> global_template = v8::ObjectTemplate::New();
global_template->Set(v8::String::New("CheckFrameCount"),
v8::FunctionTemplate::New(CheckFrameCount));
global_template->Set(v8::String::New("CheckSourceLine"),
v8::FunctionTemplate::New(CheckSourceLine));
global_template->Set(v8::String::New("CheckDataParameter"),
v8::FunctionTemplate::New(CheckDataParameter));
global_template->Set(v8::String::New("CheckClosure"),
v8::FunctionTemplate::New(CheckClosure));
v8::Handle<v8::Context> context = v8::Context::New(NULL, global_template);
v8::Context::Scope context_scope(context);
// Compile a function for checking the number of JavaScript frames.
v8::Script::Compile(v8::String::New(frame_count_source))->Run();
frame_count = v8::Local<v8::Function>::Cast(
context->Global()->Get(v8::String::New("frame_count")));
// Compile a function for returning the source line for the top frame.
v8::Script::Compile(v8::String::New(frame_source_line_source))->Run();
frame_source_line = v8::Local<v8::Function>::Cast(
context->Global()->Get(v8::String::New("frame_source_line")));
// Compile a function returning the data parameter.
v8::Script::Compile(v8::String::New(debugger_call_with_data_source))->Run();
debugger_call_with_data = v8::Local<v8::Function>::Cast(
context->Global()->Get(v8::String::New("debugger_call_with_data")));
// Compile a function capturing closure.
debugger_call_with_closure = v8::Local<v8::Function>::Cast(
v8::Script::Compile(
v8::String::New(debugger_call_with_closure_source))->Run());
// Calling a function through the debugger returns undefined if there are no
// JavaScript frames.
CHECK(v8::Debug::Call(frame_count)->IsUndefined());
CHECK(v8::Debug::Call(frame_source_line)->IsUndefined());
CHECK(v8::Debug::Call(debugger_call_with_data)->IsUndefined());
// Test that the number of frames can be retrieved.
v8::Script::Compile(v8::String::New("CheckFrameCount(1)"))->Run();
v8::Script::Compile(v8::String::New("function f() {"
" CheckFrameCount(2);"
"}; f()"))->Run();
// Test that the source line can be retrieved.
v8::Script::Compile(v8::String::New("CheckSourceLine(0)"))->Run();
v8::Script::Compile(v8::String::New("function f() {\n"
" CheckSourceLine(1)\n"
" CheckSourceLine(2)\n"
" CheckSourceLine(3)\n"
"}; f()"))->Run();
// Test that a parameter can be passed to a function called in the debugger.
v8::Script::Compile(v8::String::New("CheckDataParameter()"))->Run();
// Test that a function with closure can be run in the debugger.
v8::Script::Compile(v8::String::New("CheckClosure()"))->Run();
}
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