Index: src/debug.cc |
=================================================================== |
--- src/debug.cc (revision 9531) |
+++ src/debug.cc (working copy) |
@@ -40,6 +40,7 @@ |
#include "global-handles.h" |
#include "ic.h" |
#include "ic-inl.h" |
+#include "isolate-inl.h" |
#include "list.h" |
#include "messages.h" |
#include "natives.h" |
@@ -401,15 +402,15 @@ |
// Step in can only be prepared if currently positioned on an IC call, |
// construct call or CallFunction stub call. |
Address target = rinfo()->target_address(); |
- Handle<Code> code(Code::GetCodeFromTargetAddress(target)); |
- if (code->is_call_stub() || code->is_keyed_call_stub()) { |
+ Handle<Code> target_code(Code::GetCodeFromTargetAddress(target)); |
+ if (target_code->is_call_stub() || target_code->is_keyed_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 |
// called. If the executing code has a debug break at the location change |
// the call in the original code as it is the code there that will be |
// executed in place of the debug break call. |
- Handle<Code> stub = ComputeCallDebugPrepareStepIn(code->arguments_count(), |
- code->kind()); |
+ Handle<Code> stub = ComputeCallDebugPrepareStepIn( |
+ target_code->arguments_count(), target_code->kind()); |
if (IsDebugBreak()) { |
original_rinfo()->set_target_address(stub->entry()); |
} else { |
@@ -419,7 +420,7 @@ |
#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; |
+ Handle<Code> maybe_call_function_stub = target_code; |
if (IsDebugBreak()) { |
Address original_target = original_rinfo()->target_address(); |
maybe_call_function_stub = |
@@ -436,8 +437,9 @@ |
// 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); |
+ ASSERT(RelocInfo::IsConstructCall(rmode()) || |
+ target_code->is_inline_cache_stub() || |
+ is_call_function_stub); |
#endif |
} |
} |
@@ -474,11 +476,11 @@ |
RelocInfo::Mode mode = rmode(); |
if (RelocInfo::IsCodeTarget(mode)) { |
Address target = rinfo()->target_address(); |
- Handle<Code> code(Code::GetCodeFromTargetAddress(target)); |
+ Handle<Code> target_code(Code::GetCodeFromTargetAddress(target)); |
// Patch the code to invoke the builtin debug break function matching the |
// calling convention used by the call site. |
- Handle<Code> dbgbrk_code(Debug::FindDebugBreak(code, mode)); |
+ Handle<Code> dbgbrk_code(Debug::FindDebugBreak(target_code, mode)); |
rinfo()->set_target_address(dbgbrk_code->entry()); |
} |
} |
@@ -772,7 +774,7 @@ |
// Execute the shared function in the debugger context. |
Handle<Context> context = isolate->global_context(); |
- bool caught_exception = false; |
+ bool caught_exception; |
Handle<JSFunction> function = |
factory->NewFunctionFromSharedFunctionInfo(function_info, context); |
@@ -1103,7 +1105,7 @@ |
Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id()); |
// Call HandleBreakPointx. |
- bool caught_exception = false; |
+ bool caught_exception; |
const int argc = 2; |
Object** argv[argc] = { |
break_id.location(), |
@@ -1732,6 +1734,10 @@ |
if (!has_break_points_) { |
Deoptimizer::DeoptimizeAll(); |
+ // We are going to iterate heap to find all functions without |
+ // debug break slots. |
+ isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask); |
+ |
AssertNoAllocation no_allocation; |
Builtins* builtins = isolate_->builtins(); |
Code* lazy_compile = builtins->builtin(Builtins::kLazyCompile); |
@@ -1997,9 +2003,10 @@ |
// Perform two GCs to get rid of all unreferenced scripts. The first GC gets |
// rid of all the cached script wrappers and the second gets rid of the |
- // scripts which are no longer referenced. |
- heap->CollectAllGarbage(false); |
- heap->CollectAllGarbage(false); |
+ // scripts which are no longer referenced. The second also sweeps precisely, |
+ // which saves us doing yet another GC to make the heap iterable. |
+ heap->CollectAllGarbage(Heap::kNoGCFlags); |
+ heap->CollectAllGarbage(Heap::kMakeHeapIterableMask); |
ASSERT(script_cache_ == NULL); |
script_cache_ = new ScriptCache(); |
@@ -2007,6 +2014,8 @@ |
// Scan heap for Script objects. |
int count = 0; |
HeapIterator iterator; |
+ AssertNoAllocation no_allocation; |
+ |
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) { |
if (obj->IsScript() && Script::cast(obj)->HasValidSource()) { |
script_cache_->Add(Handle<Script>(Script::cast(obj))); |
@@ -2047,7 +2056,7 @@ |
// Perform GC to get unreferenced scripts evicted from the cache before |
// returning the content. |
- isolate_->heap()->CollectAllGarbage(false); |
+ isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags); |
// Get the scripts from the cache. |
return script_cache_->GetScripts(); |
@@ -2345,7 +2354,7 @@ |
Handle<JSValue> wrapper = GetScriptWrapper(script); |
// Call UpdateScriptBreakPoints expect no exceptions. |
- bool caught_exception = false; |
+ bool caught_exception; |
const int argc = 1; |
Object** argv[argc] = { reinterpret_cast<Object**>(wrapper.location()) }; |
Execution::TryCall(Handle<JSFunction>::cast(update_script_break_points), |
@@ -2486,7 +2495,7 @@ |
exec_state.location(), |
Handle<Object>::cast(event_data).location(), |
event_listener_data_.location() }; |
- bool caught_exception = false; |
+ bool caught_exception; |
Execution::TryCall(fun, isolate_->global(), argc, argv, &caught_exception); |
// Silently ignore exceptions from debug event listeners. |
} |
@@ -2929,6 +2938,94 @@ |
} |
+EnterDebugger::EnterDebugger() |
+ : isolate_(Isolate::Current()), |
+ prev_(isolate_->debug()->debugger_entry()), |
+ it_(isolate_), |
+ has_js_frames_(!it_.done()), |
+ save_(isolate_) { |
+ Debug* debug = isolate_->debug(); |
+ ASSERT(prev_ != NULL || !debug->is_interrupt_pending(PREEMPT)); |
+ ASSERT(prev_ != NULL || !debug->is_interrupt_pending(DEBUGBREAK)); |
+ |
+ // Link recursive debugger entry. |
+ debug->set_debugger_entry(this); |
+ |
+ // Store the previous break id and frame id. |
+ break_id_ = debug->break_id(); |
+ break_frame_id_ = debug->break_frame_id(); |
+ |
+ // Create the new break info. If there is no JavaScript frames there is no |
+ // break frame id. |
+ if (has_js_frames_) { |
+ debug->NewBreak(it_.frame()->id()); |
+ } else { |
+ debug->NewBreak(StackFrame::NO_ID); |
+ } |
+ |
+ // Make sure that debugger is loaded and enter the debugger context. |
+ load_failed_ = !debug->Load(); |
+ if (!load_failed_) { |
+ // NOTE the member variable save which saves the previous context before |
+ // this change. |
+ isolate_->set_context(*debug->debug_context()); |
+ } |
+} |
+ |
+ |
+EnterDebugger::~EnterDebugger() { |
+ ASSERT(Isolate::Current() == isolate_); |
+ Debug* debug = isolate_->debug(); |
+ |
+ // Restore to the previous break state. |
+ debug->SetBreak(break_frame_id_, break_id_); |
+ |
+ // Check for leaving the debugger. |
+ if (prev_ == NULL) { |
+ // Clear mirror cache when leaving the debugger. Skip this if there is a |
+ // pending exception as clearing the mirror cache calls back into |
+ // JavaScript. This can happen if the v8::Debug::Call is used in which |
+ // case the exception should end up in the calling code. |
+ if (!isolate_->has_pending_exception()) { |
+ // Try to avoid any pending debug break breaking in the clear mirror |
+ // cache JavaScript code. |
+ if (isolate_->stack_guard()->IsDebugBreak()) { |
+ debug->set_interrupts_pending(DEBUGBREAK); |
+ isolate_->stack_guard()->Continue(DEBUGBREAK); |
+ } |
+ debug->ClearMirrorCache(); |
+ } |
+ |
+ // Request preemption and debug break when leaving the last debugger entry |
+ // if any of these where recorded while debugging. |
+ if (debug->is_interrupt_pending(PREEMPT)) { |
+ // This re-scheduling of preemption is to avoid starvation in some |
+ // debugging scenarios. |
+ debug->clear_interrupt_pending(PREEMPT); |
+ isolate_->stack_guard()->Preempt(); |
+ } |
+ if (debug->is_interrupt_pending(DEBUGBREAK)) { |
+ debug->clear_interrupt_pending(DEBUGBREAK); |
+ isolate_->stack_guard()->DebugBreak(); |
+ } |
+ |
+ // If there are commands in the queue when leaving the debugger request |
+ // that these commands are processed. |
+ if (isolate_->debugger()->HasCommands()) { |
+ isolate_->stack_guard()->DebugCommand(); |
+ } |
+ |
+ // If leaving the debugger with the debugger no longer active unload it. |
+ if (!isolate_->debugger()->IsDebuggerActive()) { |
+ isolate_->debugger()->UnloadDebugger(); |
+ } |
+ } |
+ |
+ // Leaving this debugger entry. |
+ debug->set_debugger_entry(prev_); |
+} |
+ |
+ |
MessageImpl MessageImpl::NewEvent(DebugEvent event, |
bool running, |
Handle<JSObject> exec_state, |