Commit d63a3e13 authored by sgjesse@chromium.org's avatar sgjesse@chromium.org

Changed the debugger break handling to support situations where there are no...

Changed the debugger break handling to support situations where there are no stack frames. This can happen when an exception is thrown when compiling code.

This is related to Chromium issue 5349 (http://code.google.com/p/chromium/issues/detail?id=5349).
Review URL: http://codereview.chromium.org/13720

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@959 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 6ef835e5
...@@ -849,7 +849,12 @@ void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) { ...@@ -849,7 +849,12 @@ void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
void Debug::FloodHandlerWithOneShot() { void Debug::FloodHandlerWithOneShot() {
// Iterate through the JavaScript stack looking for handlers.
StackFrame::Id id = Top::break_frame_id(); StackFrame::Id id = Top::break_frame_id();
if (id == StackFrame::NO_ID) {
// If there is no JavaScript stack don't do anything.
return;
}
for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) { for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) {
JavaScriptFrame* frame = it.frame(); JavaScriptFrame* frame = it.frame();
if (frame->HasHandler()) { if (frame->HasHandler()) {
...@@ -886,6 +891,10 @@ void Debug::PrepareStep(StepAction step_action, int step_count) { ...@@ -886,6 +891,10 @@ void Debug::PrepareStep(StepAction step_action, int step_count) {
// hitting a break point. In other situations (e.g. unhandled exception) the // hitting a break point. In other situations (e.g. unhandled exception) the
// debug frame is not present. // debug frame is not present.
StackFrame::Id id = Top::break_frame_id(); StackFrame::Id id = Top::break_frame_id();
if (id == StackFrame::NO_ID) {
// If there is no JavaScript stack don't do anything.
return;
}
JavaScriptFrameIterator frames_it(id); JavaScriptFrameIterator frames_it(id);
JavaScriptFrame* frame = frames_it.frame(); JavaScriptFrame* frame = frames_it.frame();
......
...@@ -489,16 +489,17 @@ class DebugMessageThread: public Thread { ...@@ -489,16 +489,17 @@ class DebugMessageThread: public Thread {
// some reason could not be entered FailedToEnter will return true. // some reason could not be entered FailedToEnter will return true.
class EnterDebugger BASE_EMBEDDED { class EnterDebugger BASE_EMBEDDED {
public: public:
EnterDebugger() : set_(!it_.done()) { EnterDebugger() : has_js_frames_(!it_.done()) {
// If there is no JavaScript frames on the stack don't switch to new break // Store the previous break id and frame id.
// and break frame. break_id_ = Top::break_id();
if (set_) { break_frame_id_ = Top::break_frame_id();
// Store the previous break is and frame id.
break_id_ = Top::break_id(); // Create the new break info. If there is no JavaScript frames there is no
break_frame_id_ = Top::break_frame_id(); // break frame id.
if (has_js_frames_) {
// Create the new break info.
Top::new_break(it_.frame()->id()); Top::new_break(it_.frame()->id());
} else {
Top::new_break(StackFrame::NO_ID);
} }
// Make sure that debugger is loaded and enter the debugger context. // Make sure that debugger is loaded and enter the debugger context.
...@@ -511,21 +512,19 @@ class EnterDebugger BASE_EMBEDDED { ...@@ -511,21 +512,19 @@ class EnterDebugger BASE_EMBEDDED {
} }
~EnterDebugger() { ~EnterDebugger() {
if (set_) { // Restore to the previous break state.
// Restore to the previous break state. Top::set_break(break_frame_id_, break_id_);
Top::set_break(break_frame_id_, break_id_);
}
} }
// Check whether the debugger could be entered. // Check whether the debugger could be entered.
inline bool FailedToEnter() { return load_failed_; } inline bool FailedToEnter() { return load_failed_; }
// Check whether there are any JavaScript frames on the stack. // Check whether there are any JavaScript frames on the stack.
inline bool HasJavaScriptFrames() { return set_; } inline bool HasJavaScriptFrames() { return has_js_frames_; }
private: private:
JavaScriptFrameIterator it_; JavaScriptFrameIterator it_;
const bool set_; // Was the break actually set? const bool has_js_frames_; // Were there any JavaScript frames?
StackFrame::Id break_frame_id_; // Previous break frame id. StackFrame::Id break_frame_id_; // Previous break frame id.
int break_id_; // Previous break id. int break_id_; // Previous break id.
bool load_failed_; // Did the debugger fail to load? bool load_failed_; // Did the debugger fail to load?
......
...@@ -4764,10 +4764,8 @@ static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) { ...@@ -4764,10 +4764,8 @@ static Object* Runtime_DebugIndexedInterceptorElementValue(Arguments args) {
static Object* Runtime_CheckExecutionState(Arguments args) { static Object* Runtime_CheckExecutionState(Arguments args) {
ASSERT(args.length() >= 1); ASSERT(args.length() >= 1);
CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]); CONVERT_NUMBER_CHECKED(int, break_id, Int32, args[0]);
// Check that the break id is valid and that there is a valid frame // Check that the break id is valid.
// where execution is broken. if (Top::break_id() == 0 || break_id != Top::break_id()) {
if (break_id != Top::break_id() ||
Top::break_frame_id() == StackFrame::NO_ID) {
return Top::Throw(Heap::illegal_execution_state_symbol()); return Top::Throw(Heap::illegal_execution_state_symbol());
} }
...@@ -4786,6 +4784,10 @@ static Object* Runtime_GetFrameCount(Arguments args) { ...@@ -4786,6 +4784,10 @@ static Object* Runtime_GetFrameCount(Arguments args) {
// Count all frames which are relevant to debugging stack trace. // Count all frames which are relevant to debugging stack trace.
int n = 0; int n = 0;
StackFrame::Id id = Top::break_frame_id(); StackFrame::Id id = Top::break_frame_id();
if (id == StackFrame::NO_ID) {
// If there is no JavaScript stack frame count is 0.
return Smi::FromInt(0);
}
for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++; for (JavaScriptFrameIterator it(id); !it.done(); it.Advance()) n++;
return Smi::FromInt(n); return Smi::FromInt(n);
} }
...@@ -4827,6 +4829,10 @@ static Object* Runtime_GetFrameDetails(Arguments args) { ...@@ -4827,6 +4829,10 @@ static Object* Runtime_GetFrameDetails(Arguments args) {
// Find the relevant frame with the requested index. // Find the relevant frame with the requested index.
StackFrame::Id id = Top::break_frame_id(); StackFrame::Id id = Top::break_frame_id();
if (id == StackFrame::NO_ID) {
// If there are no JavaScript stack frames return undefined.
return Heap::undefined_value();
}
int count = 0; int count = 0;
JavaScriptFrameIterator it(id); JavaScriptFrameIterator it(id);
for (; !it.done(); it.Advance()) { for (; !it.done(); it.Advance()) {
......
...@@ -434,6 +434,15 @@ const char* frame_function_name_source = ...@@ -434,6 +434,15 @@ const char* frame_function_name_source =
"}"; "}";
v8::Local<v8::Function> frame_function_name; v8::Local<v8::Function> frame_function_name;
// 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;
// Global variable to store the last function hit - used by some tests. // Global variable to store the last function hit - used by some tests.
char last_function_hit[80]; char last_function_hit[80];
...@@ -443,6 +452,9 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event, ...@@ -443,6 +452,9 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
// Count the number of breaks. // Count the number of breaks.
if (event == v8::Break) { if (event == v8::Break) {
break_point_hit_count++; break_point_hit_count++;
...@@ -464,9 +476,11 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event, ...@@ -464,9 +476,11 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event,
} }
// Debug event handler which counts a number of events. // Debug event handler which counts a number of events and collects the stack
// height if there is a function compiled for that.
int exception_hit_count = 0; int exception_hit_count = 0;
int uncaught_exception_hit_count = 0; int uncaught_exception_hit_count = 0;
int last_js_stack_height = -1;
static void DebugEventCounterClear() { static void DebugEventCounterClear() {
break_point_hit_count = 0; break_point_hit_count = 0;
...@@ -478,6 +492,9 @@ static void DebugEventCounter(v8::DebugEvent event, ...@@ -478,6 +492,9 @@ static void DebugEventCounter(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
// Count the number of breaks. // Count the number of breaks.
if (event == v8::Break) { if (event == v8::Break) {
break_point_hit_count++; break_point_hit_count++;
...@@ -493,6 +510,16 @@ static void DebugEventCounter(v8::DebugEvent event, ...@@ -493,6 +510,16 @@ static void DebugEventCounter(v8::DebugEvent event,
uncaught_exception_hit_count++; uncaught_exception_hit_count++;
} }
} }
// Collect the JavsScript stack height if the function frame_count is
// compiled.
if (!frame_count.IsEmpty()) {
static const int kArgc = 1;
v8::Handle<v8::Value> argv[kArgc] = { exec_state };
// Using exec_state as receiver is just to have a receiver.
v8::Handle<v8::Value> result = frame_count->Call(exec_state, kArgc, argv);
last_js_stack_height = result->Int32Value();
}
} }
...@@ -523,6 +550,9 @@ static void DebugEventEvaluate(v8::DebugEvent event, ...@@ -523,6 +550,9 @@ static void DebugEventEvaluate(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
if (event == v8::Break) { if (event == v8::Break) {
for (int i = 0; checks[i].expr != NULL; i++) { for (int i = 0; checks[i].expr != NULL; i++) {
const int argc = 3; const int argc = 3;
...@@ -546,6 +576,9 @@ static void DebugEventRemoveBreakPoint(v8::DebugEvent event, ...@@ -546,6 +576,9 @@ static void DebugEventRemoveBreakPoint(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
if (event == v8::Break) { if (event == v8::Break) {
break_point_hit_count++; break_point_hit_count++;
v8::Handle<v8::Function> fun = v8::Handle<v8::Function>::Cast(data); v8::Handle<v8::Function> fun = v8::Handle<v8::Function>::Cast(data);
...@@ -561,6 +594,9 @@ static void DebugEventStep(v8::DebugEvent event, ...@@ -561,6 +594,9 @@ static void DebugEventStep(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
if (event == v8::Break) { if (event == v8::Break) {
break_point_hit_count++; break_point_hit_count++;
PrepareStep(step_action); PrepareStep(step_action);
...@@ -584,6 +620,9 @@ static void DebugEventStepSequence(v8::DebugEvent event, ...@@ -584,6 +620,9 @@ static void DebugEventStepSequence(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
if (event == v8::Break || event == v8::Exception) { if (event == v8::Break || event == v8::Exception) {
// Check that the current function is the expected. // Check that the current function is the expected.
CHECK(break_point_hit_count < CHECK(break_point_hit_count <
...@@ -611,6 +650,9 @@ static void DebugEventBreakPointCollectGarbage( ...@@ -611,6 +650,9 @@ static void DebugEventBreakPointCollectGarbage(
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
// Perform a garbage collection when break point is hit and continue. Based // Perform a garbage collection when break point is hit and continue. Based
// on the number of break points hit either scavenge or mark compact // on the number of break points hit either scavenge or mark compact
// collector is used. // collector is used.
...@@ -633,6 +675,9 @@ static void DebugEventBreak(v8::DebugEvent event, ...@@ -633,6 +675,9 @@ static void DebugEventBreak(v8::DebugEvent event,
v8::Handle<v8::Object> exec_state, v8::Handle<v8::Object> exec_state,
v8::Handle<v8::Object> event_data, v8::Handle<v8::Object> event_data,
v8::Handle<v8::Value> data) { v8::Handle<v8::Value> data) {
// When hitting a debug event listener there must be a break set.
CHECK(v8::internal::Top::is_break());
if (event == v8::Break) { if (event == v8::Break) {
// Count the number of breaks. // Count the number of breaks.
break_point_hit_count++; break_point_hit_count++;
...@@ -2164,7 +2209,7 @@ TEST(DebugStepNatives) { ...@@ -2164,7 +2209,7 @@ TEST(DebugStepNatives) {
// Test break on exceptions. For each exception break combination the number // Test break on exceptions. For each exception break combination the number
// of debug event exception callbacks and message callbacks are collected. The // of debug event exception callbacks and message callbacks are collected. The
// number of debug event exception callbacks are cused to check that the // number of debug event exception callbacks are used to check that the
// debugger is called correctly and the number of message callbacks is used to // debugger is called correctly and the number of message callbacks is used to
// check that uncaught exceptions are still returned even if there is a break // check that uncaught exceptions are still returned even if there is a break
// for them. // for them.
...@@ -2309,6 +2354,60 @@ TEST(BreakOnException) { ...@@ -2309,6 +2354,60 @@ TEST(BreakOnException) {
} }
// Test break on exception from compiler errors. When compiling using
// v8::Script::Compile there is no JavaScript stack whereas when compiling using
// eval there are JavaScript frames.
TEST(BreakOnCompileException) {
v8::HandleScope scope;
DebugLocalContext env;
v8::internal::Top::TraceException(false);
// Create a function for checking the function when hitting a break point.
frame_count = CompileFunction(&env, frame_count_source, "frame_count");
v8::V8::AddMessageListener(MessageCallbackCount);
v8::Debug::AddDebugEventListener(DebugEventCounter);
DebugEventCounterClear();
MessageCallbackCountClear();
// Check initial state.
CHECK_EQ(0, exception_hit_count);
CHECK_EQ(0, uncaught_exception_hit_count);
CHECK_EQ(0, message_callback_count);
CHECK_EQ(-1, last_js_stack_height);
// Throws SyntaxError: Unexpected end of input
v8::Script::Compile(v8::String::New("+++"));
CHECK_EQ(1, exception_hit_count);
CHECK_EQ(1, uncaught_exception_hit_count);
CHECK_EQ(1, message_callback_count);
CHECK_EQ(0, last_js_stack_height); // No JavaScript stack.
// Throws SyntaxError: Unexpected identifier
v8::Script::Compile(v8::String::New("x x"));
CHECK_EQ(2, exception_hit_count);
CHECK_EQ(2, uncaught_exception_hit_count);
CHECK_EQ(2, message_callback_count);
CHECK_EQ(0, last_js_stack_height); // No JavaScript stack.
// Throws SyntaxError: Unexpected end of input
v8::Script::Compile(v8::String::New("eval('+++')"))->Run();
CHECK_EQ(3, exception_hit_count);
CHECK_EQ(3, uncaught_exception_hit_count);
CHECK_EQ(3, message_callback_count);
CHECK_EQ(1, last_js_stack_height);
// Throws SyntaxError: Unexpected identifier
v8::Script::Compile(v8::String::New("eval('x x')"))->Run();
CHECK_EQ(4, exception_hit_count);
CHECK_EQ(4, uncaught_exception_hit_count);
CHECK_EQ(4, message_callback_count);
CHECK_EQ(1, last_js_stack_height);
}
TEST(StepWithException) { TEST(StepWithException) {
v8::HandleScope scope; v8::HandleScope scope;
DebugLocalContext env; DebugLocalContext env;
...@@ -3168,14 +3267,6 @@ TEST(SendCommandToUninitializedVM) { ...@@ -3168,14 +3267,6 @@ TEST(SendCommandToUninitializedVM) {
} }
// 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 // Source for a JavaScript function which returns the source line for the top
// frame. // frame.
static const char* frame_source_line_source = static const char* frame_source_line_source =
......
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