| 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,
|
|
|