diff options
Diffstat (limited to 'deps/v8/src/debug/debug.cc')
-rw-r--r-- | deps/v8/src/debug/debug.cc | 984 |
1 files changed, 459 insertions, 525 deletions
diff --git a/deps/v8/src/debug/debug.cc b/deps/v8/src/debug/debug.cc index cb2d8648aa..e04695771b 100644 --- a/deps/v8/src/debug/debug.cc +++ b/deps/v8/src/debug/debug.cc @@ -4,24 +4,30 @@ #include "src/debug/debug.h" +#include <memory> + #include "src/api.h" #include "src/arguments.h" #include "src/bootstrapper.h" #include "src/code-stubs.h" #include "src/codegen.h" #include "src/compilation-cache.h" +#include "src/compiler-dispatcher/optimizing-compile-dispatcher.h" #include "src/compiler.h" #include "src/deoptimizer.h" #include "src/execution.h" #include "src/frames-inl.h" #include "src/full-codegen/full-codegen.h" #include "src/global-handles.h" +#include "src/globals.h" #include "src/interpreter/interpreter.h" #include "src/isolate-inl.h" #include "src/list.h" #include "src/log.h" #include "src/messages.h" #include "src/snapshot/natives.h" +#include "src/wasm/wasm-debug.h" +#include "src/wasm/wasm-module.h" #include "include/v8-debug.h" @@ -49,63 +55,136 @@ Debug::Debug(Isolate* isolate) ThreadInit(); } +BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info, + JavaScriptFrame* frame) { + FrameSummary summary = FrameSummary::GetFirst(frame); + int offset = summary.code_offset(); + Handle<AbstractCode> abstract_code = summary.abstract_code(); + if (abstract_code->IsCode()) offset = offset - 1; + auto it = BreakIterator::GetIterator(debug_info, abstract_code); + it->SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset)); + return it->GetBreakLocation(); +} -static v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) { - Handle<Context> context = isolate->debug()->debugger_entry()->GetContext(); - // Isolate::context() may have been NULL when "script collected" event - // occured. - if (context.is_null()) return v8::Local<v8::Context>(); - Handle<Context> native_context(context->native_context()); - return v8::Utils::ToLocal(native_context); +void BreakLocation::AllAtCurrentStatement(Handle<DebugInfo> debug_info, + JavaScriptFrame* frame, + List<BreakLocation>* result_out) { + FrameSummary summary = FrameSummary::GetFirst(frame); + int offset = summary.code_offset(); + Handle<AbstractCode> abstract_code = summary.abstract_code(); + if (abstract_code->IsCode()) offset = offset - 1; + int statement_position; + { + auto it = BreakIterator::GetIterator(debug_info, abstract_code); + it->SkipTo(BreakIndexFromCodeOffset(debug_info, abstract_code, offset)); + statement_position = it->statement_position(); + } + for (auto it = BreakIterator::GetIterator(debug_info, abstract_code); + !it->Done(); it->Next()) { + if (it->statement_position() == statement_position) { + result_out->Add(it->GetBreakLocation()); + } + } } -BreakLocation::BreakLocation(Handle<DebugInfo> debug_info, DebugBreakType type, - int code_offset, int position, - int statement_position) - : debug_info_(debug_info), - code_offset_(code_offset), - type_(type), - position_(position), - statement_position_(statement_position) {} +int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info, + Handle<AbstractCode> abstract_code, + int offset) { + // Run through all break points to locate the one closest to the address. + int closest_break = 0; + int distance = kMaxInt; + DCHECK(0 <= offset && offset < abstract_code->Size()); + for (auto it = BreakIterator::GetIterator(debug_info, abstract_code); + !it->Done(); it->Next()) { + // Check if this break point is closer that what was previously found. + if (it->code_offset() <= offset && offset - it->code_offset() < distance) { + closest_break = it->break_index(); + distance = offset - it->code_offset(); + // Check whether we can't get any closer. + if (distance == 0) break; + } + } + return closest_break; +} -BreakLocation::Iterator* BreakLocation::GetIterator( - Handle<DebugInfo> debug_info, BreakLocatorType type) { - if (debug_info->abstract_code()->IsBytecodeArray()) { - return new BytecodeArrayIterator(debug_info, type); +bool BreakLocation::HasBreakPoint(Handle<DebugInfo> debug_info) const { + // First check whether there is a break point with the same source position. + if (!debug_info->HasBreakPoint(position_)) return false; + // Then check whether a break point at that source position would have + // the same code offset. Otherwise it's just a break location that we can + // 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); + it.SkipToPosition(position_, BREAK_POSITION_ALIGNED); + return it.code_offset() == code_offset_; } else { - return new CodeIterator(debug_info, type); + DCHECK(abstract_code_->IsBytecodeArray()); + BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); + it.SkipToPosition(position_, BREAK_POSITION_ALIGNED); + return it.code_offset() == code_offset_; } } -BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info) - : debug_info_(debug_info), - break_index_(-1), - position_(1), - statement_position_(1) {} - -int BreakLocation::Iterator::ReturnPosition() { - if (debug_info_->shared()->HasSourceCode()) { - return debug_info_->shared()->end_position() - - debug_info_->shared()->start_position() - 1; +std::unique_ptr<BreakIterator> BreakIterator::GetIterator( + Handle<DebugInfo> debug_info, Handle<AbstractCode> abstract_code, + BreakLocatorType type) { + if (abstract_code->IsBytecodeArray()) { + DCHECK(debug_info->HasDebugBytecodeArray()); + return std::unique_ptr<BreakIterator>( + new BytecodeArrayBreakIterator(debug_info, type)); } else { - return 0; + DCHECK(abstract_code->IsCode()); + DCHECK(debug_info->HasDebugCode()); + return std::unique_ptr<BreakIterator>( + new CodeBreakIterator(debug_info, type)); } } -BreakLocation::CodeIterator::CodeIterator(Handle<DebugInfo> debug_info, - BreakLocatorType type) - : Iterator(debug_info), - reloc_iterator_(debug_info->abstract_code()->GetCode(), - GetModeMask(type)) { +BreakIterator::BreakIterator(Handle<DebugInfo> debug_info, + BreakLocatorType type) + : debug_info_(debug_info), break_index_(-1), break_locator_type_(type) { + position_ = debug_info->shared()->start_position(); + statement_position_ = position_; +} + +int BreakIterator::BreakIndexFromPosition(int source_position, + BreakPositionAlignment alignment) { + int distance = kMaxInt; + int closest_break = break_index(); + while (!Done()) { + int next_position; + if (alignment == STATEMENT_ALIGNED) { + next_position = statement_position(); + } else { + DCHECK(alignment == BREAK_POSITION_ALIGNED); + next_position = position(); + } + if (source_position <= next_position && + next_position - source_position < distance) { + closest_break = break_index(); + distance = next_position - source_position; + // Check whether we can't get any closer. + if (distance == 0) break; + } + Next(); + } + return closest_break; +} + +CodeBreakIterator::CodeBreakIterator(Handle<DebugInfo> debug_info, + BreakLocatorType type) + : BreakIterator(debug_info, type), + reloc_iterator_(debug_info->DebugCode(), GetModeMask(type)), + source_position_iterator_( + debug_info->DebugCode()->source_position_table()) { // There is at least one break location. DCHECK(!Done()); Next(); } -int BreakLocation::CodeIterator::GetModeMask(BreakLocatorType type) { +int CodeBreakIterator::GetModeMask(BreakLocatorType type) { int mask = 0; - mask |= RelocInfo::ModeMask(RelocInfo::POSITION); - mask |= RelocInfo::ModeMask(RelocInfo::STATEMENT_POSITION); 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()) { @@ -118,81 +197,97 @@ int BreakLocation::CodeIterator::GetModeMask(BreakLocatorType type) { return mask; } -void BreakLocation::CodeIterator::Next() { +void CodeBreakIterator::Next() { DisallowHeapAllocation no_gc; DCHECK(!Done()); // Iterate through reloc info stopping at each breakable code target. bool first = break_index_ == -1; - while (!Done()) { - if (!first) reloc_iterator_.next(); - first = false; - if (Done()) return; - - // Whenever a statement position or (plain) position is passed update the - // current value of these. - if (RelocInfo::IsPosition(rmode())) { - if (RelocInfo::IsStatementPosition(rmode())) { - statement_position_ = static_cast<int>( - rinfo()->data() - debug_info_->shared()->start_position()); - } - // Always update the position as we don't want that to be before the - // statement position. - position_ = static_cast<int>(rinfo()->data() - - debug_info_->shared()->start_position()); - DCHECK(position_ >= 0); - DCHECK(statement_position_ >= 0); - continue; - } - DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) || - RelocInfo::IsDebuggerStatement(rmode())); + if (!first) reloc_iterator_.next(); + first = false; + if (Done()) return; - if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) { - // Set the positions to the end of the function. - statement_position_ = position_ = ReturnPosition(); + int offset = code_offset(); + while (!source_position_iterator_.done() && + source_position_iterator_.code_offset() <= offset) { + position_ = source_position_iterator_.source_position(); + if (source_position_iterator_.is_statement()) { + statement_position_ = position_; } - - break; + source_position_iterator_.Advance(); } + + DCHECK(RelocInfo::IsDebugBreakSlot(rmode()) || + RelocInfo::IsDebuggerStatement(rmode())); break_index_++; } -BreakLocation BreakLocation::CodeIterator::GetBreakLocation() { - DebugBreakType type; +DebugBreakType CodeBreakIterator::GetDebugBreakType() { if (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) { - type = DEBUG_BREAK_SLOT_AT_RETURN; + return DEBUG_BREAK_SLOT_AT_RETURN; } else if (RelocInfo::IsDebugBreakSlotAtCall(rmode())) { - type = DEBUG_BREAK_SLOT_AT_CALL; + return DEBUG_BREAK_SLOT_AT_CALL; } else if (RelocInfo::IsDebugBreakSlotAtTailCall(rmode())) { - type = isolate()->is_tail_call_elimination_enabled() + return isolate()->is_tail_call_elimination_enabled() ? DEBUG_BREAK_SLOT_AT_TAIL_CALL : DEBUG_BREAK_SLOT_AT_CALL; } else if (RelocInfo::IsDebuggerStatement(rmode())) { - type = DEBUGGER_STATEMENT; + return DEBUGGER_STATEMENT; } else if (RelocInfo::IsDebugBreakSlot(rmode())) { - type = DEBUG_BREAK_SLOT; + return DEBUG_BREAK_SLOT; } else { - type = NOT_DEBUG_BREAK; + return NOT_DEBUG_BREAK; } - return BreakLocation(debug_info_, type, code_offset(), position(), - statement_position()); } -BreakLocation::BytecodeArrayIterator::BytecodeArrayIterator( +void CodeBreakIterator::SkipToPosition(int position, + BreakPositionAlignment alignment) { + CodeBreakIterator it(debug_info_, break_locator_type_); + SkipTo(it.BreakIndexFromPosition(position, alignment)); +} + +void CodeBreakIterator::SetDebugBreak() { + DebugBreakType debug_break_type = GetDebugBreakType(); + if (debug_break_type == DEBUGGER_STATEMENT) return; + DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); + Builtins* builtins = isolate()->builtins(); + Handle<Code> target = debug_break_type == DEBUG_BREAK_SLOT_AT_RETURN + ? builtins->Return_DebugBreak() + : builtins->Slot_DebugBreak(); + DebugCodegen::PatchDebugBreakSlot(isolate(), rinfo()->pc(), target); +} + +void CodeBreakIterator::ClearDebugBreak() { + DebugBreakType debug_break_type = GetDebugBreakType(); + if (debug_break_type == DEBUGGER_STATEMENT) return; + DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); + DebugCodegen::ClearDebugBreakSlot(isolate(), rinfo()->pc()); +} + +bool CodeBreakIterator::IsDebugBreak() { + DebugBreakType debug_break_type = GetDebugBreakType(); + if (debug_break_type == DEBUGGER_STATEMENT) return false; + DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); + return DebugCodegen::DebugBreakSlotIsPatched(rinfo()->pc()); +} + +BreakLocation CodeBreakIterator::GetBreakLocation() { + Handle<AbstractCode> code(AbstractCode::cast(debug_info_->DebugCode())); + return BreakLocation(code, GetDebugBreakType(), code_offset(), position_); +} + +BytecodeArrayBreakIterator::BytecodeArrayBreakIterator( Handle<DebugInfo> debug_info, BreakLocatorType type) - : Iterator(debug_info), - source_position_iterator_(debug_info->abstract_code() - ->GetBytecodeArray() - ->source_position_table()), - break_locator_type_(type), - start_position_(debug_info->shared()->start_position()) { + : BreakIterator(debug_info, type), + source_position_iterator_( + debug_info->DebugBytecodeArray()->source_position_table()) { // There is at least one break location. DCHECK(!Done()); Next(); } -void BreakLocation::BytecodeArrayIterator::Next() { +void BytecodeArrayBreakIterator::Next() { DisallowHeapAllocation no_gc; DCHECK(!Done()); bool first = break_index_ == -1; @@ -200,32 +295,27 @@ void BreakLocation::BytecodeArrayIterator::Next() { if (!first) source_position_iterator_.Advance(); first = false; if (Done()) return; - position_ = source_position_iterator_.source_position() - start_position_; + position_ = source_position_iterator_.source_position(); if (source_position_iterator_.is_statement()) { statement_position_ = position_; } DCHECK(position_ >= 0); DCHECK(statement_position_ >= 0); - enum DebugBreakType type = GetDebugBreakType(); + 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) { - DCHECK_EQ(ReturnPosition(), position_); - DCHECK_EQ(ReturnPosition(), statement_position_); - break; - } + if (type == DEBUG_BREAK_SLOT_AT_RETURN) break; } break_index_++; } -BreakLocation::DebugBreakType -BreakLocation::BytecodeArrayIterator::GetDebugBreakType() { - BytecodeArray* bytecode_array = debug_info_->original_bytecode_array(); +DebugBreakType BytecodeArrayBreakIterator::GetDebugBreakType() { + BytecodeArray* bytecode_array = debug_info_->OriginalBytecodeArray(); interpreter::Bytecode bytecode = interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset())); @@ -246,225 +336,52 @@ BreakLocation::BytecodeArrayIterator::GetDebugBreakType() { } } -BreakLocation BreakLocation::BytecodeArrayIterator::GetBreakLocation() { - return BreakLocation(debug_info_, GetDebugBreakType(), code_offset(), - position(), statement_position()); -} - -// Find the break point at the supplied address, or the closest one before -// the address. -BreakLocation BreakLocation::FromCodeOffset(Handle<DebugInfo> debug_info, - int offset) { - base::SmartPointer<Iterator> it(GetIterator(debug_info)); - it->SkipTo(BreakIndexFromCodeOffset(debug_info, offset)); - return it->GetBreakLocation(); -} - -FrameSummary GetFirstFrameSummary(JavaScriptFrame* frame) { - List<FrameSummary> frames(FLAG_max_inlining_levels + 1); - frame->Summarize(&frames); - return frames.first(); -} - -int CallOffsetFromCodeOffset(int code_offset, bool is_interpreted) { - // Code offset points to the instruction after the call. Subtract 1 to - // exclude that instruction from the search. For bytecode, the code offset - // still points to the call. - return is_interpreted ? code_offset : code_offset - 1; -} - -BreakLocation BreakLocation::FromFrame(Handle<DebugInfo> debug_info, - JavaScriptFrame* frame) { - FrameSummary summary = GetFirstFrameSummary(frame); - int call_offset = - CallOffsetFromCodeOffset(summary.code_offset(), frame->is_interpreted()); - return FromCodeOffset(debug_info, call_offset); -} - -void BreakLocation::AllForStatementPosition(Handle<DebugInfo> debug_info, - int statement_position, - List<BreakLocation>* result_out) { - for (base::SmartPointer<Iterator> it(GetIterator(debug_info)); !it->Done(); - it->Next()) { - if (it->statement_position() == statement_position) { - result_out->Add(it->GetBreakLocation()); - } - } -} - -int BreakLocation::BreakIndexFromCodeOffset(Handle<DebugInfo> debug_info, - int offset) { - // Run through all break points to locate the one closest to the address. - int closest_break = 0; - int distance = kMaxInt; - DCHECK(0 <= offset && offset < debug_info->abstract_code()->Size()); - for (base::SmartPointer<Iterator> it(GetIterator(debug_info)); !it->Done(); - it->Next()) { - // Check if this break point is closer that what was previously found. - if (it->code_offset() <= offset && offset - it->code_offset() < distance) { - closest_break = it->break_index(); - distance = offset - it->code_offset(); - // Check whether we can't get any closer. - if (distance == 0) break; - } - } - return closest_break; -} - - -BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info, - int position, - BreakPositionAlignment alignment) { - // Run through all break points to locate the one closest to the source - // position. - int distance = kMaxInt; - base::SmartPointer<Iterator> it(GetIterator(debug_info)); - BreakLocation closest_break = it->GetBreakLocation(); - while (!it->Done()) { - int next_position; - if (alignment == STATEMENT_ALIGNED) { - next_position = it->statement_position(); - } else { - DCHECK(alignment == BREAK_POSITION_ALIGNED); - next_position = it->position(); - } - if (position <= next_position && next_position - position < distance) { - closest_break = it->GetBreakLocation(); - distance = next_position - position; - // Check whether we can't get any closer. - if (distance == 0) break; - } - it->Next(); - } - return closest_break; -} - - -void BreakLocation::SetBreakPoint(Handle<Object> break_point_object) { - // If there is not already a real break point here patch code with debug - // break. - if (!HasBreakPoint()) SetDebugBreak(); - DCHECK(IsDebugBreak() || IsDebuggerStatement()); - // Set the break point information. - DebugInfo::SetBreakPoint(debug_info_, code_offset_, position_, - statement_position_, break_point_object); -} - - -void BreakLocation::ClearBreakPoint(Handle<Object> break_point_object) { - // Clear the break point information. - DebugInfo::ClearBreakPoint(debug_info_, code_offset_, break_point_object); - // If there are no more break points here remove the debug break. - if (!HasBreakPoint()) { - ClearDebugBreak(); - DCHECK(!IsDebugBreak()); - } -} - - -void BreakLocation::SetOneShot() { - // Debugger statement always calls debugger. No need to modify it. - if (IsDebuggerStatement()) return; - - // If there is a real break point here no more to do. - if (HasBreakPoint()) { - DCHECK(IsDebugBreak()); - return; - } - - // Patch code with debug break. - SetDebugBreak(); -} - - -void BreakLocation::ClearOneShot() { - // Debugger statement always calls debugger. No need to modify it. - if (IsDebuggerStatement()) return; - - // If there is a real break point here no more to do. - if (HasBreakPoint()) { - DCHECK(IsDebugBreak()); - return; - } - - // Patch code removing debug break. - ClearDebugBreak(); - DCHECK(!IsDebugBreak()); -} - - -void BreakLocation::SetDebugBreak() { - // Debugger statement always calls debugger. No need to modify it. - if (IsDebuggerStatement()) return; - - // If there is already a break point here just return. This might happen if - // the same code is flooded with break points twice. Flooding the same - // function twice might happen when stepping in a function with an exception - // handler as the handler and the function is the same. - if (IsDebugBreak()) return; - - DCHECK(IsDebugBreakSlot()); - if (abstract_code()->IsCode()) { - Code* code = abstract_code()->GetCode(); - DCHECK(code->kind() == Code::FUNCTION); - Builtins* builtins = isolate()->builtins(); - Handle<Code> target = IsReturn() ? builtins->Return_DebugBreak() - : builtins->Slot_DebugBreak(); - Address pc = code->instruction_start() + code_offset(); - DebugCodegen::PatchDebugBreakSlot(isolate(), pc, target); - } else { - BytecodeArray* bytecode_array = abstract_code()->GetBytecodeArray(); - interpreter::Bytecode bytecode = - interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset())); - interpreter::Bytecode debugbreak = - interpreter::Bytecodes::GetDebugBreak(bytecode); - bytecode_array->set(code_offset(), - interpreter::Bytecodes::ToByte(debugbreak)); - } - DCHECK(IsDebugBreak()); +void BytecodeArrayBreakIterator::SkipToPosition( + int position, BreakPositionAlignment alignment) { + BytecodeArrayBreakIterator it(debug_info_, break_locator_type_); + SkipTo(it.BreakIndexFromPosition(position, alignment)); } - -void BreakLocation::ClearDebugBreak() { - // Debugger statement always calls debugger. No need to modify it. - if (IsDebuggerStatement()) return; - - DCHECK(IsDebugBreakSlot()); - if (abstract_code()->IsCode()) { - Code* code = abstract_code()->GetCode(); - DCHECK(code->kind() == Code::FUNCTION); - Address pc = code->instruction_start() + code_offset(); - DebugCodegen::ClearDebugBreakSlot(isolate(), pc); - } else { - BytecodeArray* bytecode_array = abstract_code()->GetBytecodeArray(); - BytecodeArray* original = debug_info_->original_bytecode_array(); - bytecode_array->set(code_offset(), original->get(code_offset())); - } - DCHECK(!IsDebugBreak()); +void BytecodeArrayBreakIterator::SetDebugBreak() { + DebugBreakType debug_break_type = GetDebugBreakType(); + if (debug_break_type == DEBUGGER_STATEMENT) return; + DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); + BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray(); + interpreter::Bytecode bytecode = + interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset())); + if (interpreter::Bytecodes::IsDebugBreak(bytecode)) return; + interpreter::Bytecode debugbreak = + interpreter::Bytecodes::GetDebugBreak(bytecode); + bytecode_array->set(code_offset(), + interpreter::Bytecodes::ToByte(debugbreak)); +} + +void BytecodeArrayBreakIterator::ClearDebugBreak() { + DebugBreakType debug_break_type = GetDebugBreakType(); + if (debug_break_type == DEBUGGER_STATEMENT) return; + DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); + BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray(); + BytecodeArray* original = debug_info_->OriginalBytecodeArray(); + bytecode_array->set(code_offset(), original->get(code_offset())); +} + +bool BytecodeArrayBreakIterator::IsDebugBreak() { + DebugBreakType debug_break_type = GetDebugBreakType(); + if (debug_break_type == DEBUGGER_STATEMENT) return false; + DCHECK(debug_break_type >= DEBUG_BREAK_SLOT); + BytecodeArray* bytecode_array = debug_info_->DebugBytecodeArray(); + interpreter::Bytecode bytecode = + interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset())); + return interpreter::Bytecodes::IsDebugBreak(bytecode); } - -bool BreakLocation::IsDebugBreak() const { - if (IsDebuggerStatement()) return false; - DCHECK(IsDebugBreakSlot()); - if (abstract_code()->IsCode()) { - Code* code = abstract_code()->GetCode(); - DCHECK(code->kind() == Code::FUNCTION); - Address pc = code->instruction_start() + code_offset(); - return DebugCodegen::DebugBreakSlotIsPatched(pc); - } else { - BytecodeArray* bytecode_array = abstract_code()->GetBytecodeArray(); - interpreter::Bytecode bytecode = - interpreter::Bytecodes::FromByte(bytecode_array->get(code_offset())); - return interpreter::Bytecodes::IsDebugBreak(bytecode); - } +BreakLocation BytecodeArrayBreakIterator::GetBreakLocation() { + Handle<AbstractCode> code( + AbstractCode::cast(debug_info_->DebugBytecodeArray())); + return BreakLocation(code, GetDebugBreakType(), code_offset(), position_); } -Handle<Object> BreakLocation::BreakPointObjects() const { - return debug_info_->GetBreakPointObjects(code_offset_); -} - void DebugFeatureTracker::Track(DebugFeatureTracker::Feature feature) { uint32_t mask = 1 << feature; // Only count one sample per feature and isolate. @@ -480,11 +397,11 @@ void Debug::ThreadInit() { thread_local_.break_id_ = 0; thread_local_.break_frame_id_ = StackFrame::NO_ID; thread_local_.last_step_action_ = StepNone; - thread_local_.last_statement_position_ = RelocInfo::kNoPosition; + thread_local_.last_statement_position_ = kNoSourcePosition; thread_local_.last_fp_ = 0; thread_local_.target_fp_ = 0; - thread_local_.step_in_enabled_ = false; thread_local_.return_value_ = Handle<Object>(); + clear_suspended_generator(); // TODO(isolates): frames_are_dropped_? base::NoBarrier_Store(&thread_local_.current_debug_scope_, static_cast<base::AtomicWord>(0)); @@ -492,25 +409,24 @@ void Debug::ThreadInit() { char* Debug::ArchiveDebug(char* storage) { - char* to = storage; - MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal)); + // Simply reset state. Don't archive anything. ThreadInit(); return storage + ArchiveSpacePerThread(); } char* Debug::RestoreDebug(char* storage) { - char* from = storage; - MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal)); + // Simply reset state. Don't restore anything. + ThreadInit(); return storage + ArchiveSpacePerThread(); } +int Debug::ArchiveSpacePerThread() { return 0; } -int Debug::ArchiveSpacePerThread() { - return sizeof(ThreadLocal); +void Debug::Iterate(ObjectVisitor* v) { + v->VisitPointer(&thread_local_.suspended_generator_); } - DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) { // Globalize the request debug info object and make it weak. GlobalHandles* global_handles = debug_info->GetIsolate()->global_handles(); @@ -543,9 +459,13 @@ bool Debug::Load() { // Create the debugger context. HandleScope scope(isolate_); ExtensionConfiguration no_extensions; + // TODO(yangguo): we rely on the fact that first context snapshot is usable + // as debug context. This dependency is gone once we remove + // debug context completely. + static const int kFirstContextSnapshotIndex = 0; Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment( MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), &no_extensions, - DEBUG_CONTEXT); + kFirstContextSnapshotIndex, DEBUG_CONTEXT); // Fail if no context could be created. if (context.is_null()) return false; @@ -594,14 +514,14 @@ void Debug::Break(JavaScriptFrame* frame) { // Return if we failed to retrieve the debug info. return; } - Handle<DebugInfo> debug_info(shared->GetDebugInfo()); + Handle<DebugInfo> debug_info(shared->GetDebugInfo(), isolate_); // Find the break location where execution has stopped. BreakLocation location = BreakLocation::FromFrame(debug_info, frame); // Find actual break points, if any, and trigger debug break event. - Handle<Object> break_points_hit = CheckBreakPoints(&location); - if (!break_points_hit->IsUndefined()) { + Handle<Object> break_points_hit = CheckBreakPoints(debug_info, &location); + if (!break_points_hit->IsUndefined(isolate_)) { // Clear all current stepping setup. ClearStepping(); // Notify the debug event listeners. @@ -631,12 +551,12 @@ void Debug::Break(JavaScriptFrame* frame) { step_break = location.IsTailCall(); // Fall through. case StepIn: { - FrameSummary summary = GetFirstFrameSummary(frame); + FrameSummary summary = FrameSummary::GetFirst(frame); int offset = summary.code_offset(); step_break = step_break || location.IsReturn() || (current_fp != last_fp) || (thread_local_.last_statement_position_ != - location.abstract_code()->SourceStatementPosition(offset)); + summary.abstract_code()->SourceStatementPosition(offset)); break; } case StepFrame: @@ -659,20 +579,22 @@ void Debug::Break(JavaScriptFrame* frame) { // Find break point objects for this location, if any, and evaluate them. // Return an array of break point objects that evaluated true. -Handle<Object> Debug::CheckBreakPoints(BreakLocation* location, +Handle<Object> Debug::CheckBreakPoints(Handle<DebugInfo> debug_info, + BreakLocation* location, bool* has_break_points) { Factory* factory = isolate_->factory(); bool has_break_points_to_check = - break_points_active_ && location->HasBreakPoint(); + break_points_active_ && location->HasBreakPoint(debug_info); if (has_break_points) *has_break_points = has_break_points_to_check; if (!has_break_points_to_check) return factory->undefined_value(); - Handle<Object> break_point_objects = location->BreakPointObjects(); + Handle<Object> break_point_objects = + debug_info->GetBreakPointObjects(location->position()); // Count the number of break points hit. If there are multiple break points // they are in a FixedArray. Handle<FixedArray> break_points_hit; int break_points_hit_count = 0; - DCHECK(!break_point_objects->IsUndefined()); + DCHECK(!break_point_objects->IsUndefined(isolate_)); if (break_point_objects->IsFixedArray()) { Handle<FixedArray> array(FixedArray::cast(*break_point_objects)); break_points_hit = factory->NewFixedArray(array->length()); @@ -710,17 +632,15 @@ bool Debug::IsMutedAtCurrentLocation(JavaScriptFrame* frame) { // Enter the debugger. DebugScope debug_scope(this); if (debug_scope.failed()) return false; - BreakLocation current_position = BreakLocation::FromFrame(debug_info, frame); List<BreakLocation> break_locations; - BreakLocation::AllForStatementPosition( - debug_info, current_position.statement_position(), &break_locations); + BreakLocation::AllAtCurrentStatement(debug_info, frame, &break_locations); bool has_break_points_at_all = false; for (int i = 0; i < break_locations.length(); i++) { bool has_break_points; Handle<Object> check_result = - CheckBreakPoints(&break_locations[i], &has_break_points); + CheckBreakPoints(debug_info, &break_locations[i], &has_break_points); has_break_points_at_all |= has_break_points; - if (has_break_points && !check_result->IsUndefined()) return false; + if (has_break_points && !check_result->IsUndefined(isolate_)) return false; } return has_break_points_at_all; } @@ -759,7 +679,7 @@ bool Debug::CheckBreakPoint(Handle<Object> break_point_object) { } // Return whether the break point is triggered. - return result->IsTrue(); + return result->IsTrue(isolate_); } @@ -780,15 +700,17 @@ bool Debug::SetBreakPoint(Handle<JSFunction> function, DCHECK(*source_position >= 0); // Find the break point and change it. - BreakLocation location = BreakLocation::FromPosition( - debug_info, *source_position, STATEMENT_ALIGNED); - *source_position = location.statement_position(); - location.SetBreakPoint(break_point_object); + *source_position = + FindBreakablePosition(debug_info, *source_position, STATEMENT_ALIGNED); + DebugInfo::SetBreakPoint(debug_info, *source_position, break_point_object); + // At least one active break point now. + DCHECK(debug_info->GetBreakPointCount() > 0); - feature_tracker()->Track(DebugFeatureTracker::kBreakPoint); + ClearBreakPoints(debug_info); + ApplyBreakPoints(debug_info); - // At least one active break point now. - return debug_info->GetBreakPointCount() > 0; + feature_tracker()->Track(DebugFeatureTracker::kBreakPoint); + return true; } @@ -796,12 +718,16 @@ bool Debug::SetBreakPointForScript(Handle<Script> script, Handle<Object> break_point_object, int* source_position, BreakPositionAlignment alignment) { + if (script->type() == Script::TYPE_WASM) { + // TODO(clemensh): set breakpoint for wasm. + return false; + } HandleScope scope(isolate_); // Obtain shared function info for the function. Handle<Object> result = FindSharedFunctionInfoInScript(script, *source_position); - if (result->IsUndefined()) return false; + if (result->IsUndefined(isolate_)) return false; // Make sure the function has set up the debug info. Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(result); @@ -812,76 +738,111 @@ bool Debug::SetBreakPointForScript(Handle<Script> script, // Find position within function. The script position might be before the // source position of the first function. - int position; if (shared->start_position() > *source_position) { - position = 0; - } else { - position = *source_position - shared->start_position(); + *source_position = shared->start_position(); } Handle<DebugInfo> debug_info(shared->GetDebugInfo()); - // Source positions starts with zero. - DCHECK(position >= 0); // Find the break point and change it. - BreakLocation location = - BreakLocation::FromPosition(debug_info, position, alignment); - location.SetBreakPoint(break_point_object); + *source_position = + FindBreakablePosition(debug_info, *source_position, alignment); + DebugInfo::SetBreakPoint(debug_info, *source_position, break_point_object); + // At least one active break point now. + DCHECK(debug_info->GetBreakPointCount() > 0); - feature_tracker()->Track(DebugFeatureTracker::kBreakPoint); + ClearBreakPoints(debug_info); + ApplyBreakPoints(debug_info); - position = (alignment == STATEMENT_ALIGNED) ? location.statement_position() - : location.position(); + feature_tracker()->Track(DebugFeatureTracker::kBreakPoint); + return true; +} - *source_position = position + shared->start_position(); +int Debug::FindBreakablePosition(Handle<DebugInfo> debug_info, + int source_position, + BreakPositionAlignment alignment) { + int statement_position; + int position; + if (debug_info->HasDebugCode()) { + CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); + 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); + it.SkipToPosition(source_position, alignment); + statement_position = it.statement_position(); + position = it.position(); + } + return alignment == STATEMENT_ALIGNED ? statement_position : position; +} - // At least one active break point now. - DCHECK(debug_info->GetBreakPointCount() > 0); - return true; +void Debug::ApplyBreakPoints(Handle<DebugInfo> debug_info) { + DisallowHeapAllocation no_gc; + if (debug_info->break_points()->IsUndefined(isolate_)) return; + FixedArray* break_points = debug_info->break_points(); + for (int i = 0; i < break_points->length(); i++) { + if (break_points->get(i)->IsUndefined(isolate_)) continue; + BreakPointInfo* info = BreakPointInfo::cast(break_points->get(i)); + if (info->GetBreakPointCount() == 0) continue; + if (debug_info->HasDebugCode()) { + CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); + it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED); + it.SetDebugBreak(); + } + if (debug_info->HasDebugBytecodeArray()) { + BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); + it.SkipToPosition(info->source_position(), BREAK_POSITION_ALIGNED); + it.SetDebugBreak(); + } + } } +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()) { + it.ClearDebugBreak(); + } + } + if (debug_info->HasDebugBytecodeArray()) { + for (BytecodeArrayBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); + !it.Done(); it.Next()) { + it.ClearDebugBreak(); + } + } +} void Debug::ClearBreakPoint(Handle<Object> break_point_object) { HandleScope scope(isolate_); - DebugInfoListNode* node = debug_info_list_; - while (node != NULL) { + for (DebugInfoListNode* node = debug_info_list_; node != NULL; + node = node->next()) { Handle<Object> result = DebugInfo::FindBreakPointInfo(node->debug_info(), break_point_object); - if (!result->IsUndefined()) { - // Get information in the break point. - Handle<BreakPointInfo> break_point_info = - Handle<BreakPointInfo>::cast(result); - Handle<DebugInfo> debug_info = node->debug_info(); - - BreakLocation location = BreakLocation::FromCodeOffset( - debug_info, break_point_info->code_offset()); - location.ClearBreakPoint(break_point_object); - - // If there are no more break points left remove the debug info for this - // function. + if (result->IsUndefined(isolate_)) continue; + Handle<DebugInfo> debug_info = node->debug_info(); + if (DebugInfo::ClearBreakPoint(debug_info, break_point_object)) { + ClearBreakPoints(debug_info); if (debug_info->GetBreakPointCount() == 0) { RemoveDebugInfoAndClearFromShared(debug_info); + } else { + ApplyBreakPoints(debug_info); } - return; } - node = node->next(); } } - // Clear out all the debug break code. This is ONLY supposed to be used when // shutting down the debugger as it will leave the break point information in // DebugInfo even though the code is patched back to the non break point state. void Debug::ClearAllBreakPoints() { for (DebugInfoListNode* node = debug_info_list_; node != NULL; node = node->next()) { - for (base::SmartPointer<BreakLocation::Iterator> it( - BreakLocation::GetIterator(node->debug_info())); - !it->Done(); it->Next()) { - it->GetBreakLocation().ClearDebugBreak(); - } + ClearBreakPoints(node->debug_info()); } // Remove all debug info. while (debug_info_list_ != NULL) { @@ -889,7 +850,6 @@ void Debug::ClearAllBreakPoints() { } } - void Debug::FloodWithOneShot(Handle<JSFunction> function, BreakLocatorType type) { // Debug utility functions are not subject to debugging. @@ -911,14 +871,19 @@ void Debug::FloodWithOneShot(Handle<JSFunction> function, // Flood the function with break points. Handle<DebugInfo> debug_info(shared->GetDebugInfo()); - for (base::SmartPointer<BreakLocation::Iterator> it( - BreakLocation::GetIterator(debug_info, type)); - !it->Done(); it->Next()) { - it->GetBreakLocation().SetOneShot(); + if (debug_info->HasDebugCode()) { + for (CodeBreakIterator it(debug_info, type); !it.Done(); it.Next()) { + it.SetDebugBreak(); + } + } + if (debug_info->HasDebugBytecodeArray()) { + for (BytecodeArrayBreakIterator it(debug_info, type); !it.Done(); + it.Next()) { + it.SetDebugBreak(); + } } } - void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) { if (type == BreakUncaughtException) { break_on_uncaught_exception_ = enable; @@ -938,14 +903,22 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { void Debug::PrepareStepIn(Handle<JSFunction> function) { + CHECK(last_step_action() >= StepIn); if (!is_active()) return; - if (last_step_action() < StepIn) return; if (in_debug_scope()) return; - if (thread_local_.step_in_enabled_) { - FloodWithOneShot(function); - } + FloodWithOneShot(function); } +void Debug::PrepareStepInSuspendedGenerator() { + CHECK(has_suspended_generator()); + if (!is_active()) return; + if (in_debug_scope()) return; + thread_local_.last_step_action_ = StepIn; + Handle<JSFunction> function( + JSGeneratorObject::cast(thread_local_.suspended_generator_)->function()); + FloodWithOneShot(function); + clear_suspended_generator(); +} void Debug::PrepareStepOnThrow() { if (!is_active()) return; @@ -1000,10 +973,7 @@ void Debug::PrepareStep(StepAction step_action) { feature_tracker()->Track(DebugFeatureTracker::kStepping); - // Remember this step action and count. thread_local_.last_step_action_ = step_action; - STATIC_ASSERT(StepFrame > StepIn); - thread_local_.step_in_enabled_ = (step_action >= StepIn); // 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 @@ -1019,7 +989,7 @@ void Debug::PrepareStep(StepAction step_action) { } // Get the debug info (create it if it does not exist). - FrameSummary summary = GetFirstFrameSummary(frame); + FrameSummary summary = FrameSummary::GetFirst(frame); Handle<JSFunction> function(summary.function()); Handle<SharedFunctionInfo> shared(function->shared()); if (!EnsureDebugInfo(shared, function)) { @@ -1028,15 +998,7 @@ void Debug::PrepareStep(StepAction step_action) { } Handle<DebugInfo> debug_info(shared->GetDebugInfo()); - // Refresh frame summary if the code has been recompiled for debugging. - if (AbstractCode::cast(shared->code()) != *summary.abstract_code()) { - summary = GetFirstFrameSummary(frame); - } - - int call_offset = - CallOffsetFromCodeOffset(summary.code_offset(), frame->is_interpreted()); - BreakLocation location = - BreakLocation::FromCodeOffset(debug_info, call_offset); + BreakLocation location = BreakLocation::FromFrame(debug_info, frame); // Any step at a return is a step-out. if (location.IsReturn()) step_action = StepOut; @@ -1044,9 +1006,10 @@ void Debug::PrepareStep(StepAction step_action) { if (location.IsTailCall() && step_action == StepNext) step_action = StepOut; thread_local_.last_statement_position_ = - debug_info->abstract_code()->SourceStatementPosition( - summary.code_offset()); + summary.abstract_code()->SourceStatementPosition(summary.code_offset()); thread_local_.last_fp_ = frame->UnpaddedFP(); + // No longer perform the current async step. + clear_suspended_generator(); switch (step_action) { case StepNone: @@ -1063,18 +1026,14 @@ void Debug::PrepareStep(StepAction step_action) { Deoptimizer::DeoptimizeFunction(frames_it.frame()->function()); frames_it.Advance(); } - if (frames_it.done()) { - // Stepping out to the embedder. Disable step-in to avoid stepping into - // the next (unrelated) call that the embedder makes. - thread_local_.step_in_enabled_ = false; - } else { + if (!frames_it.done()) { // Fill the caller function to return to with one-shot break points. Handle<JSFunction> caller_function(frames_it.frame()->function()); FloodWithOneShot(caller_function); thread_local_.target_fp_ = frames_it.frame()->UnpaddedFP(); } // Clear last position info. For stepping out it does not matter. - thread_local_.last_statement_position_ = RelocInfo::kNoPosition; + thread_local_.last_statement_position_ = kNoSourcePosition; thread_local_.last_fp_ = 0; break; case StepNext: @@ -1092,37 +1051,44 @@ void Debug::PrepareStep(StepAction step_action) { } } - // Simple function for returning the source positions for active break points. Handle<Object> Debug::GetSourceBreakLocations( Handle<SharedFunctionInfo> shared, BreakPositionAlignment position_alignment) { Isolate* isolate = shared->GetIsolate(); - Heap* heap = isolate->heap(); if (!shared->HasDebugInfo()) { - return Handle<Object>(heap->undefined_value(), isolate); + return isolate->factory()->undefined_value(); } Handle<DebugInfo> debug_info(shared->GetDebugInfo()); if (debug_info->GetBreakPointCount() == 0) { - return Handle<Object>(heap->undefined_value(), isolate); + return isolate->factory()->undefined_value(); } Handle<FixedArray> locations = isolate->factory()->NewFixedArray(debug_info->GetBreakPointCount()); int count = 0; for (int i = 0; i < debug_info->break_points()->length(); ++i) { - if (!debug_info->break_points()->get(i)->IsUndefined()) { + if (!debug_info->break_points()->get(i)->IsUndefined(isolate)) { BreakPointInfo* break_point_info = BreakPointInfo::cast(debug_info->break_points()->get(i)); int break_points = break_point_info->GetBreakPointCount(); if (break_points == 0) continue; Smi* position = NULL; - switch (position_alignment) { - case STATEMENT_ALIGNED: - position = Smi::FromInt(break_point_info->statement_position()); - break; - case BREAK_POSITION_ALIGNED: - position = Smi::FromInt(break_point_info->source_position()); - break; + if (position_alignment == STATEMENT_ALIGNED) { + if (debug_info->HasDebugCode()) { + CodeBreakIterator it(debug_info, ALL_BREAK_LOCATIONS); + 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); + it.SkipToPosition(break_point_info->source_position(), + BREAK_POSITION_ALIGNED); + position = Smi::FromInt(it.statement_position()); + } + } else { + DCHECK_EQ(BREAK_POSITION_ALIGNED, position_alignment); + position = Smi::FromInt(break_point_info->source_position()); } for (int j = 0; j < break_points; ++j) locations->set(count++, position); } @@ -1130,14 +1096,12 @@ Handle<Object> Debug::GetSourceBreakLocations( return locations; } - void Debug::ClearStepping() { // Clear the various stepping setup. ClearOneShot(); thread_local_.last_step_action_ = StepNone; - thread_local_.step_in_enabled_ = false; - thread_local_.last_statement_position_ = RelocInfo::kNoPosition; + thread_local_.last_statement_position_ = kNoSourcePosition; thread_local_.last_fp_ = 0; thread_local_.target_fp_ = 0; } @@ -1152,21 +1116,13 @@ void Debug::ClearOneShot() { // removed from the list. for (DebugInfoListNode* node = debug_info_list_; node != NULL; node = node->next()) { - for (base::SmartPointer<BreakLocation::Iterator> it( - BreakLocation::GetIterator(node->debug_info())); - !it->Done(); it->Next()) { - it->GetBreakLocation().ClearOneShot(); - } + Handle<DebugInfo> debug_info = node->debug_info(); + ClearBreakPoints(debug_info); + ApplyBreakPoints(debug_info); } } -void Debug::EnableStepIn() { - STATIC_ASSERT(StepFrame > StepIn); - thread_local_.step_in_enabled_ = (last_step_action() >= StepIn); -} - - bool MatchingCodeTargets(Code* target1, Code* target2) { if (target1 == target2) return true; if (target1->kind() != target2->kind()) return false; @@ -1266,7 +1222,7 @@ class RedirectActiveFunctions : public ThreadVisitor { InterpretedFrame* interpreted_frame = reinterpret_cast<InterpretedFrame*>(frame); BytecodeArray* debug_copy = - shared_->GetDebugInfo()->abstract_code()->GetBytecodeArray(); + shared_->GetDebugInfo()->DebugBytecodeArray(); interpreted_frame->PatchBytecodeArray(debug_copy); continue; } @@ -1319,9 +1275,7 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { { SharedFunctionInfo::Iterator iterator(isolate_); while (SharedFunctionInfo* shared = iterator.Next()) { - if (!shared->OptimizedCodeMapIsCleared()) { - shared->ClearOptimizedCodeMap(); - } + shared->ClearCodeFromOptimizedCodeMap(); } } @@ -1329,7 +1283,8 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, "prepare for break points"); - bool is_interpreted = shared->HasBytecodeArray(); + DCHECK(shared->is_compiled()); + bool baseline_exists = shared->HasBaselineCode(); { // TODO(yangguo): with bytecode, we still walk the heap to find all @@ -1337,7 +1292,8 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { // smarter here and avoid the heap walk. HeapIterator iterator(isolate_->heap()); HeapObject* obj; - bool include_generators = !is_interpreted && shared->is_generator(); + // Continuation from old-style generators need to be recomputed. + bool find_resumables = baseline_exists && shared->is_resumable(); while ((obj = iterator.next())) { if (obj->IsJSFunction()) { @@ -1346,9 +1302,12 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) { Deoptimizer::DeoptimizeFunction(function); } - if (is_interpreted) continue; - if (function->shared() == *shared) functions.Add(handle(function)); - } else if (include_generators && obj->IsJSGeneratorObject()) { + if (baseline_exists && function->shared() == *shared) { + functions.Add(handle(function)); + } + } else if (find_resumables && obj->IsJSGeneratorObject()) { + // This case handles async functions as well, as they use generator + // objects for in-progress async function execution. JSGeneratorObject* generator_obj = JSGeneratorObject::cast(obj); if (!generator_obj->is_suspended()) continue; JSFunction* function = generator_obj->function(); @@ -1363,17 +1322,18 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { } // We do not need to replace code to debug bytecode. - DCHECK(!is_interpreted || functions.length() == 0); - DCHECK(!is_interpreted || suspended_generators.length() == 0); + DCHECK(baseline_exists || functions.is_empty()); + DCHECK(baseline_exists || suspended_generators.is_empty()); // We do not need to recompile to debug bytecode. - if (!is_interpreted && !shared->HasDebugCode()) { + if (baseline_exists && !shared->code()->has_debug_break_slots()) { DCHECK(functions.length() > 0); if (!Compiler::CompileDebugCode(functions.first())) return false; } for (Handle<JSFunction> const function : functions) { function->ReplaceCode(shared->code()); + JSFunction::EnsureLiterals(function); } for (Handle<JSGeneratorObject> const generator_obj : suspended_generators) { @@ -1390,19 +1350,26 @@ bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { return true; } +void Debug::RecordAsyncFunction(Handle<JSGeneratorObject> generator_object) { + if (last_step_action() <= StepOut) return; + if (!generator_object->function()->shared()->is_async()) return; + DCHECK(!has_suspended_generator()); + thread_local_.suspended_generator_ = *generator_object; + ClearStepping(); +} class SharedFunctionInfoFinder { public: explicit SharedFunctionInfoFinder(int target_position) : current_candidate_(NULL), current_candidate_closure_(NULL), - current_start_position_(RelocInfo::kNoPosition), + current_start_position_(kNoSourcePosition), target_position_(target_position) {} void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) { if (!shared->IsSubjectToDebugging()) return; int start_position = shared->function_token_position(); - if (start_position == RelocInfo::kNoPosition) { + if (start_position == kNoSourcePosition) { start_position = shared->start_position(); } @@ -1538,23 +1505,17 @@ bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared, return false; } - if (shared->HasBytecodeArray()) { - // To prepare bytecode for debugging, we already need to have the debug - // info (containing the debug copy) upfront, but since we do not recompile, - // preparing for break points cannot fail. - CreateDebugInfo(shared); - CHECK(PrepareFunctionForBreakPoints(shared)); - } else { - if (!PrepareFunctionForBreakPoints(shared)) return false; - CreateDebugInfo(shared); - } + // To prepare bytecode for debugging, we already need to have the debug + // info (containing the debug copy) upfront, but since we do not recompile, + // preparing for break points cannot fail. + CreateDebugInfo(shared); + CHECK(PrepareFunctionForBreakPoints(shared)); return true; } void Debug::CreateDebugInfo(Handle<SharedFunctionInfo> shared) { // Create the debug info object. - DCHECK(shared->HasDebugCode()); Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared); // Add debug info to the list. @@ -1600,23 +1561,18 @@ void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) { } } - bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { HandleScope scope(isolate_); // Get the executing function in which the debug break occurred. - Handle<JSFunction> function(JSFunction::cast(frame->function())); - Handle<SharedFunctionInfo> shared(function->shared()); + Handle<SharedFunctionInfo> shared(frame->function()->shared()); // With no debug info there are no break points, so we can't be at a return. if (!shared->HasDebugInfo()) return false; DCHECK(!frame->is_optimized()); - FrameSummary summary = GetFirstFrameSummary(frame); - Handle<DebugInfo> debug_info(shared->GetDebugInfo()); - BreakLocation location = - BreakLocation::FromCodeOffset(debug_info, summary.code_offset()); + BreakLocation location = BreakLocation::FromFrame(debug_info, frame); return location.IsReturn() || location.IsTailCall(); } @@ -1664,21 +1620,6 @@ Handle<FixedArray> Debug::GetLoadedScripts() { } -void Debug::RecordEvalCaller(Handle<Script> script) { - script->set_compilation_type(Script::COMPILATION_TYPE_EVAL); - // For eval scripts add information on the function from which eval was - // called. - StackTraceFrameIterator it(script->GetIsolate()); - if (!it.done()) { - script->set_eval_from_shared(it.frame()->function()->shared()); - Code* code = it.frame()->LookupCode(); - int offset = static_cast<int>( - it.frame()->pc() - code->instruction_start()); - script->set_eval_from_instructions_offset(offset); - } -} - - MaybeHandle<Object> Debug::MakeExecutionState() { // Create the execution state object. Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()) }; @@ -1716,13 +1657,6 @@ MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script, } -MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) { - // Create the promise event object. - Handle<Object> argv[] = { event_data }; - return CallFunction("MakePromiseEvent", arraysize(argv), argv); -} - - MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) { // Create the async task event object. Handle<Object> argv[] = { task_event }; @@ -1753,7 +1687,7 @@ void Debug::OnPromiseReject(Handle<JSObject> promise, Handle<Object> value) { HandleScope scope(isolate_); // Check whether the promise has been marked as having triggered a message. Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol(); - if (JSReceiver::GetDataProperty(promise, key)->IsUndefined()) { + if (JSReceiver::GetDataProperty(promise, key)->IsUndefined(isolate_)) { OnException(value, promise); } } @@ -1767,8 +1701,11 @@ MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler( void Debug::OnException(Handle<Object> exception, Handle<Object> promise) { - // In our prediction, try-finally is not considered to catch. Isolate::CatchType catch_type = isolate_->PredictExceptionCatcher(); + + // Don't notify listener of exceptions that are internal to a desugaring. + if (catch_type == Isolate::CAUGHT_BY_DESUGARING) return; + bool uncaught = (catch_type == Isolate::NOT_CAUGHT); if (promise->IsJSObject()) { Handle<JSObject> jspromise = Handle<JSObject>::cast(promise); @@ -1780,7 +1717,7 @@ void Debug::OnException(Handle<Object> exception, Handle<Object> promise) { ASSIGN_RETURN_ON_EXCEPTION_VALUE( isolate_, has_reject_handler, PromiseHasUserDefinedRejectHandler(jspromise), /* void */); - uncaught = has_reject_handler->IsFalse(); + uncaught = has_reject_handler->IsFalse(isolate_); } // Bail out if exception breaks are not active if (uncaught) { @@ -1853,25 +1790,6 @@ void Debug::OnAfterCompile(Handle<Script> script) { } -void Debug::OnPromiseEvent(Handle<JSObject> data) { - if (in_debug_scope() || ignore_events()) return; - - HandleScope scope(isolate_); - DebugScope debug_scope(this); - if (debug_scope.failed()) return; - - // Create the script collected state object. - Handle<Object> event_data; - // Bail out and don't call debugger if exception. - if (!MakePromiseEvent(data).ToHandle(&event_data)) return; - - // Process debug event. - ProcessDebugEvent(v8::PromiseEvent, - Handle<JSObject>::cast(event_data), - true); -} - - void Debug::OnAsyncTaskEvent(Handle<JSObject> data) { if (in_debug_scope() || ignore_events()) return; @@ -1945,7 +1863,7 @@ void Debug::CallEventCallback(v8::DebugEvent event, exec_state, event_data, event_listener_data_ }; - Handle<JSReceiver> global(isolate_->global_proxy()); + Handle<JSReceiver> global = isolate_->global_proxy(); Execution::TryCall(isolate_, Handle<JSFunction>::cast(event_listener_), global, arraysize(argv), argv); } @@ -2021,7 +1939,6 @@ void Debug::NotifyMessageHandler(v8::DebugEvent event, case v8::NewFunction: case v8::BeforeCompile: case v8::CompileError: - case v8::PromiseEvent: case v8::AsyncTaskEvent: break; case v8::Exception: @@ -2099,7 +2016,7 @@ void Debug::NotifyMessageHandler(v8::DebugEvent event, request_args, &maybe_exception); if (maybe_result.ToHandle(&answer_value)) { - if (answer_value->IsUndefined()) { + if (answer_value->IsUndefined(isolate_)) { answer = isolate_->factory()->empty_string(); } else { answer = Handle<String>::cast(answer_value); @@ -2116,7 +2033,7 @@ void Debug::NotifyMessageHandler(v8::DebugEvent event, isolate_, is_running, cmd_processor, 1, is_running_args); Handle<Object> result; if (!maybe_result.ToHandle(&result)) break; - running = result->IsTrue(); + running = result->IsTrue(isolate_); } else { Handle<Object> exception; if (!maybe_exception.ToHandle(&exception)) break; @@ -2150,7 +2067,7 @@ void Debug::SetEventListener(Handle<Object> callback, event_listener_data_ = Handle<Object>(); // Set new entry. - if (!callback->IsUndefined() && !callback->IsNull()) { + if (!callback->IsUndefined(isolate_) && !callback->IsNull(isolate_)) { event_listener_ = global_handles->Create(*callback); if (data.is_null()) data = isolate_->factory()->undefined_value(); event_listener_data_ = global_handles->Create(*data); @@ -2295,7 +2212,7 @@ void Debug::PrintBreakLocation() { JavaScriptFrameIterator iterator(isolate_); if (iterator.done()) return; JavaScriptFrame* frame = iterator.frame(); - FrameSummary summary = GetFirstFrameSummary(frame); + FrameSummary summary = FrameSummary::GetFirst(frame); int source_position = summary.abstract_code()->SourcePosition(summary.code_offset()); Handle<Object> script_obj(summary.function()->shared()->script(), isolate_); @@ -2306,8 +2223,10 @@ void Debug::PrintBreakLocation() { Handle<Script> script = Handle<Script>::cast(script_obj); Handle<String> source(String::cast(script->source())); Script::InitLineEnds(script); - int line = Script::GetLineNumber(script, source_position); - int column = Script::GetColumnNumber(script, source_position); + int line = + Script::GetLineNumber(script, source_position) - script->line_offset(); + int column = Script::GetColumnNumber(script, source_position) - + (line == 0 ? script->column_offset() : 0); Handle<FixedArray> line_ends(FixedArray::cast(script->line_ends())); int line_start = line == 0 ? 0 : Smi::cast(line_ends->get(line - 1))->value() + 1; @@ -2342,12 +2261,14 @@ DebugScope::DebugScope(Debug* debug) break_frame_id_ = debug_->break_frame_id(); return_value_ = debug_->return_value(); - // Create the new break info. If there is no JavaScript frames there is no - // break frame id. - JavaScriptFrameIterator it(isolate()); - bool has_js_frames = !it.done(); - debug_->thread_local_.break_frame_id_ = has_js_frames ? it.frame()->id() - : StackFrame::NO_ID; + // Create the new break info. If there is no proper frames there is no break + // frame id. + StackTraceFrameIterator it(isolate()); + bool has_frames = !it.done(); + // We don't currently support breaking inside wasm framess. + DCHECK(!has_frames || !it.is_wasm()); + debug_->thread_local_.break_frame_id_ = + has_frames ? it.frame()->id() : StackFrame::NO_ID; debug_->SetNextBreakId(); debug_->UpdateState(); @@ -2482,6 +2403,16 @@ v8::Local<v8::String> MessageImpl::GetJSON() const { } } +namespace { +v8::Local<v8::Context> GetDebugEventContext(Isolate* isolate) { + Handle<Context> context = isolate->debug()->debugger_entry()->GetContext(); + // Isolate::context() may have been NULL when "script collected" event + // occured. + if (context.is_null()) return v8::Local<v8::Context>(); + Handle<Context> native_context(context->native_context()); + return v8::Utils::ToLocal(native_context); +} +} // anonymous namespace v8::Local<v8::Context> MessageImpl::GetEventContext() const { Isolate* isolate = event_data_->GetIsolate(); @@ -2538,6 +2469,9 @@ v8::Debug::ClientData* EventDetailsImpl::GetClientData() const { return client_data_; } +v8::Isolate* EventDetailsImpl::GetIsolate() const { + return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate()); +} CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()), client_data_(NULL) { |