Chromium Code Reviews| Index: src/debug/debug.cc |
| diff --git a/src/debug/debug.cc b/src/debug/debug.cc |
| index 1dc28186bd7efb6f9dd1fec17b698aa4d3c3def9..762176f380237316d8e2e788156868313dcd8b57 100644 |
| --- a/src/debug/debug.cc |
| +++ b/src/debug/debug.cc |
| @@ -116,35 +116,32 @@ bool BreakLocation::HasBreakPoint(Handle<DebugInfo> debug_info) const { |
| // step to, but not actually a location where we can put a break point. |
| if (abstract_code_->IsCode()) { |
| DCHECK_EQ(debug_info->DebugCode(), abstract_code_->GetCode()); |
| - CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + CodeBreakIterator it(debug_info); |
| it.SkipToPosition(position_, BREAK_POSITION_ALIGNED); |
| return it.code_offset() == code_offset_; |
| } else { |
| DCHECK(abstract_code_->IsBytecodeArray()); |
| - BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + BytecodeArrayBreakIterator it(debug_info); |
| it.SkipToPosition(position_, BREAK_POSITION_ALIGNED); |
| return it.code_offset() == code_offset_; |
| } |
| } |
| std::unique_ptr<BreakIterator> BreakIterator::GetIterator( |
| - Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code, |
| - BreakLocatorType type) { |
| + Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code) { |
| if (abstract_code->IsBytecodeArray()) { |
| DCHECK(debug_info->HasDebugBytecodeArray()); |
| return std::unique_ptr<BreakIterator>( |
| - new BytecodeArrayBreakIterator(debug_info, type)); |
| + new BytecodeArrayBreakIterator(debug_info)); |
| } else { |
| DCHECK(abstract_code->IsCode()); |
| DCHECK(debug_info->HasDebugCode()); |
| - return std::unique_ptr<BreakIterator>( |
| - new CodeBreakIterator(debug_info, type)); |
| + return std::unique_ptr<BreakIterator>(new CodeBreakIterator(debug_info)); |
| } |
| } |
| -BreakIterator::BreakIterator(Handle<DebugInfo> debug_info, |
| - BreakLocatorType type) |
| - : debug_info_(debug_info), break_index_(-1), break_locator_type_(type) { |
| +BreakIterator::BreakIterator(Handle<DebugInfo> debug_info) |
| + : debug_info_(debug_info), break_index_(-1) { |
| position_ = debug_info->shared()->start_position(); |
| statement_position_ = position_; |
| } |
| @@ -173,10 +170,9 @@ int BreakIterator::BreakIndexFromPosition(int source_position, |
| return closest_break; |
| } |
| -CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info, |
| - BreakLocatorType type) |
| - : BreakIterator(debug_info, type), |
| - reloc_iterator_(debug_info->DebugCode(), GetModeMask(type)), |
| +CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info) |
| + : BreakIterator(debug_info), |
| + reloc_iterator_(debug_info->DebugCode(), GetModeMask()), |
| source_position_iterator_( |
| debug_info->DebugCode()->source_position_table()) { |
| // There is at least one break location. |
| @@ -184,17 +180,13 @@ CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info, |
| Next(); |
| } |
| -int CodeBreakIterator::GetModeMask(BreakLocatorType type) { |
| +int CodeBreakIterator::GetModeMask() { |
| int mask = 0; |
| mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN); |
| mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CALL); |
| - if (isolate()->is_tail_call_elimination_enabled()) { |
|
jgruber
2017/01/30 15:42:17
Is TCE now enabled by default? Or why is this now
Yang
2017/01/30 19:47:19
If TCE is not enabled, the emitted code will not h
|
| - mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL); |
| - } |
| - if (type == ALL_BREAK_LOCATIONS) { |
| - mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION); |
| - mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT); |
| - } |
| + mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_TAIL_CALL); |
| + mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION); |
| + mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT); |
| return mask; |
| } |
| @@ -244,7 +236,7 @@ DebugBreakType CodeBreakIterator::GetDebugBreakType() { |
| void CodeBreakIterator::SkipToPosition(int position, |
| BreakPositionAlignment alignment) { |
| - CodeBreakIterator it(debug_info_, break_locator_type_); |
| + CodeBreakIterator it(debug_info_); |
| SkipTo(it.BreakIndexFromPosition(position, alignment)); |
| } |
| @@ -279,8 +271,8 @@ BreakLocation CodeBreakIterator::GetBreakLocation() { |
| } |
| BytecodeArrayBreakIterator::BytecodeArrayBreakIterator( |
| - Handle<DebugInfo> debug_info, BreakLocatorType type) |
| - : BreakIterator(debug_info, type), |
| + Handle<DebugInfo> debug_info) |
| + : BreakIterator(debug_info), |
| source_position_iterator_( |
| debug_info->DebugBytecodeArray()->source_position_table()) { |
| // There is at least one break location. |
| @@ -304,13 +296,7 @@ void BytecodeArrayBreakIterator::Next() { |
| DCHECK(statement_position_ >= 0); |
| DebugBreakType type = GetDebugBreakType(); |
| - if (type == NOT_DEBUG_BREAK) continue; |
| - |
| - if (break_locator_type_ == ALL_BREAK_LOCATIONS) break; |
| - |
| - DCHECK_EQ(CALLS_AND_RETURNS, break_locator_type_); |
| - if (type == DEBUG_BREAK_SLOT_AT_CALL) break; |
| - if (type == DEBUG_BREAK_SLOT_AT_RETURN) break; |
| + if (type != NOT_DEBUG_BREAK) break; |
| } |
| break_index_++; |
| } |
| @@ -339,7 +325,7 @@ DebugBreakType BytecodeArrayBreakIterator::GetDebugBreakType() { |
| void BytecodeArrayBreakIterator::SkipToPosition( |
| int position, BreakPositionAlignment alignment) { |
| - BytecodeArrayBreakIterator it(debug_info_, break_locator_type_); |
| + BytecodeArrayBreakIterator it(debug_info_); |
| SkipTo(it.BreakIndexFromPosition(position, alignment)); |
| } |
| @@ -399,8 +385,8 @@ void Debug::ThreadInit() { |
| thread_local_.break_frame_id_ = StackFrame::NO_ID; |
| thread_local_.last_step_action_ = StepNone; |
| thread_local_.last_statement_position_ = kNoSourcePosition; |
| - thread_local_.last_fp_ = 0; |
| - thread_local_.target_fp_ = 0; |
| + thread_local_.last_frame_count_ = -1; |
| + thread_local_.target_frame_count_ = -1; |
| thread_local_.return_value_ = Handle<Object>(); |
| thread_local_.async_task_count_ = 0; |
| clear_suspended_generator(); |
| @@ -514,7 +500,7 @@ void Debug::Break(JavaScriptFrame* frame) { |
| // Return if we fail to retrieve debug info. |
| Handle<JSFunction> function(frame->function()); |
| Handle<SharedFunctionInfo> shared(function->shared()); |
| - if (!EnsureDebugInfo(shared, function)) return; |
| + if (!EnsureDebugInfo(shared)) return; |
| Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); |
| // Find the break location where execution has stopped. |
| @@ -535,28 +521,29 @@ void Debug::Break(JavaScriptFrame* frame) { |
| // No break point. Check for stepping. |
| StepAction step_action = last_step_action(); |
| - Address current_fp = frame->UnpaddedFP(); |
| - Address target_fp = thread_local_.target_fp_; |
| - Address last_fp = thread_local_.last_fp_; |
| + int current_frame_count = CurrentFrameCount(); |
| + int target_frame_count = thread_local_.target_frame_count_; |
| + int last_frame_count = thread_local_.last_frame_count_; |
| bool step_break = false; |
| switch (step_action) { |
| case StepNone: |
| return; |
| case StepOut: |
| - // Step out has not reached the target frame yet. |
| - if (current_fp < target_fp) return; |
| + // Step out should not break in a deeper frame than target frame. |
| + if (current_frame_count > target_frame_count) return; |
| step_break = true; |
| break; |
| case StepNext: |
| - // Step next should not break in a deeper frame. |
| - if (current_fp < target_fp) return; |
| + // Step next should not break in a deeper frame than target frame. |
| + if (current_frame_count > target_frame_count) return; |
| // For step-next, a tail call is like a return and should break. |
| step_break = location.IsTailCall(); |
| // Fall through. |
| case StepIn: { |
| FrameSummary summary = FrameSummary::GetTop(frame); |
| - step_break = step_break || location.IsReturn() || current_fp != last_fp || |
| + step_break = step_break || location.IsReturn() || |
| + current_frame_count != last_frame_count || |
| thread_local_.last_statement_position_ != |
| summary.SourceStatementPosition(); |
| break; |
| @@ -594,16 +581,16 @@ MaybeHandle<FixedArray> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info, |
| bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) { |
| + HandleScope scope(isolate_); |
| // A break location is considered muted if break locations on the current |
| // statement have at least one break point, and all of these break points |
| // evaluate to false. Aside from not triggering a debug break event at the |
| // break location, we also do not trigger one for debugger statements, nor |
| // an exception event on exception at this location. |
| - Object* fun = frame->function(); |
| - if (!fun->IsJSFunction()) return false; |
| - JSFunction* function = JSFunction::cast(fun); |
| + FrameSummary summary = FrameSummary::GetTop(frame); |
| + DCHECK(!summary.IsWasm()); |
| + Handle<JSFunction> function = summary.AsJavaScript().function(); |
| if (!function->shared()->HasDebugInfo()) return false; |
| - HandleScope scope(isolate_); |
| Handle<DebugInfo> debug_info(function->shared()->GetDebugInfo()); |
| // Enter the debugger. |
| DebugScope debug_scope(this); |
| @@ -669,11 +656,7 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function, |
| // Make sure the function is compiled and has set up the debug info. |
| Handle<SharedFunctionInfo> shared(function->shared()); |
| - if (!EnsureDebugInfo(shared, function)) { |
| - // Return if retrieving debug info failed. |
| - return true; |
| - } |
| - |
| + if (!EnsureDebugInfo(shared)) return true; |
| Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
| // Source positions starts with zero. |
| DCHECK(*source_position >= 0); |
| @@ -713,10 +696,7 @@ bool Debug::SetBreakPointForScript(Handle<Script> script, |
| // Make sure the function has set up the debug info. |
| Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result); |
| - if (!EnsureDebugInfo(shared, Handle<JSFunction>::null())) { |
| - // Return if retrieving debug info failed. |
| - return false; |
| - } |
| + if (!EnsureDebugInfo(shared)) return false; |
| // Find position within function. The script position might be before the |
| // source position of the first function. |
| @@ -746,13 +726,13 @@ int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info, |
| int statement_position; |
| int position; |
| if (debug_info->HasDebugCode()) { |
| - CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + CodeBreakIterator it(debug_info); |
| it.SkipToPosition(source_position, alignment); |
| statement_position = it.statement_position(); |
| position = it.position(); |
| } else { |
| DCHECK(debug_info->HasDebugBytecodeArray()); |
| - BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + BytecodeArrayBreakIterator it(debug_info); |
| it.SkipToPosition(source_position, alignment); |
| statement_position = it.statement_position(); |
| position = it.position(); |
| @@ -769,12 +749,12 @@ void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) { |
| BreakPointInfo* info = BreakPointInfo::cast(break_points->get(i)); |
| if (info->GetBreakPointCount() == 0) continue; |
| if (debug_info->HasDebugCode()) { |
| - CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + CodeBreakIterator it(debug_info); |
| it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED); |
| it.SetDebugBreak(); |
| } |
| if (debug_info->HasDebugBytecodeArray()) { |
| - BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + BytecodeArrayBreakIterator it(debug_info); |
| it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED); |
| it.SetDebugBreak(); |
| } |
| @@ -784,14 +764,12 @@ void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) { |
| void Debug::ClearBreakPoints(Handle<DebugInfo> debug_info) { |
| DisallowHeapAllocation no_gc; |
| if (debug_info->HasDebugCode()) { |
| - for (CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); !it.Done(); |
| - it.Next()) { |
| + for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) { |
| it.ClearDebugBreak(); |
| } |
| } |
| if (debug_info->HasDebugBytecodeArray()) { |
| - for (BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| - !it.Done(); it.Next()) { |
| + for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) { |
| it.ClearDebugBreak(); |
| } |
| } |
| @@ -832,36 +810,19 @@ void Debug::ClearAllBreakPoints() { |
| } |
| } |
| -void Debug::FloodWithOneShot(Handle<JSFunction> function, |
| - BreakLocatorType type) { |
| - // Debug utility functions are not subject to debugging. |
| - if (function->native_context() == *debug_context()) return; |
| - |
| - if (!function->shared()->IsSubjectToDebugging() || |
| - IsBlackboxed(function->shared())) { |
| - // Builtin functions are not subject to stepping, but need to be |
| - // deoptimized, because optimized code does not check for debug |
| - // step in at call sites. |
| - Deoptimizer::DeoptimizeFunction(*function); |
| - return; |
| - } |
| +void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) { |
| + if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) return; |
| // Make sure the function is compiled and has set up the debug info. |
| - Handle<SharedFunctionInfo> shared(function->shared()); |
| - if (!EnsureDebugInfo(shared, function)) { |
| - // Return if we failed to retrieve the debug info. |
| - return; |
| - } |
| - |
| - // Flood the function with break points. |
| + if (!EnsureDebugInfo(shared)) return; |
| Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
| + // Flood the function with break points. |
| if (debug_info->HasDebugCode()) { |
| - for (CodeBreakIterator it(debug_info, type); !it.Done(); it.Next()) { |
| + for (CodeBreakIterator it(debug_info); !it.Done(); it.Next()) { |
| it.SetDebugBreak(); |
| } |
| } |
| if (debug_info->HasDebugBytecodeArray()) { |
| - for (BytecodeArrayBreakIterator it(debug_info, type); !it.Done(); |
| - it.Next()) { |
| + for (BytecodeArrayBreakIterator it(debug_info); !it.Done(); it.Next()) { |
| it.SetDebugBreak(); |
| } |
| } |
| @@ -915,7 +876,7 @@ void Debug::PrepareStepIn(Handle<JSFunction> function) { |
| if (ignore_events()) return; |
| if (in_debug_scope()) return; |
| if (break_disabled()) return; |
| - FloodWithOneShot(function); |
| + FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_)); |
| } |
| void Debug::PrepareStepInSuspendedGenerator() { |
| @@ -927,7 +888,7 @@ void Debug::PrepareStepInSuspendedGenerator() { |
| UpdateHookOnFunctionCall(); |
| Handle<JSFunction> function( |
| JSGeneratorObject::cast(thread_local_.suspended_generator_)->function()); |
| - FloodWithOneShot(function); |
| + FloodWithOneShot(Handle<SharedFunctionInfo>(function->shared(), isolate_)); |
| clear_suspended_generator(); |
| } |
| @@ -939,32 +900,68 @@ void Debug::PrepareStepOnThrow() { |
| ClearOneShot(); |
| + int current_frame_count = CurrentFrameCount(); |
| + |
| // Iterate through the JavaScript stack looking for handlers. |
| JavaScriptFrameIterator it(isolate_); |
| while (!it.done()) { |
| JavaScriptFrame* frame = it.frame(); |
| if (frame->LookupExceptionHandlerInTable(nullptr, nullptr) > 0) break; |
| + List<SharedFunctionInfo*> infos; |
| + frame->GetFunctions(&infos); |
| + current_frame_count -= infos.length(); |
| it.Advance(); |
| } |
| - if (last_step_action() == StepNext || last_step_action() == StepOut) { |
| - while (!it.done()) { |
| - Address current_fp = it.frame()->UnpaddedFP(); |
| - if (current_fp >= thread_local_.target_fp_) break; |
| - it.Advance(); |
| + // No handler found. Nothing to instrument. |
| + if (it.done()) return; |
| + |
| + bool found_handler = false; |
| + // Iterate frames, including inlined frames. First, find the handler frame. |
| + // Then skip to the frame we want to break in, then instrument for stepping. |
| + for (; !it.done(); it.Advance()) { |
| + JavaScriptFrame* frame = JavaScriptFrame::cast(it.frame()); |
| + if (last_step_action() == StepIn) { |
| + // Deoptimize frame to ensure calls are checked for step-in. |
| + Deoptimizer::DeoptimizeFunction(frame->function()); |
| } |
| - } |
| + List<FrameSummary> summaries; |
| + frame->Summarize(&summaries); |
| + for (int i = summaries.length() - 1; i >= 0; i--, current_frame_count--) { |
| + if (!found_handler) { |
| + // We have yet to find the handler. If the frame inlines multiple |
| + // functions, we have to check each one for the handler. |
| + // If it only contains one function, we already found the handler. |
| + if (summaries.length() > 1) { |
| + Handle<AbstractCode> code = |
| + summaries[i].AsJavaScript().abstract_code(); |
| + CHECK_EQ(AbstractCode::INTERPRETED_FUNCTION, code->kind()); |
| + BytecodeArray* bytecode = code->GetBytecodeArray(); |
| + HandlerTable* table = HandlerTable::cast(bytecode->handler_table()); |
| + int code_offset = summaries[i].code_offset(); |
| + HandlerTable::CatchPrediction prediction; |
| + int index = table->LookupRange(code_offset, nullptr, &prediction); |
| + if (index > 0) found_handler = true; |
| + } else { |
| + found_handler = true; |
| + } |
| + } |
| - // Find the closest Javascript frame we can flood with one-shots. |
| - while (!it.done() && |
| - (!it.frame()->function()->shared()->IsSubjectToDebugging() || |
| - IsBlackboxed(it.frame()->function()->shared()))) { |
| - it.Advance(); |
| + if (found_handler) { |
| + // We found the handler. If we are stepping next or out, we need to |
| + // iterate until we found the suitable target frame to break in. |
| + if ((last_step_action() == StepNext || last_step_action() == StepOut) && |
| + current_frame_count > thread_local_.target_frame_count_) { |
| + continue; |
| + } |
| + Handle<SharedFunctionInfo> info( |
| + summaries[i].AsJavaScript().function()->shared()); |
| + if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue; |
| + FloodWithOneShot(info); |
| + return; |
| + } |
| + } |
| } |
| - |
| - if (it.done()) return; // No suitable Javascript catch handler. |
| - |
| - FloodWithOneShot(Handle<JSFunction>(it.frame()->function())); |
| } |
| @@ -981,14 +978,14 @@ void Debug::PrepareStep(StepAction step_action) { |
| // If there is no JavaScript stack don't do anything. |
| if (frame_id == StackFrame::NO_ID) return; |
| - StackTraceFrameIterator frames_it(isolate_, frame_id); |
| - StandardFrame* frame = frames_it.frame(); |
| - |
| feature_tracker()->Track(DebugFeatureTracker::kStepping); |
| thread_local_.last_step_action_ = step_action; |
| UpdateHookOnFunctionCall(); |
| + StackTraceFrameIterator frames_it(isolate_, frame_id); |
| + StandardFrame* frame = frames_it.frame(); |
| + |
| // Handle stepping in wasm functions via the wasm interpreter. |
| if (frame->is_wasm()) { |
| // If the top frame is compiled, we cannot step. |
| @@ -1000,30 +997,15 @@ void Debug::PrepareStep(StepAction step_action) { |
| } |
| JavaScriptFrame* js_frame = JavaScriptFrame::cast(frame); |
| - |
| - // If the function on the top frame is unresolved perform step out. This will |
| - // be the case when calling unknown function and having the debugger stopped |
| - // in an unhandled exception. |
| - if (!js_frame->function()->IsJSFunction()) { |
| - // Step out: Find the calling JavaScript frame and flood it with |
| - // breakpoints. |
| - frames_it.Advance(); |
| - // Fill the function to return to with one-shot break points. |
| - JSFunction* function = JavaScriptFrame::cast(frames_it.frame())->function(); |
| - FloodWithOneShot(handle(function, isolate_)); |
| - return; |
| - } |
| + DCHECK(js_frame->function()->IsJSFunction()); |
| // Get the debug info (create it if it does not exist). |
| auto summary = FrameSummary::GetTop(frame).AsJavaScript(); |
| Handle<JSFunction> function(summary.function()); |
| Handle<SharedFunctionInfo> shared(function->shared()); |
| - if (!EnsureDebugInfo(shared, function)) { |
| - // Return if ensuring debug info failed. |
| - return; |
| - } |
| - |
| + if (!EnsureDebugInfo(shared)) return; |
| Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
| + |
| BreakLocation location = BreakLocation::FromFrame(debug_info, js_frame); |
| // Any step at a return is a step-out. |
| @@ -1035,7 +1017,8 @@ void Debug::PrepareStep(StepAction step_action) { |
| thread_local_.last_statement_position_ = |
| summary.abstract_code()->SourceStatementPosition(summary.code_offset()); |
| - thread_local_.last_fp_ = frame->UnpaddedFP(); |
| + int current_frame_count = CurrentFrameCount(); |
| + thread_local_.last_frame_count_ = current_frame_count; |
| // No longer perform the current async step. |
| clear_suspended_generator(); |
| @@ -1043,41 +1026,45 @@ void Debug::PrepareStep(StepAction step_action) { |
| case StepNone: |
| UNREACHABLE(); |
| break; |
| - case StepOut: |
| - // Advance to caller frame. |
| - frames_it.Advance(); |
| - // Find top-most function which is subject to debugging. |
| - while (!frames_it.done()) { |
| - StandardFrame* caller_frame = frames_it.frame(); |
| - if (caller_frame->is_wasm()) { |
| - // TODO(clemensh): Implement stepping out from JS to WASM. |
| - break; |
| + case StepOut: { |
| + // Clear last position info. For stepping out it does not matter. |
| + thread_local_.last_statement_position_ = kNoSourcePosition; |
| + thread_local_.last_frame_count_ = -1; |
| + // 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; |
| + for (; !frames_it.done(); frames_it.Advance()) { |
| + // TODO(clemensh): Implement stepping out from JS to WASM. |
| + if (frames_it.frame()->is_wasm()) continue; |
| + JavaScriptFrame* frame = JavaScriptFrame::cast(frames_it.frame()); |
| + if (last_step_action() == StepIn) { |
| + // Deoptimize frame to ensure calls are checked for step-in. |
| + Deoptimizer::DeoptimizeFunction(frame->function()); |
| } |
| - Handle<JSFunction> js_caller_function( |
| - JavaScriptFrame::cast(caller_frame)->function(), isolate_); |
| - if (js_caller_function->shared()->IsSubjectToDebugging() && |
| - !IsBlackboxed(js_caller_function->shared())) { |
| - // Fill the caller function to return to with one-shot break points. |
| - FloodWithOneShot(js_caller_function); |
| - thread_local_.target_fp_ = frames_it.frame()->UnpaddedFP(); |
| - break; |
| + HandleScope scope(isolate_); |
| + List<Handle<SharedFunctionInfo>> infos; |
| + frame->GetFunctions(&infos); |
| + for (; !infos.is_empty(); current_frame_count--) { |
| + Handle<SharedFunctionInfo> info = infos.RemoveLast(); |
| + if (in_current_frame) { |
| + // We want to skip out, so skip the current frame. |
| + in_current_frame = false; |
| + continue; |
| + } |
| + if (!info->IsSubjectToDebugging() || IsBlackboxed(info)) continue; |
| + FloodWithOneShot(info); |
| + thread_local_.target_frame_count_ = current_frame_count; |
| + return; |
| } |
| - // Builtin functions are not subject to stepping, but need to be |
| - // deoptimized to include checks for step-in at call sites. |
| - Deoptimizer::DeoptimizeFunction(*js_caller_function); |
| - frames_it.Advance(); |
| } |
| - // Clear last position info. For stepping out it does not matter. |
| - thread_local_.last_statement_position_ = kNoSourcePosition; |
| - thread_local_.last_fp_ = 0; |
| break; |
| + } |
| case StepNext: |
| - thread_local_.target_fp_ = frame->UnpaddedFP(); |
| - FloodWithOneShot(function); |
| - break; |
| + thread_local_.target_frame_count_ = current_frame_count; |
| + // Fall through. |
| case StepIn: |
| // TODO(clemensh): Implement stepping from JS into WASM. |
| - FloodWithOneShot(function); |
| + FloodWithOneShot(shared); |
| break; |
| } |
| } |
| @@ -1106,13 +1093,13 @@ Handle<Object> Debug::GetSourceBreakLocations( |
| Smi* position = NULL; |
| if (position_alignment == STATEMENT_ALIGNED) { |
| if (debug_info->HasDebugCode()) { |
| - CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + CodeBreakIterator it(debug_info); |
| it.SkipToPosition(break_point_info->source_position(), |
| BREAK_POSITION_ALIGNED); |
| position = Smi::FromInt(it.statement_position()); |
| } else { |
| DCHECK(debug_info->HasDebugBytecodeArray()); |
| - BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + BytecodeArrayBreakIterator it(debug_info); |
| it.SkipToPosition(break_point_info->source_position(), |
| BREAK_POSITION_ALIGNED); |
| position = Smi::FromInt(it.statement_position()); |
| @@ -1133,8 +1120,8 @@ void Debug::ClearStepping() { |
| thread_local_.last_step_action_ = StepNone; |
| thread_local_.last_statement_position_ = kNoSourcePosition; |
| - thread_local_.last_fp_ = 0; |
| - thread_local_.target_fp_ = 0; |
| + thread_local_.last_frame_count_ = -1; |
| + thread_local_.target_frame_count_ = -1; |
| UpdateHookOnFunctionCall(); |
| } |
| @@ -1352,12 +1339,12 @@ void FindBreakablePositions(Handle<DebugInfo> debug_info, int start_position, |
| int end_position, BreakPositionAlignment alignment, |
| std::set<int>* positions) { |
| if (debug_info->HasDebugCode()) { |
| - CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + CodeBreakIterator it(debug_info); |
| GetBreakablePositions(&it, start_position, end_position, alignment, |
| positions); |
| } else { |
| DCHECK(debug_info->HasDebugBytecodeArray()); |
| - BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); |
| + BytecodeArrayBreakIterator it(debug_info); |
| GetBreakablePositions(&it, start_position, end_position, alignment, |
| positions); |
| } |
| @@ -1392,8 +1379,7 @@ bool Debug::GetPossibleBreakpoints(Handle<Script> script, int start_position, |
| was_compiled = true; |
| } |
| } |
| - if (!EnsureDebugInfo(candidates[i], Handle<JSFunction>::null())) |
| - return false; |
| + if (!EnsureDebugInfo(candidates[i])) return false; |
| } |
| if (was_compiled) continue; |
| @@ -1414,9 +1400,7 @@ void Debug::RecordGenerator(Handle<JSGeneratorObject> generator_object) { |
| if (last_step_action() == StepNext) { |
| // Only consider this generator a step-next target if not stepping in. |
| - JavaScriptFrameIterator stack_iterator(isolate_); |
| - JavaScriptFrame* frame = stack_iterator.frame(); |
| - if (frame->UnpaddedFP() < thread_local_.target_fp_) return; |
| + if (thread_local_.target_frame_count_ < CurrentFrameCount()) return; |
| } |
| DCHECK(!has_suspended_generator()); |
| @@ -1524,16 +1508,11 @@ Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, |
| // Ensures the debug information is present for shared. |
| -bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared, |
| - Handle<JSFunction> function) { |
| - if (!shared->IsSubjectToDebugging()) return false; |
| - |
| +bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) { |
| // Return if we already have the debug info for shared. |
| if (shared->HasDebugInfo()) return true; |
| - |
| - if (function.is_null()) { |
| - DCHECK(shared->HasDebugCode()); |
| - } else if (!Compiler::Compile(function, Compiler::CLEAR_EXCEPTION)) { |
| + if (!shared->IsSubjectToDebugging()) return false; |
| + if (!shared->is_compiled() && !Compiler::CompileDebugCode(shared)) { |
| return false; |
| } |
| @@ -1699,7 +1678,6 @@ MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<Smi> type, |
| void Debug::OnThrow(Handle<Object> exception) { |
| if (in_debug_scope() || ignore_events()) return; |
| - PrepareStepOnThrow(); |
| // Temporarily clear any scheduled_exception to allow evaluating |
| // JavaScript from the debug event handler. |
| HandleScope scope(isolate_); |
| @@ -1712,6 +1690,7 @@ void Debug::OnThrow(Handle<Object> exception) { |
| if (!scheduled_exception.is_null()) { |
| isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception; |
| } |
| + PrepareStepOnThrow(); |
| } |
| void Debug::OnPromiseReject(Handle<Object> promise, Handle<Object> value) { |
| @@ -1755,17 +1734,13 @@ bool Debug::IsExceptionBlackboxed(bool uncaught) { |
| bool Debug::IsFrameBlackboxed(JavaScriptFrame* frame) { |
| HandleScope scope(isolate_); |
| if (!frame->HasInlinedFrames()) { |
| - return IsBlackboxed(frame->function()->shared()); |
| - } |
| - List<SharedFunctionInfo*> raw_shareds; |
| - frame->GetFunctions(&raw_shareds); |
| - List<Handle<SharedFunctionInfo>> shareds; |
| - for (int i = 0; i < raw_shareds.length(); ++i) { |
| - shareds.Add(handle(raw_shareds[i])); |
| - } |
| - for (int i = 0; i < shareds.length(); ++i) { |
| - if (!IsBlackboxed(shareds[i])) return false; |
| + Handle<SharedFunctionInfo> shared(frame->function()->shared(), isolate_); |
| + return IsBlackboxed(shared); |
| } |
| + List<Handle<SharedFunctionInfo>> infos; |
| + frame->GetFunctions(&infos); |
| + for (const auto& info : infos) |
| + if (!IsBlackboxed(info)) return false; |
| return true; |
| } |
| @@ -2006,12 +1981,6 @@ debug::Location GetDebugLocation(Handle<Script> script, int source_position) { |
| } |
| } // namespace |
| -bool Debug::IsBlackboxed(SharedFunctionInfo* shared) { |
| - HandleScope scope(isolate_); |
| - Handle<SharedFunctionInfo> shared_function_info(shared); |
| - return IsBlackboxed(shared_function_info); |
| -} |
| - |
| bool Debug::IsBlackboxed(Handle<SharedFunctionInfo> shared) { |
| if (!debug_delegate_) return false; |
| if (!shared->computed_debug_is_blackboxed()) { |
| @@ -2155,6 +2124,27 @@ void Debug::SetEventListener(Handle<Object> callback, |
| UpdateState(); |
| } |
| +int Debug::CurrentFrameCount() { |
| + StackTraceFrameIterator it(isolate_); |
| + if (break_frame_id() != StackFrame::NO_ID) { |
| + // Skip to break frame. |
| + DCHECK(in_debug_scope()); |
| + while (!it.done() && it.frame()->id() != break_frame_id()) it.Advance(); |
| + } |
| + int counter = 0; |
| + while (!it.done()) { |
| + if (it.frame()->is_optimized()) { |
| + List<SharedFunctionInfo*> infos; |
| + OptimizedFrame::cast(it.frame())->GetFunctions(&infos); |
| + counter += infos.length(); |
| + } else { |
| + counter++; |
| + } |
| + it.Advance(); |
| + } |
| + return counter; |
| +} |
| + |
| void Debug::SetDebugDelegate(debug::DebugDelegate* delegate) { |
| debug_delegate_ = delegate; |
| UpdateState(); |
| @@ -2216,9 +2206,11 @@ void Debug::HandleDebugBreak() { |
| DCHECK(!it.done()); |
| Object* fun = it.frame()->function(); |
| if (fun && fun->IsJSFunction()) { |
| + HandleScope scope(isolate_); |
| // Don't stop in builtin and blackboxed functions. |
| - if (!JSFunction::cast(fun)->shared()->IsSubjectToDebugging() || |
| - IsBlackboxed(JSFunction::cast(fun)->shared())) { |
| + Handle<SharedFunctionInfo> shared(JSFunction::cast(fun)->shared(), |
| + isolate_); |
| + if (!shared->IsSubjectToDebugging() || IsBlackboxed(shared)) { |
| // Inspector uses pause on next statement for asynchronous breakpoints. |
| // When breakpoint is fired we try to break on first not blackboxed |
| // statement. To achieve this goal we need to deoptimize current |