Index: src/debug.cc |
=================================================================== |
--- src/debug.cc (revision 2827) |
+++ src/debug.cc (working copy) |
@@ -75,6 +75,9 @@ |
BreakLocatorType type) { |
debug_info_ = debug_info; |
type_ = type; |
+ // Get the stub early to avoid possible GC during iterations. We may need |
+ // this stub to detect debugger calls generated from debugger statements. |
+ debug_break_stub_ = RuntimeStub(Runtime::kDebugBreak, 0).GetCode(); |
reloc_iterator_ = NULL; |
reloc_iterator_original_ = NULL; |
Reset(); // Initialize the rest of the member variables. |
@@ -126,6 +129,10 @@ |
return; |
} |
if (code->kind() == Code::STUB) { |
+ if (IsDebuggerStatement()) { |
+ break_point_++; |
+ return; |
+ } |
if (type_ == ALL_BREAK_LOCATIONS) { |
if (Debug::IsBreakStub(code)) { |
break_point_++; |
@@ -238,7 +245,7 @@ |
if (!HasBreakPoint()) { |
SetDebugBreak(); |
} |
- ASSERT(IsDebugBreak()); |
+ ASSERT(IsDebugBreak() || IsDebuggerStatement()); |
// Set the break point information. |
DebugInfo::SetBreakPoint(debug_info_, code_position(), |
position(), statement_position(), |
@@ -258,6 +265,11 @@ |
void BreakLocationIterator::SetOneShot() { |
+ // Debugger statement always calls debugger. No need to modify it. |
+ if (IsDebuggerStatement()) { |
+ return; |
+ } |
+ |
// If there is a real break point here no more to do. |
if (HasBreakPoint()) { |
ASSERT(IsDebugBreak()); |
@@ -270,6 +282,11 @@ |
void BreakLocationIterator::ClearOneShot() { |
+ // Debugger statement always calls debugger. No need to modify it. |
+ if (IsDebuggerStatement()) { |
+ return; |
+ } |
+ |
// If there is a real break point here no more to do. |
if (HasBreakPoint()) { |
ASSERT(IsDebugBreak()); |
@@ -283,6 +300,11 @@ |
void BreakLocationIterator::SetDebugBreak() { |
+ // Debugger statement always calls debugger. No need to modify it. |
+ if (IsDebuggerStatement()) { |
+ return; |
+ } |
+ |
// If there is already a break point here just return. This might happen if |
// the same code is flooded with break points twice. Flooding the same |
// function twice might happen when stepping in a function with an exception |
@@ -303,6 +325,11 @@ |
void BreakLocationIterator::ClearDebugBreak() { |
+ // Debugger statement always calls debugger. No need to modify it. |
+ if (IsDebuggerStatement()) { |
+ return; |
+ } |
+ |
if (RelocInfo::IsJSReturn(rmode())) { |
// Restore the frame exit code. |
ClearDebugBreakAtReturn(); |
@@ -317,10 +344,10 @@ |
void BreakLocationIterator::PrepareStepIn() { |
HandleScope scope; |
- // Step in can only be prepared if currently positioned on an IC call or |
- // construct call. |
+ // Step in can only be prepared if currently positioned on an IC call, |
+ // construct call or CallFunction stub call. |
Address target = rinfo()->target_address(); |
- Code* code = Code::GetCodeFromTargetAddress(target); |
+ Handle<Code> code(Code::GetCodeFromTargetAddress(target)); |
if (code->is_call_stub()) { |
// Step in through IC call is handled by the runtime system. Therefore make |
// sure that the any current IC is cleared and the runtime system is |
@@ -334,11 +361,29 @@ |
rinfo()->set_target_address(stub->entry()); |
} |
} else { |
+#ifdef DEBUG |
+ // All the following stuff is needed only for assertion checks so the code |
+ // is wrapped in ifdef. |
+ Handle<Code> maybe_call_function_stub = code; |
+ if (IsDebugBreak()) { |
+ Address original_target = original_rinfo()->target_address(); |
+ maybe_call_function_stub = |
+ Handle<Code>(Code::GetCodeFromTargetAddress(original_target)); |
+ } |
+ bool is_call_function_stub = |
+ (maybe_call_function_stub->kind() == Code::STUB && |
+ maybe_call_function_stub->major_key() == CodeStub::CallFunction); |
+ |
// Step in through construct call requires no changes to the running code. |
// Step in through getters/setters should already be prepared as well |
// because caller of this function (Debug::PrepareStep) is expected to |
// flood the top frame's function with one shot breakpoints. |
- ASSERT(RelocInfo::IsConstructCall(rmode()) || code->is_inline_cache_stub()); |
+ // Step in through CallFunction stub should also be prepared by caller of |
+ // this function (Debug::PrepareStep) which should flood target function |
+ // with breakpoints. |
+ ASSERT(RelocInfo::IsConstructCall(rmode()) || code->is_inline_cache_stub() |
+ || is_call_function_stub); |
+#endif |
} |
} |
@@ -409,6 +454,21 @@ |
} |
+bool BreakLocationIterator::IsDebuggerStatement() { |
+ if (RelocInfo::IsCodeTarget(rmode())) { |
+ Address target = original_rinfo()->target_address(); |
+ Code* code = Code::GetCodeFromTargetAddress(target); |
+ if (code->kind() == Code::STUB) { |
+ CodeStub::Major major_key = code->major_key(); |
+ if (major_key == CodeStub::Runtime) { |
+ return (*debug_break_stub_ == code); |
+ } |
+ } |
+ } |
+ return false; |
+} |
+ |
+ |
Object* BreakLocationIterator::BreakPointObjects() { |
return debug_info_->GetBreakPointObjects(code_position()); |
} |
@@ -1092,6 +1152,7 @@ |
bool is_call_target = false; |
bool is_load_or_store = false; |
bool is_inline_cache_stub = false; |
+ Handle<Code> call_function_stub; |
if (RelocInfo::IsCodeTarget(it.rinfo()->rmode())) { |
Address target = it.rinfo()->target_address(); |
Code* code = Code::GetCodeFromTargetAddress(target); |
@@ -1102,6 +1163,22 @@ |
is_inline_cache_stub = true; |
is_load_or_store = !is_call_target; |
} |
+ |
+ // Check if target code is CallFunction stub. |
+ Code* maybe_call_function_stub = code; |
+ // If there is a breakpoint at this line look at the original code to |
+ // check if it is a CallFunction stub. |
+ if (it.IsDebugBreak()) { |
+ Address original_target = it.original_rinfo()->target_address(); |
+ maybe_call_function_stub = |
+ Code::GetCodeFromTargetAddress(original_target); |
+ } |
+ if (maybe_call_function_stub->kind() == Code::STUB && |
+ maybe_call_function_stub->major_key() == CodeStub::CallFunction) { |
+ // Save reference to the code as we may need it to find out arguments |
+ // count for 'step in' later. |
+ call_function_stub = Handle<Code>(maybe_call_function_stub); |
+ } |
} |
// If this is the last break code target step out is the only possibility. |
@@ -1114,7 +1191,8 @@ |
JSFunction* function = JSFunction::cast(frames_it.frame()->function()); |
FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared())); |
} |
- } else if (!(is_inline_cache_stub || RelocInfo::IsConstructCall(it.rmode())) |
+ } else if (!(is_inline_cache_stub || RelocInfo::IsConstructCall(it.rmode()) || |
+ !call_function_stub.is_null()) |
|| step_action == StepNext || step_action == StepMin) { |
// Step next or step min. |
@@ -1126,6 +1204,45 @@ |
debug_info->code()->SourceStatementPosition(frame->pc()); |
thread_local_.last_fp_ = frame->fp(); |
} else { |
+ // If it's CallFunction stub ensure target function is compiled and flood |
+ // it with one shot breakpoints. |
+ if (!call_function_stub.is_null()) { |
+ // Find out number of arguments from the stub minor key. |
+ // Reverse lookup required as the minor key cannot be retrieved |
+ // from the code object. |
+ Handle<Object> obj( |
+ Heap::code_stubs()->SlowReverseLookup(*call_function_stub)); |
+ ASSERT(*obj != Heap::undefined_value()); |
+ ASSERT(obj->IsSmi()); |
+ // Get the STUB key and extract major and minor key. |
+ uint32_t key = Smi::cast(*obj)->value(); |
+ // Argc in the stub is the number of arguments passed - not the |
+ // expected arguments of the called function. |
+ int call_function_arg_count = CodeStub::MinorKeyFromKey(key); |
+ ASSERT(call_function_stub->major_key() == |
+ CodeStub::MajorKeyFromKey(key)); |
+ |
+ // Find target function on the expression stack. |
+ // Expression stack lools like this (top to bottom): |
+ // argN |
+ // ... |
+ // arg0 |
+ // Receiver |
+ // Function to call |
+ int expressions_count = frame->ComputeExpressionsCount(); |
+ ASSERT(expressions_count - 2 - call_function_arg_count >= 0); |
+ Object* fun = frame->GetExpression( |
+ expressions_count - 2 - call_function_arg_count); |
+ if (fun->IsJSFunction()) { |
+ Handle<JSFunction> js_function(JSFunction::cast(fun)); |
+ // Don't step into builtins. |
+ if (!js_function->IsBuiltin()) { |
+ // It will also compile target function if it's not compiled yet. |
+ FloodWithOneShot(Handle<SharedFunctionInfo>(js_function->shared())); |
+ } |
+ } |
+ } |
+ |
// Fill the current function with one-shot break points even for step in on |
// a call target as the function called might be a native function for |
// which step in will not stop. It also prepares for stepping in |