| Index: src/debug/debug.cc
|
| diff --git a/src/debug/debug.cc b/src/debug/debug.cc
|
| index a7e5ea6cef4d4330328d5872028c0c0b4e240694..33f0834f2e06ebaebd7ee2b6da1fe90c10111440 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()) {
|
| - 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_ = Smi::kZero;
|
| thread_local_.async_task_count_ = 0;
|
| clear_suspended_generator();
|
| @@ -515,7 +501,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.
|
| @@ -536,28 +522,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;
|
| @@ -595,16 +582,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);
|
| @@ -670,11 +657,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);
|
| @@ -714,10 +697,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.
|
| @@ -747,13 +727,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();
|
| @@ -770,12 +750,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();
|
| }
|
| @@ -785,14 +765,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();
|
| }
|
| }
|
| @@ -833,36 +811,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();
|
| }
|
| }
|
| @@ -916,7 +877,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() {
|
| @@ -928,7 +889,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();
|
| }
|
|
|
| @@ -940,32 +901,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()));
|
| }
|
|
|
|
|
| @@ -982,14 +979,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.
|
| @@ -1001,30 +998,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.
|
| @@ -1036,7 +1018,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();
|
|
|
| @@ -1044,41 +1027,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;
|
| }
|
| }
|
| @@ -1107,13 +1094,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());
|
| @@ -1134,8 +1121,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();
|
| }
|
|
|
| @@ -1354,12 +1341,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);
|
| }
|
| @@ -1394,8 +1381,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;
|
|
|
| @@ -1416,9 +1402,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());
|
| @@ -1526,16 +1510,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;
|
| }
|
|
|
| @@ -1706,7 +1685,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_);
|
| @@ -1719,6 +1697,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) {
|
| @@ -1762,17 +1741,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;
|
| }
|
|
|
| @@ -2013,12 +1988,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()) {
|
| @@ -2162,6 +2131,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();
|
| @@ -2225,9 +2215,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
|
|
|