| Index: src/debug.cc
|
| diff --git a/src/debug.cc b/src/debug.cc
|
| index a4cce055db78a0cf86c8ec452e97ed5794480883..f6086abf84f9f17de41b2a7b8162209b2a4f6d70 100644
|
| --- a/src/debug.cc
|
| +++ b/src/debug.cc
|
| @@ -80,6 +80,22 @@ BreakLocationIterator::~BreakLocationIterator() {
|
| }
|
|
|
|
|
| +// Check whether a code stub with the specified major key is a possible break
|
| +// point location when looking for source break locations.
|
| +static bool IsSourceBreakStub(Code* code) {
|
| + CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
| + return major_key == CodeStub::CallFunction;
|
| +}
|
| +
|
| +
|
| +// Check whether a code stub with the specified major key is a possible break
|
| +// location.
|
| +static bool IsBreakStub(Code* code) {
|
| + CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
| + return major_key == CodeStub::CallFunction;
|
| +}
|
| +
|
| +
|
| void BreakLocationIterator::Next() {
|
| DisallowHeapAllocation no_gc;
|
| ASSERT(!RinfoDone());
|
| @@ -129,15 +145,14 @@ void BreakLocationIterator::Next() {
|
| if (IsDebuggerStatement()) {
|
| break_point_++;
|
| return;
|
| - }
|
| - if (type_ == ALL_BREAK_LOCATIONS) {
|
| - if (Debug::IsBreakStub(code)) {
|
| + } else if (type_ == ALL_BREAK_LOCATIONS) {
|
| + if (IsBreakStub(code)) {
|
| break_point_++;
|
| return;
|
| }
|
| } else {
|
| ASSERT(type_ == SOURCE_BREAK_LOCATIONS);
|
| - if (Debug::IsSourceBreakStub(code)) {
|
| + if (IsSourceBreakStub(code)) {
|
| break_point_++;
|
| return;
|
| }
|
| @@ -422,6 +437,53 @@ bool BreakLocationIterator::IsDebugBreak() {
|
| }
|
|
|
|
|
| +// Find the builtin to use for invoking the debug break
|
| +static Handle<Code> DebugBreakForIC(Handle<Code> code, RelocInfo::Mode mode) {
|
| + Isolate* isolate = code->GetIsolate();
|
| +
|
| + // Find the builtin debug break function matching the calling convention
|
| + // used by the call site.
|
| + if (code->is_inline_cache_stub()) {
|
| + switch (code->kind()) {
|
| + case Code::CALL_IC:
|
| + return isolate->builtins()->CallICStub_DebugBreak();
|
| +
|
| + case Code::LOAD_IC:
|
| + return isolate->builtins()->LoadIC_DebugBreak();
|
| +
|
| + case Code::STORE_IC:
|
| + return isolate->builtins()->StoreIC_DebugBreak();
|
| +
|
| + case Code::KEYED_LOAD_IC:
|
| + return isolate->builtins()->KeyedLoadIC_DebugBreak();
|
| +
|
| + case Code::KEYED_STORE_IC:
|
| + return isolate->builtins()->KeyedStoreIC_DebugBreak();
|
| +
|
| + case Code::COMPARE_NIL_IC:
|
| + return isolate->builtins()->CompareNilIC_DebugBreak();
|
| +
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| + }
|
| + if (RelocInfo::IsConstructCall(mode)) {
|
| + if (code->has_function_cache()) {
|
| + return isolate->builtins()->CallConstructStub_Recording_DebugBreak();
|
| + } else {
|
| + return isolate->builtins()->CallConstructStub_DebugBreak();
|
| + }
|
| + }
|
| + if (code->kind() == Code::STUB) {
|
| + ASSERT(code->major_key() == CodeStub::CallFunction);
|
| + return isolate->builtins()->CallFunctionStub_DebugBreak();
|
| + }
|
| +
|
| + UNREACHABLE();
|
| + return Handle<Code>::null();
|
| +}
|
| +
|
| +
|
| void BreakLocationIterator::SetDebugBreakAtIC() {
|
| // Patch the original code with the current address as the current address
|
| // might have changed by the inline caching since the code was copied.
|
| @@ -434,7 +496,7 @@ void BreakLocationIterator::SetDebugBreakAtIC() {
|
|
|
| // 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(target_code, mode));
|
| + Handle<Code> dbgbrk_code = DebugBreakForIC(target_code, mode);
|
| rinfo()->set_target_address(dbgbrk_code->entry());
|
| }
|
| }
|
| @@ -504,7 +566,6 @@ void Debug::ThreadInit() {
|
| thread_local_.step_out_fp_ = 0;
|
| // TODO(isolates): frames_are_dropped_?
|
| thread_local_.debugger_entry_ = NULL;
|
| - thread_local_.has_pending_interrupt_ = false;
|
| thread_local_.restarter_frame_function_pointer_ = NULL;
|
| thread_local_.promise_on_stack_ = NULL;
|
| }
|
| @@ -530,6 +591,30 @@ int Debug::ArchiveSpacePerThread() {
|
| }
|
|
|
|
|
| +ScriptCache::ScriptCache(Isolate* isolate) : HashMap(HashMap::PointersMatch),
|
| + isolate_(isolate),
|
| + collected_scripts_(10) {
|
| + Heap* heap = isolate_->heap();
|
| + HandleScope scope(isolate_);
|
| +
|
| + // 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(Heap::kMakeHeapIterableMask, "ScriptCache");
|
| + heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "ScriptCache");
|
| +
|
| + // Scan heap for Script objects.
|
| + HeapIterator iterator(heap);
|
| + DisallowHeapAllocation no_allocation;
|
| +
|
| + for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
| + if (obj->IsScript() && Script::cast(obj)->HasValidSource()) {
|
| + Add(Handle<Script>(Script::cast(obj)));
|
| + }
|
| + }
|
| +}
|
| +
|
| +
|
| void ScriptCache::Add(Handle<Script> script) {
|
| GlobalHandles* global_handles = isolate_->global_handles();
|
| // Create an entry in the hash map for the script.
|
| @@ -785,7 +870,10 @@ void Debug::Unload() {
|
| if (!is_loaded()) return;
|
|
|
| // Clear the script cache.
|
| - DestroyScriptCache();
|
| + if (script_cache_ != NULL) {
|
| + delete script_cache_;
|
| + script_cache_ = NULL;
|
| + }
|
|
|
| // Clear debugger context global handle.
|
| GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location());
|
| @@ -843,7 +931,8 @@ void Debug::Break(Arguments args, JavaScriptFrame* frame) {
|
|
|
| // If step out is active skip everything until the frame where we need to step
|
| // out to is reached, unless real breakpoint is hit.
|
| - if (StepOutActive() && frame->fp() != step_out_fp() &&
|
| + if (StepOutActive() &&
|
| + frame->fp() != thread_local_.step_out_fp_ &&
|
| break_points_hit->IsUndefined() ) {
|
| // Step count should always be 0 for StepOut.
|
| ASSERT(thread_local_.step_count_ == 0);
|
| @@ -1192,7 +1281,7 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) {
|
| }
|
|
|
|
|
| -Debug::PromiseOnStack::PromiseOnStack(Isolate* isolate,
|
| +PromiseOnStack::PromiseOnStack(Isolate* isolate,
|
| PromiseOnStack* prev,
|
| Handle<JSFunction> getter)
|
| : isolate_(isolate), prev_(prev) {
|
| @@ -1203,7 +1292,7 @@ Debug::PromiseOnStack::PromiseOnStack(Isolate* isolate,
|
| }
|
|
|
|
|
| -Debug::PromiseOnStack::~PromiseOnStack() {
|
| +PromiseOnStack::~PromiseOnStack() {
|
| isolate_->global_handles()->Destroy(Handle<Object>::cast(getter_).location());
|
| }
|
|
|
| @@ -1530,67 +1619,7 @@ bool Debug::IsDebugBreak(Address addr) {
|
| }
|
|
|
|
|
| -// Check whether a code stub with the specified major key is a possible break
|
| -// point location when looking for source break locations.
|
| -bool Debug::IsSourceBreakStub(Code* code) {
|
| - CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
| - return major_key == CodeStub::CallFunction;
|
| -}
|
| -
|
| -
|
| -// Check whether a code stub with the specified major key is a possible break
|
| -// location.
|
| -bool Debug::IsBreakStub(Code* code) {
|
| - CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
| - return major_key == CodeStub::CallFunction;
|
| -}
|
| -
|
| -
|
| -// Find the builtin to use for invoking the debug break
|
| -Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
|
| - Isolate* isolate = code->GetIsolate();
|
| -
|
| - // Find the builtin debug break function matching the calling convention
|
| - // used by the call site.
|
| - if (code->is_inline_cache_stub()) {
|
| - switch (code->kind()) {
|
| - case Code::CALL_IC:
|
| - return isolate->builtins()->CallICStub_DebugBreak();
|
| -
|
| - case Code::LOAD_IC:
|
| - return isolate->builtins()->LoadIC_DebugBreak();
|
| -
|
| - case Code::STORE_IC:
|
| - return isolate->builtins()->StoreIC_DebugBreak();
|
| -
|
| - case Code::KEYED_LOAD_IC:
|
| - return isolate->builtins()->KeyedLoadIC_DebugBreak();
|
| -
|
| - case Code::KEYED_STORE_IC:
|
| - return isolate->builtins()->KeyedStoreIC_DebugBreak();
|
| -
|
| - case Code::COMPARE_NIL_IC:
|
| - return isolate->builtins()->CompareNilIC_DebugBreak();
|
| -
|
| - default:
|
| - UNREACHABLE();
|
| - }
|
| - }
|
| - if (RelocInfo::IsConstructCall(mode)) {
|
| - if (code->has_function_cache()) {
|
| - return isolate->builtins()->CallConstructStub_Recording_DebugBreak();
|
| - } else {
|
| - return isolate->builtins()->CallConstructStub_DebugBreak();
|
| - }
|
| - }
|
| - if (code->kind() == Code::STUB) {
|
| - ASSERT(code->major_key() == CodeStub::CallFunction);
|
| - return isolate->builtins()->CallFunctionStub_DebugBreak();
|
| - }
|
|
|
| - UNREACHABLE();
|
| - return Handle<Code>::null();
|
| -}
|
|
|
|
|
| // Simple function for returning the source positions for active break points.
|
| @@ -1655,7 +1684,7 @@ void Debug::HandleStepIn(Handle<JSFunction> function,
|
|
|
| // Flood the function with one-shot break points if it is called from where
|
| // step into was requested.
|
| - if (fp == step_in_fp()) {
|
| + if (fp == thread_local_.step_into_fp_) {
|
| if (function->shared()->bound()) {
|
| // Handle Function.prototype.bind
|
| Debug::FloodBoundFunctionWithOneShot(function);
|
| @@ -1912,7 +1941,7 @@ class ActiveFunctionsRedirector : public ThreadVisitor {
|
| };
|
|
|
|
|
| -void Debug::EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function) {
|
| +static void EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function) {
|
| if (function->code()->kind() == Code::FUNCTION &&
|
| function->code()->has_debug_break_slots()) {
|
| // Nothing to do. Function code already had debug break slots.
|
| @@ -1931,7 +1960,7 @@ void Debug::EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function) {
|
| }
|
|
|
|
|
| -void Debug::RecompileAndRelocateSuspendedGenerators(
|
| +static void RecompileAndRelocateSuspendedGenerators(
|
| const List<Handle<JSGeneratorObject> > &generators) {
|
| for (int i = 0; i < generators.length(); i++) {
|
| Handle<JSFunction> fun(generators[i]->function());
|
| @@ -2444,60 +2473,10 @@ void Debug::ClearMirrorCache() {
|
| }
|
|
|
|
|
| -void Debug::CreateScriptCache() {
|
| - Heap* heap = isolate_->heap();
|
| - HandleScope scope(isolate_);
|
| -
|
| - // 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. The second also sweeps precisely,
|
| - // which saves us doing yet another GC to make the heap iterable.
|
| - heap->CollectAllGarbage(Heap::kNoGCFlags, "Debug::CreateScriptCache");
|
| -
|
| - ASSERT(script_cache_ == NULL);
|
| - script_cache_ = new ScriptCache(isolate_);
|
| -
|
| - // Scan heap for Script objects.
|
| - int count = 0;
|
| - HeapIterator iterator(heap);
|
| -
|
| - 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)));
|
| - count++;
|
| - }
|
| - }
|
| -}
|
| -
|
| -
|
| -void Debug::DestroyScriptCache() {
|
| - // Get rid of the script cache if it was created.
|
| - if (script_cache_ != NULL) {
|
| - delete script_cache_;
|
| - script_cache_ = NULL;
|
| - }
|
| -}
|
| -
|
| -
|
| -void Debug::AddScriptToScriptCache(Handle<Script> script) {
|
| - if (script_cache_ != NULL) {
|
| - script_cache_->Add(script);
|
| - }
|
| -}
|
| -
|
| -
|
| Handle<FixedArray> Debug::GetLoadedScripts() {
|
| // Create and fill the script cache when the loaded scripts is requested for
|
| // the first time.
|
| - if (script_cache_ == NULL) {
|
| - CreateScriptCache();
|
| - }
|
| -
|
| - // If the script cache is not active just return an empty array.
|
| - ASSERT(script_cache_ != NULL);
|
| - if (script_cache_ == NULL) {
|
| - isolate_->factory()->NewFixedArray(0);
|
| - }
|
| + if (script_cache_ == NULL) script_cache_ = new ScriptCache(isolate_);
|
|
|
| // Perform GC to get unreferenced scripts evicted from the cache before
|
| // returning the content.
|
| @@ -2604,22 +2583,19 @@ MaybeHandle<Object> Debug::MakeScriptCollectedEvent(int id) {
|
|
|
|
|
| void Debug::OnException(Handle<Object> exception, bool uncaught) {
|
| - HandleScope scope(isolate_);
|
| -
|
| - // Bail out based on state or if there is no listener for this event
|
| - if (is_entered()) return;
|
| - if (!EventActive()) return;
|
| + if (is_entered() || ignore_events()) return;
|
|
|
| + HandleScope scope(isolate_);
|
| Handle<Object> promise = GetPromiseForUncaughtException();
|
| uncaught |= !promise->IsUndefined();
|
|
|
| // Bail out if exception breaks are not active
|
| if (uncaught) {
|
| // Uncaught exceptions are reported by either flags.
|
| - if (!(break_on_uncaught_exception() || break_on_exception())) return;
|
| + if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
|
| } else {
|
| // Caught exceptions are reported is activated.
|
| - if (!break_on_exception()) return;
|
| + if (!break_on_exception_) return;
|
| }
|
|
|
| // Enter the debugger.
|
| @@ -2645,14 +2621,12 @@ void Debug::OnException(Handle<Object> exception, bool uncaught) {
|
|
|
| void Debug::OnDebugBreak(Handle<Object> break_points_hit,
|
| bool auto_continue) {
|
| - HandleScope scope(isolate_);
|
| -
|
| // Debugger has already been entered by caller.
|
| ASSERT(isolate_->context() == *debug_context());
|
| -
|
| // Bail out if there is no listener for this event
|
| - if (!EventActive()) return;
|
| + if (ignore_events()) return;
|
|
|
| + HandleScope scope(isolate_);
|
| // Create the event data object.
|
| Handle<Object> event_data;
|
| // Bail out and don't call debugger if exception.
|
| @@ -2666,12 +2640,9 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit,
|
|
|
|
|
| void Debug::OnBeforeCompile(Handle<Script> script) {
|
| - HandleScope scope(isolate_);
|
| -
|
| - // Bail out based on state or if there is no listener for this event
|
| - if (is_entered()) return;
|
| - if (!EventActive()) return;
|
| + if (is_entered() || ignore_events()) return;
|
|
|
| + HandleScope scope(isolate_);
|
| // Enter the debugger.
|
| EnterDebugger debugger(isolate_);
|
| if (debugger.FailedToEnter()) return;
|
| @@ -2690,15 +2661,14 @@ void Debug::OnBeforeCompile(Handle<Script> script) {
|
|
|
| // Handle debugger actions when a new script is compiled.
|
| void Debug::OnAfterCompile(Handle<Script> script,
|
| - AfterCompileFlags after_compile_flags) {
|
| - HandleScope scope(isolate_);
|
| -
|
| + AfterCompileFlags after_compile_flags) {
|
| // Add the newly compiled script to the script cache.
|
| - AddScriptToScriptCache(script);
|
| + if (script_cache_ != NULL) script_cache_->Add(script);
|
|
|
| // No more to do if not debugging.
|
| - if (!EventActive()) return;
|
| + if (is_entered() || ignore_events()) return;
|
|
|
| + HandleScope scope(isolate_);
|
| // Store whether in debugger before entering debugger.
|
| bool in_debugger = is_entered();
|
|
|
| @@ -2748,12 +2718,9 @@ void Debug::OnAfterCompile(Handle<Script> script,
|
|
|
|
|
| void Debug::OnScriptCollected(int id) {
|
| - HandleScope scope(isolate_);
|
| -
|
| - // No more to do if not debugging.
|
| - if (is_entered()) return;
|
| - if (!EventActive()) return;
|
| + if (is_entered() || ignore_events()) return;
|
|
|
| + HandleScope scope(isolate_);
|
| // Enter the debugger.
|
| EnterDebugger debugger(isolate_);
|
| if (debugger.FailedToEnter()) return;
|
| @@ -2775,9 +2742,6 @@ void Debug::ProcessDebugEvent(v8::DebugEvent event,
|
| bool auto_continue) {
|
| HandleScope scope(isolate_);
|
|
|
| - // Clear any pending debug break if this is a real break.
|
| - if (!auto_continue) thread_local_.has_pending_interrupt_ = false;
|
| -
|
| // Create the execution state.
|
| Handle<Object> exec_state;
|
| // Bail out and don't call debugger if exception.
|
| @@ -3095,7 +3059,7 @@ MaybeHandle<Object> Debug::Call(Handle<JSFunction> fun, Handle<Object> data) {
|
| }
|
|
|
|
|
| -void Debug::DebugBreakHelper() {
|
| +void Debug::HandleDebugBreak() {
|
| // Ignore debug break during bootstrapping.
|
| if (isolate_->bootstrapper()->IsActive()) return;
|
| // Just continue if breaks are disabled.
|
| @@ -3161,9 +3125,9 @@ EnterDebugger::EnterDebugger(Isolate* isolate)
|
| // Create the new break info. If there is no JavaScript frames there is no
|
| // break frame id.
|
| JavaScriptFrameIterator it(isolate_);
|
| - has_js_frames_ = !it.done();
|
| - debug->thread_local_.break_frame_id_ = has_js_frames_ ? it.frame()->id()
|
| - : StackFrame::NO_ID;
|
| + bool has_js_frames = !it.done();
|
| + debug->thread_local_.break_frame_id_ = has_js_frames ? it.frame()->id()
|
| + : StackFrame::NO_ID;
|
| debug->SetNextBreakId();
|
|
|
| debug->UpdateState();
|
| @@ -3187,9 +3151,7 @@ EnterDebugger::~EnterDebugger() {
|
| // 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()) {
|
| - debug->ClearMirrorCache();
|
| - }
|
| + if (!isolate_->has_pending_exception()) debug->ClearMirrorCache();
|
|
|
| // If there are commands in the queue when leaving the debugger request
|
| // that these commands are processed.
|
| @@ -3369,10 +3331,6 @@ CommandMessage::CommandMessage(const Vector<uint16_t>& text,
|
| }
|
|
|
|
|
| -CommandMessage::~CommandMessage() {
|
| -}
|
| -
|
| -
|
| void CommandMessage::Dispose() {
|
| text_.Dispose();
|
| delete client_data_;
|
| @@ -3393,10 +3351,7 @@ CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0),
|
|
|
|
|
| CommandMessageQueue::~CommandMessageQueue() {
|
| - while (!IsEmpty()) {
|
| - CommandMessage m = Get();
|
| - m.Dispose();
|
| - }
|
| + while (!IsEmpty()) Get().Dispose();
|
| DeleteArray(messages_);
|
| }
|
|
|
|
|