Index: src/debug/debug.cc |
diff --git a/src/debug/debug.cc b/src/debug/debug.cc |
index feca580bb86085ea09beca9b0046c8a3404c11d4..d60da0321439fe72250a430565b2866f1f2753f6 100644 |
--- a/src/debug/debug.cc |
+++ b/src/debug/debug.cc |
@@ -389,6 +389,8 @@ void Debug::ThreadInit() { |
thread_local_.last_step_action_ = StepNone; |
thread_local_.last_statement_position_ = kNoSourcePosition; |
thread_local_.last_frame_count_ = -1; |
+ thread_local_.fast_forward_to_return_ = false; |
+ thread_local_.ignore_step_into_function_ = Smi::kZero; |
thread_local_.target_frame_count_ = -1; |
thread_local_.return_value_ = Smi::kZero; |
thread_local_.async_task_count_ = 0; |
@@ -418,6 +420,7 @@ int Debug::ArchiveSpacePerThread() { return 0; } |
void Debug::Iterate(ObjectVisitor* v) { |
v->VisitPointer(&thread_local_.return_value_); |
v->VisitPointer(&thread_local_.suspended_generator_); |
+ v->VisitPointer(&thread_local_.ignore_step_into_function_); |
} |
DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) { |
@@ -529,6 +532,17 @@ void Debug::Break(JavaScriptFrame* frame) { |
int target_frame_count = thread_local_.target_frame_count_; |
int last_frame_count = thread_local_.last_frame_count_; |
+ // StepOut at not return position was requested and return break locations |
+ // were flooded with one shots. |
+ if (thread_local_.fast_forward_to_return_) { |
+ DCHECK(location.IsReturn()); |
+ // We have to ignore recursive calls to function. |
+ if (current_frame_count > target_frame_count) return; |
+ ClearStepping(); |
+ PrepareStep(StepOut); |
+ return; |
+ } |
+ |
bool step_break = false; |
switch (step_action) { |
case StepNone: |
@@ -814,7 +828,8 @@ void Debug::ClearAllBreakPoints() { |
} |
} |
-void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) { |
+void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared, |
+ bool returns_only) { |
if (IsBlackboxed(shared)) return; |
// Make sure the function is compiled and has set up the debug info. |
if (!EnsureDebugInfo(shared)) return; |
@@ -822,11 +837,13 @@ void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) { |
// Flood the function with break points. |
if (debug_info->HasDebugCode()) { |
for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) { |
+ if (returns_only && !it.GetBreakLocation().IsReturn()) continue; |
it.SetDebugBreak(); |
} |
} |
if (debug_info->HasDebugBytecodeArray()) { |
for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) { |
+ if (returns_only && !it.GetBreakLocation().IsReturn()) continue; |
it.SetDebugBreak(); |
} |
} |
@@ -880,6 +897,10 @@ void Debug::PrepareStepIn(Handle<JSFunction> function) { |
if (ignore_events()) return; |
if (in_debug_scope()) return; |
if (break_disabled()) return; |
+ Handle<SharedFunctionInfo> shared(function->shared()); |
+ if (IsBlackboxed(shared)) return; |
+ if (*function == thread_local_.ignore_step_into_function_) return; |
+ thread_local_.ignore_step_into_function_ = Smi::kZero; |
FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_)); |
} |
@@ -985,7 +1006,6 @@ void Debug::PrepareStep(StepAction step_action) { |
feature_tracker()->Track(DebugFeatureTracker::kStepping); |
thread_local_.last_step_action_ = step_action; |
- UpdateHookOnFunctionCall(); |
StackTraceFrameIterator frames_it(isolate_, frame_id); |
StandardFrame* frame = frames_it.frame(); |
@@ -1012,8 +1032,19 @@ void Debug::PrepareStep(StepAction step_action) { |
BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame); |
- // Any step at a return is a step-out. |
- if (location.IsReturn()) step_action = StepOut; |
+ // Any step at a return is a step-out and we need to schedule DebugOnFunction |
+ // call callback. |
+ if (location.IsReturn()) { |
+ // On StepOut we'll ignore our further calls to current function in |
+ // PrepareStepIn callback. |
+ if (last_step_action() == StepOut) { |
+ thread_local_.ignore_step_into_function_ = *function; |
+ } |
+ step_action = StepOut; |
+ thread_local_.last_step_action_ = StepIn; |
+ } |
+ UpdateHookOnFunctionCall(); |
+ |
// A step-next at a tail call is a step-out. |
if (location.IsTailCall() && step_action == StepNext) step_action = StepOut; |
// A step-next in blackboxed function is a step-out. |
@@ -1034,6 +1065,14 @@ void Debug::PrepareStep(StepAction step_action) { |
// Clear last position info. For stepping out it does not matter. |
thread_local_.last_statement_position_ = kNoSourcePosition; |
thread_local_.last_frame_count_ = -1; |
+ if (!location.IsReturn() && !IsBlackboxed(shared)) { |
+ // At not return position we flood return positions with one shots and |
+ // will repeat StepOut automatically at next break. |
+ thread_local_.target_frame_count_ = current_frame_count; |
+ thread_local_.fast_forward_to_return_ = true; |
+ FloodWithOneShot(shared, true); |
+ return; |
+ } |
// Skip the current frame, find the first frame we want to step out to |
// and deoptimize every frame along the way. |
bool in_current_frame = true; |
@@ -1124,6 +1163,8 @@ void Debug::ClearStepping() { |
thread_local_.last_step_action_ = StepNone; |
thread_local_.last_statement_position_ = kNoSourcePosition; |
+ thread_local_.ignore_step_into_function_ = Smi::kZero; |
+ thread_local_.fast_forward_to_return_ = false; |
thread_local_.last_frame_count_ = -1; |
thread_local_.target_frame_count_ = -1; |
UpdateHookOnFunctionCall(); |