Index: src/debug.cc |
diff --git a/src/debug.cc b/src/debug.cc |
deleted file mode 100644 |
index 38d9f47f29d186a41e6d4d225fb1fd2369e6a4f6..0000000000000000000000000000000000000000 |
--- a/src/debug.cc |
+++ /dev/null |
@@ -1,2811 +0,0 @@ |
-// Copyright 2012 the V8 project authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "src/v8.h" |
- |
-#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.h" |
-#include "src/debug.h" |
-#include "src/deoptimizer.h" |
-#include "src/execution.h" |
-#include "src/full-codegen/full-codegen.h" |
-#include "src/global-handles.h" |
-#include "src/list.h" |
-#include "src/log.h" |
-#include "src/messages.h" |
-#include "src/snapshot/natives.h" |
- |
-#include "include/v8-debug.h" |
- |
-namespace v8 { |
-namespace internal { |
- |
-Debug::Debug(Isolate* isolate) |
- : debug_context_(Handle<Context>()), |
- event_listener_(Handle<Object>()), |
- event_listener_data_(Handle<Object>()), |
- message_handler_(NULL), |
- command_received_(0), |
- command_queue_(isolate->logger(), kQueueInitialSize), |
- is_active_(false), |
- is_suppressed_(false), |
- live_edit_enabled_(true), // TODO(yangguo): set to false by default. |
- break_disabled_(false), |
- in_debug_event_listener_(false), |
- break_on_exception_(false), |
- break_on_uncaught_exception_(false), |
- script_cache_(NULL), |
- debug_info_list_(NULL), |
- isolate_(isolate) { |
- ThreadInit(); |
-} |
- |
- |
-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); |
-} |
- |
- |
-BreakLocation::BreakLocation(Handle<DebugInfo> debug_info, RelocInfo* rinfo, |
- int position, int statement_position) |
- : debug_info_(debug_info), |
- pc_offset_(static_cast<int>(rinfo->pc() - debug_info->code()->entry())), |
- rmode_(rinfo->rmode()), |
- data_(rinfo->data()), |
- position_(position), |
- statement_position_(statement_position) {} |
- |
- |
-BreakLocation::Iterator::Iterator(Handle<DebugInfo> debug_info, |
- BreakLocatorType type) |
- : debug_info_(debug_info), |
- reloc_iterator_(debug_info->code(), GetModeMask(type)), |
- break_index_(-1), |
- position_(1), |
- statement_position_(1) { |
- if (!Done()) Next(); |
-} |
- |
- |
-int BreakLocation::Iterator::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); |
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_CONSTRUCT_CALL); |
- if (type == ALL_BREAK_LOCATIONS) { |
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_POSITION); |
- mask |= RelocInfo::ModeMask(RelocInfo::DEBUGGER_STATEMENT); |
- } |
- return mask; |
-} |
- |
- |
-void BreakLocation::Iterator::Next() { |
- DisallowHeapAllocation no_gc; |
- DCHECK(!Done()); |
- |
- // Iterate through reloc info for code and original code 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 (RelocInfo::IsDebugBreakSlotAtReturn(rmode())) { |
- // Set the positions to the end of the function. |
- if (debug_info_->shared()->HasSourceCode()) { |
- position_ = debug_info_->shared()->end_position() - |
- debug_info_->shared()->start_position() - 1; |
- } else { |
- position_ = 0; |
- } |
- statement_position_ = position_; |
- } |
- |
- break; |
- } |
- break_index_++; |
-} |
- |
- |
-// Find the break point at the supplied address, or the closest one before |
-// the address. |
-BreakLocation BreakLocation::FromAddress(Handle<DebugInfo> debug_info, |
- BreakLocatorType type, Address pc) { |
- Iterator it(debug_info, type); |
- it.SkipTo(BreakIndexFromAddress(debug_info, type, pc)); |
- return it.GetBreakLocation(); |
-} |
- |
- |
-// Find the break point at the supplied address, or the closest one before |
-// the address. |
-void BreakLocation::FromAddressSameStatement(Handle<DebugInfo> debug_info, |
- BreakLocatorType type, Address pc, |
- List<BreakLocation>* result_out) { |
- int break_index = BreakIndexFromAddress(debug_info, type, pc); |
- Iterator it(debug_info, type); |
- it.SkipTo(break_index); |
- int statement_position = it.statement_position(); |
- while (!it.Done() && it.statement_position() == statement_position) { |
- result_out->Add(it.GetBreakLocation()); |
- it.Next(); |
- } |
-} |
- |
- |
-int BreakLocation::BreakIndexFromAddress(Handle<DebugInfo> debug_info, |
- BreakLocatorType type, Address pc) { |
- // Run through all break points to locate the one closest to the address. |
- int closest_break = 0; |
- int distance = kMaxInt; |
- for (Iterator it(debug_info, type); !it.Done(); it.Next()) { |
- // Check if this break point is closer that what was previously found. |
- if (it.pc() <= pc && pc - it.pc() < distance) { |
- closest_break = it.break_index(); |
- distance = static_cast<int>(pc - it.pc()); |
- // Check whether we can't get any closer. |
- if (distance == 0) break; |
- } |
- } |
- return closest_break; |
-} |
- |
- |
-BreakLocation BreakLocation::FromPosition(Handle<DebugInfo> debug_info, |
- BreakLocatorType type, int position, |
- BreakPositionAlignment alignment) { |
- // Run through all break points to locate the one closest to the source |
- // position. |
- int closest_break = 0; |
- int distance = kMaxInt; |
- |
- for (Iterator it(debug_info, type); !it.Done(); it.Next()) { |
- 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.break_index(); |
- distance = next_position - position; |
- // Check whether we can't get any closer. |
- if (distance == 0) break; |
- } |
- } |
- |
- Iterator it(debug_info, type); |
- it.SkipTo(closest_break); |
- return it.GetBreakLocation(); |
-} |
- |
- |
-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_, pc_offset_, position_, |
- statement_position_, break_point_object); |
-} |
- |
- |
-void BreakLocation::ClearBreakPoint(Handle<Object> break_point_object) { |
- // Clear the break point information. |
- DebugInfo::ClearBreakPoint(debug_info_, pc_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()); |
- Builtins* builtins = debug_info_->GetIsolate()->builtins(); |
- Handle<Code> target = |
- IsReturn() ? builtins->Return_DebugBreak() : builtins->Slot_DebugBreak(); |
- DebugCodegen::PatchDebugBreakSlot(pc(), target); |
- DCHECK(IsDebugBreak()); |
-} |
- |
- |
-void BreakLocation::ClearDebugBreak() { |
- // Debugger statement always calls debugger. No need to modify it. |
- if (IsDebuggerStatement()) return; |
- |
- DCHECK(IsDebugBreakSlot()); |
- DebugCodegen::ClearDebugBreakSlot(pc()); |
- DCHECK(!IsDebugBreak()); |
-} |
- |
- |
-bool BreakLocation::IsStepInLocation() const { |
- return IsConstructCall() || IsCall(); |
-} |
- |
- |
-bool BreakLocation::IsDebugBreak() const { |
- if (IsDebugBreakSlot()) { |
- return rinfo().IsPatchedDebugBreakSlotSequence(); |
- } |
- return false; |
-} |
- |
- |
-Handle<Object> BreakLocation::BreakPointObjects() const { |
- return debug_info_->GetBreakPointObjects(pc_offset_); |
-} |
- |
- |
-// Threading support. |
-void Debug::ThreadInit() { |
- thread_local_.break_count_ = 0; |
- 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_.step_count_ = 0; |
- thread_local_.last_fp_ = 0; |
- thread_local_.queued_step_count_ = 0; |
- thread_local_.step_into_fp_ = 0; |
- thread_local_.step_out_fp_ = 0; |
- // TODO(isolates): frames_are_dropped_? |
- base::NoBarrier_Store(&thread_local_.current_debug_scope_, |
- static_cast<base::AtomicWord>(0)); |
- thread_local_.restarter_frame_function_pointer_ = NULL; |
-} |
- |
- |
-char* Debug::ArchiveDebug(char* storage) { |
- char* to = storage; |
- MemCopy(to, reinterpret_cast<char*>(&thread_local_), sizeof(ThreadLocal)); |
- ThreadInit(); |
- return storage + ArchiveSpacePerThread(); |
-} |
- |
- |
-char* Debug::RestoreDebug(char* storage) { |
- char* from = storage; |
- MemCopy(reinterpret_cast<char*>(&thread_local_), from, sizeof(ThreadLocal)); |
- return storage + ArchiveSpacePerThread(); |
-} |
- |
- |
-int Debug::ArchiveSpacePerThread() { |
- return sizeof(ThreadLocal); |
-} |
- |
- |
-ScriptCache::ScriptCache(Isolate* isolate) : isolate_(isolate) { |
- Heap* heap = isolate_->heap(); |
- HandleScope scope(isolate_); |
- |
- DCHECK(isolate_->debug()->is_active()); |
- |
- // Perform a GC to get rid of all unreferenced scripts. |
- heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "ScriptCache"); |
- |
- // Scan heap for Script objects. |
- List<Handle<Script> > scripts; |
- { |
- HeapIterator iterator(heap, HeapIterator::kFilterUnreachable); |
- DisallowHeapAllocation no_allocation; |
- for (HeapObject* obj = iterator.next(); obj != NULL; |
- obj = iterator.next()) { |
- if (obj->IsScript() && Script::cast(obj)->HasValidSource()) { |
- scripts.Add(Handle<Script>(Script::cast(obj))); |
- } |
- } |
- } |
- |
- GlobalHandles* global_handles = isolate_->global_handles(); |
- table_ = Handle<WeakValueHashTable>::cast(global_handles->Create( |
- Object::cast(*WeakValueHashTable::New(isolate_, scripts.length())))); |
- for (int i = 0; i < scripts.length(); i++) Add(scripts[i]); |
-} |
- |
- |
-void ScriptCache::Add(Handle<Script> script) { |
- HandleScope scope(isolate_); |
- Handle<Smi> id(script->id(), isolate_); |
- |
-#ifdef DEBUG |
- Handle<Object> lookup(table_->LookupWeak(id), isolate_); |
- if (!lookup->IsTheHole()) { |
- Handle<Script> found = Handle<Script>::cast(lookup); |
- DCHECK(script->id() == found->id()); |
- DCHECK(!script->name()->IsString() || |
- String::cast(script->name())->Equals(String::cast(found->name()))); |
- } |
-#endif |
- |
- Handle<WeakValueHashTable> new_table = |
- WeakValueHashTable::PutWeak(table_, id, script); |
- |
- if (new_table.is_identical_to(table_)) return; |
- GlobalHandles* global_handles = isolate_->global_handles(); |
- global_handles->Destroy(Handle<Object>::cast(table_).location()); |
- table_ = Handle<WeakValueHashTable>::cast( |
- global_handles->Create(Object::cast(*new_table))); |
-} |
- |
- |
-ScriptCache::~ScriptCache() { |
- isolate_->global_handles()->Destroy(Handle<Object>::cast(table_).location()); |
- table_ = Handle<WeakValueHashTable>(); |
-} |
- |
- |
-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(); |
- debug_info_ = |
- Handle<DebugInfo>::cast(global_handles->Create(debug_info)).location(); |
-} |
- |
- |
-DebugInfoListNode::~DebugInfoListNode() { |
- if (debug_info_ == nullptr) return; |
- GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_info_)); |
- debug_info_ = nullptr; |
-} |
- |
- |
-bool Debug::CompileDebuggerScript(Isolate* isolate, int index) { |
- Factory* factory = isolate->factory(); |
- HandleScope scope(isolate); |
- |
- // Bail out if the index is invalid. |
- if (index == -1) return false; |
- |
- // Find source and name for the requested script. |
- Handle<String> source_code = |
- isolate->bootstrapper()->SourceLookup<Natives>(index); |
- Vector<const char> name = Natives::GetScriptName(index); |
- Handle<String> script_name = |
- factory->NewStringFromAscii(name).ToHandleChecked(); |
- Handle<Context> context = isolate->native_context(); |
- |
- // Compile the script. |
- Handle<SharedFunctionInfo> function_info; |
- function_info = Compiler::CompileScript( |
- source_code, script_name, 0, 0, ScriptOriginOptions(), Handle<Object>(), |
- context, NULL, NULL, ScriptCompiler::kNoCompileOptions, NATIVES_CODE, |
- false); |
- if (function_info.is_null()) return false; |
- |
- // Execute the shared function in the debugger context. |
- Handle<JSFunction> function = |
- factory->NewFunctionFromSharedFunctionInfo(function_info, context); |
- |
- MaybeHandle<Object> maybe_exception; |
- MaybeHandle<Object> result = Execution::TryCall( |
- function, handle(context->global_proxy()), 0, NULL, &maybe_exception); |
- |
- // Check for caught exceptions. |
- if (result.is_null()) { |
- DCHECK(!isolate->has_pending_exception()); |
- MessageLocation computed_location; |
- isolate->ComputeLocation(&computed_location); |
- Handle<JSMessageObject> message = MessageHandler::MakeMessageObject( |
- isolate, MessageTemplate::kDebuggerLoading, &computed_location, |
- isolate->factory()->undefined_value(), Handle<JSArray>()); |
- DCHECK(!isolate->has_pending_exception()); |
- Handle<Object> exception; |
- if (maybe_exception.ToHandle(&exception)) { |
- isolate->set_pending_exception(*exception); |
- MessageHandler::ReportMessage(isolate, NULL, message); |
- } |
- DCHECK(!maybe_exception.is_null()); |
- return false; |
- } |
- |
- // Mark this script as native and return successfully. |
- Handle<Script> script(Script::cast(function->shared()->script())); |
- script->set_type(Smi::FromInt(Script::TYPE_NATIVE)); |
- return true; |
-} |
- |
- |
-bool Debug::Load() { |
- // Return if debugger is already loaded. |
- if (is_loaded()) return true; |
- |
- // Bail out if we're already in the process of compiling the native |
- // JavaScript source code for the debugger. |
- if (is_suppressed_) return false; |
- SuppressDebug while_loading(this); |
- |
- // Disable breakpoints and interrupts while compiling and running the |
- // debugger scripts including the context creation code. |
- DisableBreak disable(this, true); |
- PostponeInterruptsScope postpone(isolate_); |
- |
- // Create the debugger context. |
- HandleScope scope(isolate_); |
- ExtensionConfiguration no_extensions; |
- Handle<Context> context = isolate_->bootstrapper()->CreateEnvironment( |
- MaybeHandle<JSGlobalProxy>(), v8::Local<ObjectTemplate>(), |
- &no_extensions); |
- |
- // Fail if no context could be created. |
- if (context.is_null()) return false; |
- |
- // Use the debugger context. |
- SaveContext save(isolate_); |
- isolate_->set_context(*context); |
- |
- // Expose the builtins object in the debugger context. |
- Handle<String> key = isolate_->factory()->InternalizeOneByteString( |
- STATIC_CHAR_VECTOR("builtins")); |
- Handle<GlobalObject> global = |
- Handle<GlobalObject>(context->global_object(), isolate_); |
- Handle<JSBuiltinsObject> builtin = |
- Handle<JSBuiltinsObject>(global->builtins(), isolate_); |
- RETURN_ON_EXCEPTION_VALUE( |
- isolate_, Object::SetProperty(global, key, builtin, SLOPPY), false); |
- |
- // Compile the JavaScript for the debugger in the debugger context. |
- bool caught_exception = |
- !CompileDebuggerScript(isolate_, Natives::GetIndex("mirror")) || |
- !CompileDebuggerScript(isolate_, Natives::GetIndex("debug")); |
- |
- if (FLAG_enable_liveedit) { |
- caught_exception = caught_exception || |
- !CompileDebuggerScript(isolate_, Natives::GetIndex("liveedit")); |
- } |
- // Check for caught exceptions. |
- if (caught_exception) return false; |
- |
- debug_context_ = Handle<Context>::cast( |
- isolate_->global_handles()->Create(*context)); |
- return true; |
-} |
- |
- |
-void Debug::Unload() { |
- ClearAllBreakPoints(); |
- ClearStepping(); |
- |
- // Return debugger is not loaded. |
- if (!is_loaded()) return; |
- |
- // Clear the script cache. |
- if (script_cache_ != NULL) { |
- delete script_cache_; |
- script_cache_ = NULL; |
- } |
- |
- // Clear debugger context global handle. |
- GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location()); |
- debug_context_ = Handle<Context>(); |
-} |
- |
- |
-void Debug::Break(Arguments args, JavaScriptFrame* frame) { |
- Heap* heap = isolate_->heap(); |
- HandleScope scope(isolate_); |
- DCHECK(args.length() == 0); |
- |
- // Initialize LiveEdit. |
- LiveEdit::InitializeThreadLocal(this); |
- |
- // Just continue if breaks are disabled or debugger cannot be loaded. |
- if (break_disabled()) return; |
- |
- // Enter the debugger. |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return; |
- |
- // Postpone interrupt during breakpoint processing. |
- PostponeInterruptsScope postpone(isolate_); |
- |
- // Get the debug info (create it if it does not exist). |
- Handle<SharedFunctionInfo> shared = |
- Handle<SharedFunctionInfo>(frame->function()->shared()); |
- Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
- |
- // Find the break point where execution has stopped. |
- // PC points to the instruction after the current one, possibly a break |
- // location as well. So the "- 1" to exclude it from the search. |
- Address call_pc = frame->pc() - 1; |
- BreakLocation break_location = |
- BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc); |
- |
- // Check whether step next reached a new statement. |
- if (!StepNextContinue(&break_location, frame)) { |
- // Decrease steps left if performing multiple steps. |
- if (thread_local_.step_count_ > 0) { |
- thread_local_.step_count_--; |
- } |
- } |
- |
- // If there is one or more real break points check whether any of these are |
- // triggered. |
- Handle<Object> break_points_hit(heap->undefined_value(), isolate_); |
- if (break_location.HasBreakPoint()) { |
- Handle<Object> break_point_objects = break_location.BreakPointObjects(); |
- break_points_hit = CheckBreakPoints(break_point_objects); |
- } |
- |
- // If step out is active skip everything until the frame where we need to step |
- // out to is reached, unless real breakpoint is hit. |
- if (StepOutActive() && |
- frame->fp() != thread_local_.step_out_fp_ && |
- break_points_hit->IsUndefined() ) { |
- // Step count should always be 0 for StepOut. |
- DCHECK(thread_local_.step_count_ == 0); |
- } else if (!break_points_hit->IsUndefined() || |
- (thread_local_.last_step_action_ != StepNone && |
- thread_local_.step_count_ == 0)) { |
- // Notify debugger if a real break point is triggered or if performing |
- // single stepping with no more steps to perform. Otherwise do another step. |
- |
- // Clear all current stepping setup. |
- ClearStepping(); |
- |
- if (thread_local_.queued_step_count_ > 0) { |
- // Perform queued steps |
- int step_count = thread_local_.queued_step_count_; |
- |
- // Clear queue |
- thread_local_.queued_step_count_ = 0; |
- |
- PrepareStep(StepNext, step_count, StackFrame::NO_ID); |
- } else { |
- // Notify the debug event listeners. |
- OnDebugBreak(break_points_hit, false); |
- } |
- } else if (thread_local_.last_step_action_ != StepNone) { |
- // Hold on to last step action as it is cleared by the call to |
- // ClearStepping. |
- StepAction step_action = thread_local_.last_step_action_; |
- int step_count = thread_local_.step_count_; |
- |
- // If StepNext goes deeper in code, StepOut until original frame |
- // and keep step count queued up in the meantime. |
- if (step_action == StepNext && frame->fp() < thread_local_.last_fp_) { |
- // Count frames until target frame |
- int count = 0; |
- JavaScriptFrameIterator it(isolate_); |
- while (!it.done() && it.frame()->fp() < thread_local_.last_fp_) { |
- count++; |
- it.Advance(); |
- } |
- |
- // Check that we indeed found the frame we are looking for. |
- CHECK(!it.done() && (it.frame()->fp() == thread_local_.last_fp_)); |
- if (step_count > 1) { |
- // Save old count and action to continue stepping after StepOut. |
- thread_local_.queued_step_count_ = step_count - 1; |
- } |
- |
- // Set up for StepOut to reach target frame. |
- step_action = StepOut; |
- step_count = count; |
- } |
- |
- // Clear all current stepping setup. |
- ClearStepping(); |
- |
- // Set up for the remaining steps. |
- PrepareStep(step_action, step_count, StackFrame::NO_ID); |
- } |
-} |
- |
- |
-// Check the break point objects for whether one or more are actually |
-// triggered. This function returns a JSArray with the break point objects |
-// which is triggered. |
-Handle<Object> Debug::CheckBreakPoints(Handle<Object> break_point_objects) { |
- Factory* factory = isolate_->factory(); |
- |
- // 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()); |
- if (break_point_objects->IsFixedArray()) { |
- Handle<FixedArray> array(FixedArray::cast(*break_point_objects)); |
- break_points_hit = factory->NewFixedArray(array->length()); |
- for (int i = 0; i < array->length(); i++) { |
- Handle<Object> o(array->get(i), isolate_); |
- if (CheckBreakPoint(o)) { |
- break_points_hit->set(break_points_hit_count++, *o); |
- } |
- } |
- } else { |
- break_points_hit = factory->NewFixedArray(1); |
- if (CheckBreakPoint(break_point_objects)) { |
- break_points_hit->set(break_points_hit_count++, *break_point_objects); |
- } |
- } |
- |
- // Return undefined if no break points were triggered. |
- if (break_points_hit_count == 0) { |
- return factory->undefined_value(); |
- } |
- // Return break points hit as a JSArray. |
- Handle<JSArray> result = factory->NewJSArrayWithElements(break_points_hit); |
- result->set_length(Smi::FromInt(break_points_hit_count)); |
- return result; |
-} |
- |
- |
-// Check whether a single break point object is triggered. |
-bool Debug::CheckBreakPoint(Handle<Object> break_point_object) { |
- Factory* factory = isolate_->factory(); |
- HandleScope scope(isolate_); |
- |
- // Ignore check if break point object is not a JSObject. |
- if (!break_point_object->IsJSObject()) return true; |
- |
- // Get the function IsBreakPointTriggered (defined in debug-debugger.js). |
- Handle<String> is_break_point_triggered_string = |
- factory->InternalizeOneByteString( |
- STATIC_CHAR_VECTOR("IsBreakPointTriggered")); |
- Handle<GlobalObject> debug_global(debug_context()->global_object()); |
- Handle<JSFunction> check_break_point = |
- Handle<JSFunction>::cast(Object::GetProperty( |
- debug_global, is_break_point_triggered_string).ToHandleChecked()); |
- |
- // Get the break id as an object. |
- Handle<Object> break_id = factory->NewNumberFromInt(Debug::break_id()); |
- |
- // Call HandleBreakPointx. |
- Handle<Object> argv[] = { break_id, break_point_object }; |
- Handle<Object> result; |
- if (!Execution::TryCall(check_break_point, |
- isolate_->js_builtins_object(), |
- arraysize(argv), |
- argv).ToHandle(&result)) { |
- return false; |
- } |
- |
- // Return whether the break point is triggered. |
- return result->IsTrue(); |
-} |
- |
- |
-bool Debug::SetBreakPoint(Handle<JSFunction> function, |
- Handle<Object> break_point_object, |
- int* source_position) { |
- HandleScope scope(isolate_); |
- |
- // 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; |
- } |
- |
- Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
- // Source positions starts with zero. |
- DCHECK(*source_position >= 0); |
- |
- // Find the break point and change it. |
- BreakLocation location = BreakLocation::FromPosition( |
- debug_info, ALL_BREAK_LOCATIONS, *source_position, STATEMENT_ALIGNED); |
- *source_position = location.statement_position(); |
- location.SetBreakPoint(break_point_object); |
- |
- // At least one active break point now. |
- return debug_info->GetBreakPointCount() > 0; |
-} |
- |
- |
-bool Debug::SetBreakPointForScript(Handle<Script> script, |
- Handle<Object> break_point_object, |
- int* source_position, |
- BreakPositionAlignment alignment) { |
- HandleScope scope(isolate_); |
- |
- // Obtain shared function info for the function. |
- Handle<Object> result = |
- FindSharedFunctionInfoInScript(script, *source_position); |
- if (result->IsUndefined()) return false; |
- |
- // 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; |
- } |
- |
- // 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(); |
- } |
- |
- 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, ALL_BREAK_LOCATIONS, position, alignment); |
- location.SetBreakPoint(break_point_object); |
- |
- position = (alignment == STATEMENT_ALIGNED) ? location.statement_position() |
- : location.position(); |
- |
- *source_position = position + shared->start_position(); |
- |
- // At least one active break point now. |
- DCHECK(debug_info->GetBreakPointCount() > 0); |
- return true; |
-} |
- |
- |
-void Debug::ClearBreakPoint(Handle<Object> break_point_object) { |
- HandleScope scope(isolate_); |
- |
- DebugInfoListNode* node = debug_info_list_; |
- while (node != NULL) { |
- 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(); |
- |
- // Find the break point and clear it. |
- Address pc = debug_info->code()->entry() + |
- break_point_info->code_position()->value(); |
- |
- BreakLocation location = |
- BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, pc); |
- location.ClearBreakPoint(break_point_object); |
- |
- // If there are no more break points left remove the debug info for this |
- // function. |
- if (debug_info->GetBreakPointCount() == 0) { |
- RemoveDebugInfoAndClearFromShared(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 (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS); |
- !it.Done(); it.Next()) { |
- it.GetBreakLocation().ClearDebugBreak(); |
- } |
- } |
- // Remove all debug info. |
- while (debug_info_list_ != NULL) { |
- RemoveDebugInfoAndClearFromShared(debug_info_list_->debug_info()); |
- } |
-} |
- |
- |
-void Debug::FloodWithOneShot(Handle<JSFunction> function, |
- BreakLocatorType type) { |
- // 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. |
- Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
- for (BreakLocation::Iterator it(debug_info, type); !it.Done(); it.Next()) { |
- it.GetBreakLocation().SetOneShot(); |
- } |
-} |
- |
- |
-void Debug::FloodBoundFunctionWithOneShot(Handle<JSFunction> function) { |
- Handle<FixedArray> new_bindings(function->function_bindings()); |
- Handle<Object> bindee(new_bindings->get(JSFunction::kBoundFunctionIndex), |
- isolate_); |
- |
- if (!bindee.is_null() && bindee->IsJSFunction()) { |
- Handle<JSFunction> bindee_function(JSFunction::cast(*bindee)); |
- FloodWithOneShotGeneric(bindee_function); |
- } |
-} |
- |
- |
-void Debug::FloodDefaultConstructorWithOneShot(Handle<JSFunction> function) { |
- DCHECK(function->shared()->is_default_constructor()); |
- // Instead of stepping into the function we directly step into the super class |
- // constructor. |
- Isolate* isolate = function->GetIsolate(); |
- PrototypeIterator iter(isolate, function); |
- Handle<Object> proto = PrototypeIterator::GetCurrent(iter); |
- if (!proto->IsJSFunction()) return; // Object.prototype |
- Handle<JSFunction> function_proto = Handle<JSFunction>::cast(proto); |
- FloodWithOneShotGeneric(function_proto); |
-} |
- |
- |
-void Debug::FloodWithOneShotGeneric(Handle<JSFunction> function, |
- Handle<Object> holder) { |
- if (function->shared()->bound()) { |
- FloodBoundFunctionWithOneShot(function); |
- } else if (function->shared()->is_default_constructor()) { |
- FloodDefaultConstructorWithOneShot(function); |
- } else { |
- Isolate* isolate = function->GetIsolate(); |
- // Don't allow step into functions in the native context. |
- if (function->shared()->code() == |
- isolate->builtins()->builtin(Builtins::kFunctionApply) || |
- function->shared()->code() == |
- isolate->builtins()->builtin(Builtins::kFunctionCall)) { |
- // Handle function.apply and function.call separately to flood the |
- // function to be called and not the code for Builtins::FunctionApply or |
- // Builtins::FunctionCall. The receiver of call/apply is the target |
- // function. |
- if (!holder.is_null() && holder->IsJSFunction()) { |
- Handle<JSFunction> js_function = Handle<JSFunction>::cast(holder); |
- FloodWithOneShotGeneric(js_function); |
- } |
- } else { |
- FloodWithOneShot(function); |
- } |
- } |
-} |
- |
- |
-void Debug::FloodHandlerWithOneShot() { |
- // Iterate through the JavaScript stack looking for handlers. |
- StackFrame::Id id = break_frame_id(); |
- if (id == StackFrame::NO_ID) { |
- // If there is no JavaScript stack don't do anything. |
- return; |
- } |
- for (JavaScriptFrameIterator it(isolate_, id); !it.done(); it.Advance()) { |
- JavaScriptFrame* frame = it.frame(); |
- int stack_slots = 0; // The computed stack slot count is not used. |
- if (frame->LookupExceptionHandlerInTable(&stack_slots, NULL) > 0) { |
- // Flood the function with the catch/finally block with break points. |
- FloodWithOneShot(Handle<JSFunction>(frame->function())); |
- return; |
- } |
- } |
-} |
- |
- |
-void Debug::ChangeBreakOnException(ExceptionBreakType type, bool enable) { |
- if (type == BreakUncaughtException) { |
- break_on_uncaught_exception_ = enable; |
- } else { |
- break_on_exception_ = enable; |
- } |
-} |
- |
- |
-bool Debug::IsBreakOnException(ExceptionBreakType type) { |
- if (type == BreakUncaughtException) { |
- return break_on_uncaught_exception_; |
- } else { |
- return break_on_exception_; |
- } |
-} |
- |
- |
-FrameSummary GetFirstFrameSummary(JavaScriptFrame* frame) { |
- List<FrameSummary> frames(FLAG_max_inlining_levels + 1); |
- frame->Summarize(&frames); |
- return frames.first(); |
-} |
- |
- |
-void Debug::PrepareStep(StepAction step_action, |
- int step_count, |
- StackFrame::Id frame_id) { |
- HandleScope scope(isolate_); |
- |
- DCHECK(in_debug_scope()); |
- |
- // Remember this step action and count. |
- thread_local_.last_step_action_ = step_action; |
- if (step_action == StepOut) { |
- // For step out target frame will be found on the stack so there is no need |
- // to set step counter for it. It's expected to always be 0 for StepOut. |
- thread_local_.step_count_ = 0; |
- } else { |
- thread_local_.step_count_ = step_count; |
- } |
- |
- // Get the frame where the execution has stopped and skip the debug frame if |
- // any. The debug frame will only be present if execution was stopped due to |
- // hitting a break point. In other situations (e.g. unhandled exception) the |
- // debug frame is not present. |
- StackFrame::Id id = break_frame_id(); |
- if (id == StackFrame::NO_ID) { |
- // If there is no JavaScript stack don't do anything. |
- return; |
- } |
- if (frame_id != StackFrame::NO_ID) { |
- id = frame_id; |
- } |
- JavaScriptFrameIterator frames_it(isolate_, id); |
- JavaScriptFrame* frame = frames_it.frame(); |
- |
- // First of all ensure there is one-shot break points in the top handler |
- // if any. |
- FloodHandlerWithOneShot(); |
- |
- // 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 (!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 = frames_it.frame()->function(); |
- FloodWithOneShot(Handle<JSFunction>(function)); |
- return; |
- } |
- |
- // Get the debug info (create it if it does not exist). |
- FrameSummary summary = GetFirstFrameSummary(frame); |
- Handle<JSFunction> function(summary.function()); |
- Handle<SharedFunctionInfo> shared(function->shared()); |
- if (!EnsureDebugInfo(shared, function)) { |
- // Return if ensuring debug info failed. |
- return; |
- } |
- |
- Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
- // Refresh frame summary if the code has been recompiled for debugging. |
- if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame); |
- |
- // PC points to the instruction after the current one, possibly a break |
- // location as well. So the "- 1" to exclude it from the search. |
- Address call_pc = summary.pc() - 1; |
- BreakLocation location = |
- BreakLocation::FromAddress(debug_info, ALL_BREAK_LOCATIONS, call_pc); |
- |
- // If this is the last break code target step out is the only possibility. |
- if (location.IsReturn() || step_action == StepOut) { |
- if (step_action == StepOut) { |
- // Skip step_count frames starting with the current one. |
- while (step_count-- > 0 && !frames_it.done()) { |
- frames_it.Advance(); |
- } |
- } else { |
- DCHECK(location.IsReturn()); |
- frames_it.Advance(); |
- } |
- // Skip native and extension functions on the stack. |
- while (!frames_it.done() && |
- !frames_it.frame()->function()->IsSubjectToDebugging()) { |
- frames_it.Advance(); |
- } |
- // Step out: If there is a JavaScript caller frame, we need to |
- // flood it with breakpoints. |
- if (!frames_it.done()) { |
- // Fill the function to return to with one-shot break points. |
- JSFunction* function = frames_it.frame()->function(); |
- FloodWithOneShot(Handle<JSFunction>(function)); |
- // Set target frame pointer. |
- ActivateStepOut(frames_it.frame()); |
- } |
- return; |
- } |
- |
- if (step_action != StepNext && step_action != StepMin) { |
- // If there's restarter frame on top of the stack, just get the pointer |
- // to function which is going to be restarted. |
- if (thread_local_.restarter_frame_function_pointer_ != NULL) { |
- Handle<JSFunction> restarted_function( |
- JSFunction::cast(*thread_local_.restarter_frame_function_pointer_)); |
- FloodWithOneShot(restarted_function); |
- } else if (location.IsCall()) { |
- // Find target function on the expression stack. |
- // Expression stack looks like this (top to bottom): |
- // argN |
- // ... |
- // arg0 |
- // Receiver |
- // Function to call |
- int num_expressions_without_args = |
- frame->ComputeExpressionsCount() - location.CallArgumentsCount(); |
- DCHECK(num_expressions_without_args >= 2); |
- Object* fun = frame->GetExpression(num_expressions_without_args - 2); |
- |
- // Flood the actual target of call/apply. |
- if (fun->IsJSFunction()) { |
- Isolate* isolate = JSFunction::cast(fun)->GetIsolate(); |
- Code* apply = isolate->builtins()->builtin(Builtins::kFunctionApply); |
- Code* call = isolate->builtins()->builtin(Builtins::kFunctionCall); |
- // Find target function on the expression stack for expression like |
- // Function.call.call...apply(...) |
- int i = 1; |
- while (fun->IsJSFunction()) { |
- Code* code = JSFunction::cast(fun)->shared()->code(); |
- if (code != apply && code != call) break; |
- DCHECK(num_expressions_without_args >= i); |
- fun = frame->GetExpression(num_expressions_without_args - i); |
- i--; |
- } |
- } |
- |
- if (fun->IsJSFunction()) { |
- Handle<JSFunction> js_function(JSFunction::cast(fun)); |
- FloodWithOneShotGeneric(js_function); |
- } |
- } |
- |
- ActivateStepIn(frame); |
- } |
- |
- // Fill the current function with one-shot break points even for step in on |
- // a call target as the function called might be a native function for |
- // which step in will not stop. It also prepares for stepping in |
- // getters/setters. |
- // If we are stepping into another frame, only fill calls and returns. |
- FloodWithOneShot(function, step_action == StepFrame ? CALLS_AND_RETURNS |
- : ALL_BREAK_LOCATIONS); |
- |
- // Remember source position and frame to handle step next. |
- thread_local_.last_statement_position_ = |
- debug_info->code()->SourceStatementPosition(summary.pc()); |
- thread_local_.last_fp_ = frame->UnpaddedFP(); |
-} |
- |
- |
-// Check whether the current debug break should be reported to the debugger. It |
-// is used to have step next and step in only report break back to the debugger |
-// if on a different frame or in a different statement. In some situations |
-// there will be several break points in the same statement when the code is |
-// flooded with one-shot break points. This function helps to perform several |
-// steps before reporting break back to the debugger. |
-bool Debug::StepNextContinue(BreakLocation* break_location, |
- JavaScriptFrame* frame) { |
- // StepNext and StepOut shouldn't bring us deeper in code, so last frame |
- // shouldn't be a parent of current frame. |
- StepAction step_action = thread_local_.last_step_action_; |
- |
- if (step_action == StepNext || step_action == StepOut) { |
- if (frame->fp() < thread_local_.last_fp_) return true; |
- } |
- |
- // We stepped into a new frame if the frame pointer changed. |
- if (step_action == StepFrame) { |
- return frame->UnpaddedFP() == thread_local_.last_fp_; |
- } |
- |
- // If the step last action was step next or step in make sure that a new |
- // statement is hit. |
- if (step_action == StepNext || step_action == StepIn) { |
- // Never continue if returning from function. |
- if (break_location->IsReturn()) return false; |
- |
- // Continue if we are still on the same frame and in the same statement. |
- int current_statement_position = |
- break_location->code()->SourceStatementPosition(frame->pc()); |
- return thread_local_.last_fp_ == frame->UnpaddedFP() && |
- thread_local_.last_statement_position_ == current_statement_position; |
- } |
- |
- // No step next action - don't continue. |
- return false; |
-} |
- |
- |
-// Check whether the code object at the specified address is a debug break code |
-// object. |
-bool Debug::IsDebugBreak(Address addr) { |
- Code* code = Code::GetCodeFromTargetAddress(addr); |
- return code->is_debug_stub(); |
-} |
- |
- |
-// 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); |
- } |
- Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
- if (debug_info->GetBreakPointCount() == 0) { |
- return Handle<Object>(heap->undefined_value(), isolate); |
- } |
- 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()) { |
- 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 = break_point_info->statement_position(); |
- break; |
- case BREAK_POSITION_ALIGNED: |
- position = break_point_info->source_position(); |
- break; |
- } |
- for (int j = 0; j < break_points; ++j) locations->set(count++, position); |
- } |
- } |
- return locations; |
-} |
- |
- |
-// Handle stepping into a function. |
-void Debug::HandleStepIn(Handle<Object> function_obj, bool is_constructor) { |
- // Flood getter/setter if we either step in or step to another frame. |
- bool step_frame = thread_local_.last_step_action_ == StepFrame; |
- if (!StepInActive() && !step_frame) return; |
- if (!function_obj->IsJSFunction()) return; |
- Handle<JSFunction> function = Handle<JSFunction>::cast(function_obj); |
- Isolate* isolate = function->GetIsolate(); |
- |
- StackFrameIterator it(isolate); |
- it.Advance(); |
- // For constructor functions skip another frame. |
- if (is_constructor) { |
- DCHECK(it.frame()->is_construct()); |
- it.Advance(); |
- } |
- Address fp = it.frame()->fp(); |
- |
- // Flood the function with one-shot break points if it is called from where |
- // step into was requested, or when stepping into a new frame. |
- if (fp == thread_local_.step_into_fp_ || step_frame) { |
- FloodWithOneShotGeneric(function, Handle<Object>()); |
- } |
-} |
- |
- |
-void Debug::ClearStepping() { |
- // Clear the various stepping setup. |
- ClearOneShot(); |
- ClearStepIn(); |
- ClearStepOut(); |
- ClearStepNext(); |
- |
- // Clear multiple step counter. |
- thread_local_.step_count_ = 0; |
-} |
- |
- |
-// Clears all the one-shot break points that are currently set. Normally this |
-// function is called each time a break point is hit as one shot break points |
-// are used to support stepping. |
-void Debug::ClearOneShot() { |
- // The current implementation just runs through all the breakpoints. When the |
- // last break point for a function is removed that function is automatically |
- // removed from the list. |
- for (DebugInfoListNode* node = debug_info_list_; node != NULL; |
- node = node->next()) { |
- for (BreakLocation::Iterator it(node->debug_info(), ALL_BREAK_LOCATIONS); |
- !it.Done(); it.Next()) { |
- it.GetBreakLocation().ClearOneShot(); |
- } |
- } |
-} |
- |
- |
-void Debug::ActivateStepIn(StackFrame* frame) { |
- DCHECK(!StepOutActive()); |
- thread_local_.step_into_fp_ = frame->UnpaddedFP(); |
-} |
- |
- |
-void Debug::ClearStepIn() { |
- thread_local_.step_into_fp_ = 0; |
-} |
- |
- |
-void Debug::ActivateStepOut(StackFrame* frame) { |
- DCHECK(!StepInActive()); |
- thread_local_.step_out_fp_ = frame->UnpaddedFP(); |
-} |
- |
- |
-void Debug::ClearStepOut() { |
- thread_local_.step_out_fp_ = 0; |
-} |
- |
- |
-void Debug::ClearStepNext() { |
- thread_local_.last_step_action_ = StepNone; |
- thread_local_.last_statement_position_ = RelocInfo::kNoPosition; |
- thread_local_.last_fp_ = 0; |
-} |
- |
- |
-bool MatchingCodeTargets(Code* target1, Code* target2) { |
- if (target1 == target2) return true; |
- if (target1->kind() != target2->kind()) return false; |
- return target1->is_handler() || target1->is_inline_cache_stub(); |
-} |
- |
- |
-// Count the number of calls before the current frame PC to find the |
-// corresponding PC in the newly recompiled code. |
-static Address ComputeNewPcForRedirect(Code* new_code, Code* old_code, |
- Address old_pc) { |
- DCHECK_EQ(old_code->kind(), Code::FUNCTION); |
- DCHECK_EQ(new_code->kind(), Code::FUNCTION); |
- DCHECK(new_code->has_debug_break_slots()); |
- static const int mask = RelocInfo::kCodeTargetMask; |
- |
- // Find the target of the current call. |
- Code* target = NULL; |
- intptr_t delta = 0; |
- for (RelocIterator it(old_code, mask); !it.done(); it.next()) { |
- RelocInfo* rinfo = it.rinfo(); |
- Address current_pc = rinfo->pc(); |
- // The frame PC is behind the call instruction by the call instruction size. |
- if (current_pc > old_pc) break; |
- delta = old_pc - current_pc; |
- target = Code::GetCodeFromTargetAddress(rinfo->target_address()); |
- } |
- |
- // Count the number of calls to the same target before the current call. |
- int index = 0; |
- for (RelocIterator it(old_code, mask); !it.done(); it.next()) { |
- RelocInfo* rinfo = it.rinfo(); |
- Address current_pc = rinfo->pc(); |
- if (current_pc > old_pc) break; |
- Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address()); |
- if (MatchingCodeTargets(target, current)) index++; |
- } |
- |
- DCHECK(index > 0); |
- |
- // Repeat the count on the new code to find corresponding call. |
- for (RelocIterator it(new_code, mask); !it.done(); it.next()) { |
- RelocInfo* rinfo = it.rinfo(); |
- Code* current = Code::GetCodeFromTargetAddress(rinfo->target_address()); |
- if (MatchingCodeTargets(target, current)) index--; |
- if (index == 0) return rinfo->pc() + delta; |
- } |
- |
- UNREACHABLE(); |
- return NULL; |
-} |
- |
- |
-// Count the number of continuations at which the current pc offset is at. |
-static int ComputeContinuationIndexFromPcOffset(Code* code, int pc_offset) { |
- DCHECK_EQ(code->kind(), Code::FUNCTION); |
- Address pc = code->instruction_start() + pc_offset; |
- int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION); |
- int index = 0; |
- for (RelocIterator it(code, mask); !it.done(); it.next()) { |
- index++; |
- RelocInfo* rinfo = it.rinfo(); |
- Address current_pc = rinfo->pc(); |
- if (current_pc == pc) break; |
- DCHECK(current_pc < pc); |
- } |
- return index; |
-} |
- |
- |
-// Find the pc offset for the given continuation index. |
-static int ComputePcOffsetFromContinuationIndex(Code* code, int index) { |
- DCHECK_EQ(code->kind(), Code::FUNCTION); |
- DCHECK(code->has_debug_break_slots()); |
- int mask = RelocInfo::ModeMask(RelocInfo::GENERATOR_CONTINUATION); |
- RelocIterator it(code, mask); |
- for (int i = 1; i < index; i++) it.next(); |
- return static_cast<int>(it.rinfo()->pc() - code->instruction_start()); |
-} |
- |
- |
-class RedirectActiveFunctions : public ThreadVisitor { |
- public: |
- explicit RedirectActiveFunctions(SharedFunctionInfo* shared) |
- : shared_(shared) { |
- DCHECK(shared->HasDebugCode()); |
- } |
- |
- void VisitThread(Isolate* isolate, ThreadLocalTop* top) { |
- for (JavaScriptFrameIterator it(isolate, top); !it.done(); it.Advance()) { |
- JavaScriptFrame* frame = it.frame(); |
- JSFunction* function = frame->function(); |
- if (frame->is_optimized()) continue; |
- if (!function->Inlines(shared_)) continue; |
- |
- Code* frame_code = frame->LookupCode(); |
- DCHECK(frame_code->kind() == Code::FUNCTION); |
- if (frame_code->has_debug_break_slots()) continue; |
- |
- Code* new_code = function->shared()->code(); |
- Address old_pc = frame->pc(); |
- Address new_pc = ComputeNewPcForRedirect(new_code, frame_code, old_pc); |
- |
- if (FLAG_trace_deopt) { |
- PrintF("Replacing pc for debugging: %08" V8PRIxPTR " => %08" V8PRIxPTR |
- "\n", |
- reinterpret_cast<intptr_t>(old_pc), |
- reinterpret_cast<intptr_t>(new_pc)); |
- } |
- |
- if (FLAG_enable_embedded_constant_pool) { |
- // Update constant pool pointer for new code. |
- frame->set_constant_pool(new_code->constant_pool()); |
- } |
- |
- // Patch the return address to return into the code with |
- // debug break slots. |
- frame->set_pc(new_pc); |
- } |
- } |
- |
- private: |
- SharedFunctionInfo* shared_; |
- DisallowHeapAllocation no_gc_; |
-}; |
- |
- |
-bool Debug::PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared) { |
- DCHECK(shared->is_compiled()); |
- |
- if (isolate_->concurrent_recompilation_enabled()) { |
- isolate_->optimizing_compile_dispatcher()->Flush(); |
- } |
- |
- List<Handle<JSFunction> > functions; |
- List<Handle<JSGeneratorObject> > suspended_generators; |
- |
- if (!shared->optimized_code_map()->IsSmi()) { |
- shared->ClearOptimizedCodeMap(); |
- } |
- |
- // Make sure we abort incremental marking. |
- isolate_->heap()->CollectAllGarbage(Heap::kMakeHeapIterableMask, |
- "prepare for break points"); |
- |
- { |
- HeapIterator iterator(isolate_->heap()); |
- HeapObject* obj; |
- bool include_generators = shared->is_generator(); |
- |
- while ((obj = iterator.next())) { |
- if (obj->IsJSFunction()) { |
- JSFunction* function = JSFunction::cast(obj); |
- if (!function->Inlines(*shared)) continue; |
- if (function->code()->kind() == Code::OPTIMIZED_FUNCTION) { |
- Deoptimizer::DeoptimizeFunction(function); |
- } |
- if (function->shared() == *shared) functions.Add(handle(function)); |
- } else if (include_generators && obj->IsJSGeneratorObject()) { |
- JSGeneratorObject* generator_obj = JSGeneratorObject::cast(obj); |
- if (!generator_obj->is_suspended()) continue; |
- JSFunction* function = generator_obj->function(); |
- if (!function->Inlines(*shared)) continue; |
- int pc_offset = generator_obj->continuation(); |
- int index = |
- ComputeContinuationIndexFromPcOffset(function->code(), pc_offset); |
- generator_obj->set_continuation(index); |
- suspended_generators.Add(handle(generator_obj)); |
- } |
- } |
- } |
- |
- if (!shared->HasDebugCode()) { |
- DCHECK(functions.length() > 0); |
- if (!Compiler::CompileDebugCode(functions.first())) return false; |
- } |
- |
- for (Handle<JSFunction> const function : functions) { |
- function->ReplaceCode(shared->code()); |
- } |
- |
- for (Handle<JSGeneratorObject> const generator_obj : suspended_generators) { |
- int index = generator_obj->continuation(); |
- int pc_offset = ComputePcOffsetFromContinuationIndex(shared->code(), index); |
- generator_obj->set_continuation(pc_offset); |
- } |
- |
- // Update PCs on the stack to point to recompiled code. |
- RedirectActiveFunctions redirect_visitor(*shared); |
- redirect_visitor.VisitThread(isolate_, isolate_->thread_local_top()); |
- isolate_->thread_manager()->IterateArchivedThreads(&redirect_visitor); |
- |
- return true; |
-} |
- |
- |
-class SharedFunctionInfoFinder { |
- public: |
- explicit SharedFunctionInfoFinder(int target_position) |
- : current_candidate_(NULL), |
- current_candidate_closure_(NULL), |
- current_start_position_(RelocInfo::kNoPosition), |
- target_position_(target_position) {} |
- |
- void NewCandidate(SharedFunctionInfo* shared, JSFunction* closure = NULL) { |
- int start_position = shared->function_token_position(); |
- if (start_position == RelocInfo::kNoPosition) { |
- start_position = shared->start_position(); |
- } |
- |
- if (start_position > target_position_) return; |
- if (target_position_ > shared->end_position()) return; |
- |
- if (current_candidate_ != NULL) { |
- if (current_start_position_ == start_position && |
- shared->end_position() == current_candidate_->end_position()) { |
- // If a top-level function contains only one function |
- // declaration the source for the top-level and the function |
- // is the same. In that case prefer the non top-level function. |
- if (shared->is_toplevel()) return; |
- } else if (start_position < current_start_position_ || |
- current_candidate_->end_position() < shared->end_position()) { |
- return; |
- } |
- } |
- |
- current_start_position_ = start_position; |
- current_candidate_ = shared; |
- current_candidate_closure_ = closure; |
- } |
- |
- SharedFunctionInfo* Result() { return current_candidate_; } |
- |
- JSFunction* ResultClosure() { return current_candidate_closure_; } |
- |
- private: |
- SharedFunctionInfo* current_candidate_; |
- JSFunction* current_candidate_closure_; |
- int current_start_position_; |
- int target_position_; |
- DisallowHeapAllocation no_gc_; |
-}; |
- |
- |
-// We need to find a SFI for a literal that may not yet have been compiled yet, |
-// and there may not be a JSFunction referencing it. Find the SFI closest to |
-// the given position, compile it to reveal possible inner SFIs and repeat. |
-// While we are at this, also ensure code with debug break slots so that we do |
-// not have to compile a SFI without JSFunction, which is paifu for those that |
-// cannot be compiled without context (need to find outer compilable SFI etc.) |
-Handle<Object> Debug::FindSharedFunctionInfoInScript(Handle<Script> script, |
- int position) { |
- while (true) { |
- // Go through all shared function infos associated with this script to |
- // find the inner most function containing this position. |
- if (!script->shared_function_infos()->IsWeakFixedArray()) break; |
- WeakFixedArray* array = |
- WeakFixedArray::cast(script->shared_function_infos()); |
- |
- SharedFunctionInfo* shared; |
- { |
- SharedFunctionInfoFinder finder(position); |
- for (int i = 0; i < array->Length(); i++) { |
- Object* item = array->Get(i); |
- if (!item->IsSharedFunctionInfo()) continue; |
- finder.NewCandidate(SharedFunctionInfo::cast(item)); |
- } |
- shared = finder.Result(); |
- if (shared == NULL) break; |
- // We found it if it's already compiled and has debug code. |
- if (shared->HasDebugCode()) return handle(shared); |
- } |
- // If not, compile to reveal inner functions, if possible. |
- if (shared->allows_lazy_compilation_without_context()) { |
- HandleScope scope(isolate_); |
- if (!Compiler::CompileDebugCode(handle(shared))) break; |
- continue; |
- } |
- |
- // If not possible, comb the heap for the best suitable compile target. |
- JSFunction* closure; |
- { |
- HeapIterator it(isolate_->heap()); |
- SharedFunctionInfoFinder finder(position); |
- while (HeapObject* object = it.next()) { |
- JSFunction* candidate_closure = NULL; |
- SharedFunctionInfo* candidate = NULL; |
- if (object->IsJSFunction()) { |
- candidate_closure = JSFunction::cast(object); |
- candidate = candidate_closure->shared(); |
- } else if (object->IsSharedFunctionInfo()) { |
- candidate = SharedFunctionInfo::cast(object); |
- if (!candidate->allows_lazy_compilation_without_context()) continue; |
- } else { |
- continue; |
- } |
- if (candidate->script() == *script) { |
- finder.NewCandidate(candidate, candidate_closure); |
- } |
- } |
- closure = finder.ResultClosure(); |
- shared = finder.Result(); |
- } |
- HandleScope scope(isolate_); |
- if (closure == NULL) { |
- if (!Compiler::CompileDebugCode(handle(shared))) break; |
- } else { |
- if (!Compiler::CompileDebugCode(handle(closure))) break; |
- } |
- } |
- return isolate_->factory()->undefined_value(); |
-} |
- |
- |
-// Ensures the debug information is present for shared. |
-bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared, |
- Handle<JSFunction> function) { |
- if (!shared->IsSubjectToDebugging()) return false; |
- |
- // 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::EnsureCompiled(function, CLEAR_EXCEPTION)) { |
- return false; |
- } |
- |
- if (!PrepareFunctionForBreakPoints(shared)) return false; |
- |
- // Make sure IC state is clean. This is so that we correctly flood |
- // accessor pairs when stepping in. |
- shared->code()->ClearInlineCaches(); |
- shared->feedback_vector()->ClearICSlots(*shared); |
- |
- // Create the debug info object. |
- DCHECK(shared->HasDebugCode()); |
- Handle<DebugInfo> debug_info = isolate_->factory()->NewDebugInfo(shared); |
- |
- // Add debug info to the list. |
- DebugInfoListNode* node = new DebugInfoListNode(*debug_info); |
- node->set_next(debug_info_list_); |
- debug_info_list_ = node; |
- |
- return true; |
-} |
- |
- |
-void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) { |
- HandleScope scope(isolate_); |
- Handle<SharedFunctionInfo> shared(debug_info->shared()); |
- |
- DCHECK_NOT_NULL(debug_info_list_); |
- // Run through the debug info objects to find this one and remove it. |
- DebugInfoListNode* prev = NULL; |
- DebugInfoListNode* current = debug_info_list_; |
- while (current != NULL) { |
- if (current->debug_info().is_identical_to(debug_info)) { |
- // Unlink from list. If prev is NULL we are looking at the first element. |
- if (prev == NULL) { |
- debug_info_list_ = current->next(); |
- } else { |
- prev->set_next(current->next()); |
- } |
- delete current; |
- shared->set_debug_info(isolate_->heap()->undefined_value()); |
- return; |
- } |
- // Move to next in list. |
- prev = current; |
- current = current->next(); |
- } |
- |
- UNREACHABLE(); |
-} |
- |
- |
-void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) { |
- after_break_target_ = NULL; |
- |
- if (LiveEdit::SetAfterBreakTarget(this)) return; // LiveEdit did the job. |
- |
- // Continue just after the slot. |
- after_break_target_ = frame->pc(); |
-} |
- |
- |
-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()); |
- |
- // With no debug info there are no break points, so we can't be at a return. |
- if (!shared->HasDebugInfo()) return false; |
- Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
- Handle<Code> code(debug_info->code()); |
-#ifdef DEBUG |
- // Get the code which is actually executing. |
- Handle<Code> frame_code(frame->LookupCode()); |
- DCHECK(frame_code.is_identical_to(code)); |
-#endif |
- |
- // Find the reloc info matching the start of the debug break slot. |
- Address slot_pc = frame->pc() - Assembler::kDebugBreakSlotLength; |
- int mask = RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT_AT_RETURN); |
- for (RelocIterator it(*code, mask); !it.done(); it.next()) { |
- if (it.rinfo()->pc() == slot_pc) return true; |
- } |
- return false; |
-} |
- |
- |
-void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id, |
- LiveEdit::FrameDropMode mode, |
- Object** restarter_frame_function_pointer) { |
- if (mode != LiveEdit::CURRENTLY_SET_MODE) { |
- thread_local_.frame_drop_mode_ = mode; |
- } |
- thread_local_.break_frame_id_ = new_break_frame_id; |
- thread_local_.restarter_frame_function_pointer_ = |
- restarter_frame_function_pointer; |
-} |
- |
- |
-bool Debug::IsDebugGlobal(GlobalObject* global) { |
- return is_loaded() && global == debug_context()->global_object(); |
-} |
- |
- |
-void Debug::ClearMirrorCache() { |
- PostponeInterruptsScope postpone(isolate_); |
- HandleScope scope(isolate_); |
- AssertDebugContext(); |
- Factory* factory = isolate_->factory(); |
- Handle<GlobalObject> global(isolate_->global_object()); |
- JSObject::SetProperty(global, |
- factory->NewStringFromAsciiChecked("next_handle_"), |
- handle(Smi::FromInt(0), isolate_), SLOPPY).Check(); |
- JSObject::SetProperty(global, |
- factory->NewStringFromAsciiChecked("mirror_cache_"), |
- factory->NewJSArray(0, FAST_ELEMENTS), SLOPPY).Check(); |
-} |
- |
- |
-Handle<FixedArray> Debug::GetLoadedScripts() { |
- // Create and fill the script cache when the loaded scripts is requested for |
- // the first time. |
- if (script_cache_ == NULL) script_cache_ = new ScriptCache(isolate_); |
- |
- // Perform GC to get unreferenced scripts evicted from the cache before |
- // returning the content. |
- isolate_->heap()->CollectAllGarbage(Heap::kNoGCFlags, |
- "Debug::GetLoadedScripts"); |
- |
- // Get the scripts from the cache. |
- return script_cache_->GetScripts(); |
-} |
- |
- |
-void Debug::GetStepinPositions(JavaScriptFrame* frame, StackFrame::Id frame_id, |
- List<int>* results_out) { |
- FrameSummary summary = GetFirstFrameSummary(frame); |
- |
- Handle<JSFunction> fun = Handle<JSFunction>(summary.function()); |
- Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>(fun->shared()); |
- |
- if (!EnsureDebugInfo(shared, fun)) return; |
- |
- Handle<DebugInfo> debug_info(shared->GetDebugInfo()); |
- // Refresh frame summary if the code has been recompiled for debugging. |
- if (shared->code() != *summary.code()) summary = GetFirstFrameSummary(frame); |
- |
- // Find range of break points starting from the break point where execution |
- // has stopped. |
- Address call_pc = summary.pc() - 1; |
- List<BreakLocation> locations; |
- BreakLocation::FromAddressSameStatement(debug_info, ALL_BREAK_LOCATIONS, |
- call_pc, &locations); |
- |
- for (BreakLocation location : locations) { |
- if (location.pc() <= summary.pc()) { |
- // The break point is near our pc. Could be a step-in possibility, |
- // that is currently taken by active debugger call. |
- if (break_frame_id() == StackFrame::NO_ID) { |
- continue; // We are not stepping. |
- } else { |
- JavaScriptFrameIterator frame_it(isolate_, break_frame_id()); |
- // If our frame is a top frame and we are stepping, we can do step-in |
- // at this place. |
- if (frame_it.frame()->id() != frame_id) continue; |
- } |
- } |
- if (location.IsStepInLocation()) results_out->Add(location.position()); |
- } |
-} |
- |
- |
-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(Smi::FromInt(offset)); |
- } |
-} |
- |
- |
-MaybeHandle<Object> Debug::MakeJSObject(const char* constructor_name, |
- int argc, |
- Handle<Object> argv[]) { |
- AssertDebugContext(); |
- // Create the execution state object. |
- Handle<GlobalObject> global(isolate_->global_object()); |
- Handle<Object> constructor = Object::GetProperty( |
- isolate_, global, constructor_name).ToHandleChecked(); |
- DCHECK(constructor->IsJSFunction()); |
- if (!constructor->IsJSFunction()) return MaybeHandle<Object>(); |
- // We do not handle interrupts here. In particular, termination interrupts. |
- PostponeInterruptsScope no_interrupts(isolate_); |
- return Execution::TryCall(Handle<JSFunction>::cast(constructor), |
- handle(debug_context()->global_proxy()), |
- argc, |
- argv); |
-} |
- |
- |
-MaybeHandle<Object> Debug::MakeExecutionState() { |
- // Create the execution state object. |
- Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()) }; |
- return MakeJSObject("MakeExecutionState", arraysize(argv), argv); |
-} |
- |
- |
-MaybeHandle<Object> Debug::MakeBreakEvent(Handle<Object> break_points_hit) { |
- // Create the new break event object. |
- Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()), |
- break_points_hit }; |
- return MakeJSObject("MakeBreakEvent", arraysize(argv), argv); |
-} |
- |
- |
-MaybeHandle<Object> Debug::MakeExceptionEvent(Handle<Object> exception, |
- bool uncaught, |
- Handle<Object> promise) { |
- // Create the new exception event object. |
- Handle<Object> argv[] = { isolate_->factory()->NewNumberFromInt(break_id()), |
- exception, |
- isolate_->factory()->ToBoolean(uncaught), |
- promise }; |
- return MakeJSObject("MakeExceptionEvent", arraysize(argv), argv); |
-} |
- |
- |
-MaybeHandle<Object> Debug::MakeCompileEvent(Handle<Script> script, |
- v8::DebugEvent type) { |
- // Create the compile event object. |
- Handle<Object> script_wrapper = Script::GetWrapper(script); |
- Handle<Object> argv[] = { script_wrapper, |
- isolate_->factory()->NewNumberFromInt(type) }; |
- return MakeJSObject("MakeCompileEvent", arraysize(argv), argv); |
-} |
- |
- |
-MaybeHandle<Object> Debug::MakePromiseEvent(Handle<JSObject> event_data) { |
- // Create the promise event object. |
- Handle<Object> argv[] = { event_data }; |
- return MakeJSObject("MakePromiseEvent", arraysize(argv), argv); |
-} |
- |
- |
-MaybeHandle<Object> Debug::MakeAsyncTaskEvent(Handle<JSObject> task_event) { |
- // Create the async task event object. |
- Handle<Object> argv[] = { task_event }; |
- return MakeJSObject("MakeAsyncTaskEvent", arraysize(argv), argv); |
-} |
- |
- |
-void Debug::OnThrow(Handle<Object> exception) { |
- if (in_debug_scope() || ignore_events()) return; |
- // Temporarily clear any scheduled_exception to allow evaluating |
- // JavaScript from the debug event handler. |
- HandleScope scope(isolate_); |
- Handle<Object> scheduled_exception; |
- if (isolate_->has_scheduled_exception()) { |
- scheduled_exception = handle(isolate_->scheduled_exception(), isolate_); |
- isolate_->clear_scheduled_exception(); |
- } |
- OnException(exception, isolate_->GetPromiseOnStackOnThrow()); |
- if (!scheduled_exception.is_null()) { |
- isolate_->thread_local_top()->scheduled_exception_ = *scheduled_exception; |
- } |
-} |
- |
- |
-void Debug::OnPromiseReject(Handle<JSObject> promise, Handle<Object> value) { |
- if (in_debug_scope() || ignore_events()) return; |
- 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()) { |
- OnException(value, promise); |
- } |
-} |
- |
- |
-MaybeHandle<Object> Debug::PromiseHasUserDefinedRejectHandler( |
- Handle<JSObject> promise) { |
- Handle<JSFunction> fun = Handle<JSFunction>::cast( |
- JSReceiver::GetDataProperty(isolate_->js_builtins_object(), |
- isolate_->factory()->NewStringFromStaticChars( |
- "$promiseHasUserDefinedRejectHandler"))); |
- return Execution::Call(isolate_, fun, promise, 0, NULL); |
-} |
- |
- |
-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(); |
- bool uncaught = (catch_type == Isolate::NOT_CAUGHT); |
- if (promise->IsJSObject()) { |
- Handle<JSObject> jspromise = Handle<JSObject>::cast(promise); |
- // Mark the promise as already having triggered a message. |
- Handle<Symbol> key = isolate_->factory()->promise_debug_marker_symbol(); |
- JSObject::SetProperty(jspromise, key, key, STRICT).Assert(); |
- // Check whether the promise reject is considered an uncaught exception. |
- Handle<Object> has_reject_handler; |
- ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
- isolate_, has_reject_handler, |
- PromiseHasUserDefinedRejectHandler(jspromise), /* void */); |
- uncaught = has_reject_handler->IsFalse(); |
- } |
- // Bail out if exception breaks are not active |
- if (uncaught) { |
- // Uncaught exceptions are reported by either flags. |
- if (!(break_on_uncaught_exception_ || break_on_exception_)) return; |
- } else { |
- // Caught exceptions are reported is activated. |
- if (!break_on_exception_) return; |
- } |
- |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return; |
- |
- // Clear all current stepping setup. |
- ClearStepping(); |
- |
- // Create the event data object. |
- Handle<Object> event_data; |
- // Bail out and don't call debugger if exception. |
- if (!MakeExceptionEvent( |
- exception, uncaught, promise).ToHandle(&event_data)) { |
- return; |
- } |
- |
- // Process debug event. |
- ProcessDebugEvent(v8::Exception, Handle<JSObject>::cast(event_data), false); |
- // Return to continue execution from where the exception was thrown. |
-} |
- |
- |
-void Debug::OnCompileError(Handle<Script> script) { |
- if (ignore_events()) return; |
- |
- if (in_debug_scope()) { |
- ProcessCompileEventInDebugScope(v8::CompileError, script); |
- return; |
- } |
- |
- HandleScope scope(isolate_); |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return; |
- |
- // Create the compile state object. |
- Handle<Object> event_data; |
- // Bail out and don't call debugger if exception. |
- if (!MakeCompileEvent(script, v8::CompileError).ToHandle(&event_data)) return; |
- |
- // Process debug event. |
- ProcessDebugEvent(v8::CompileError, Handle<JSObject>::cast(event_data), true); |
-} |
- |
- |
-void Debug::OnDebugBreak(Handle<Object> break_points_hit, |
- bool auto_continue) { |
- // The caller provided for DebugScope. |
- AssertDebugContext(); |
- // Bail out if there is no listener for this event |
- if (ignore_events()) return; |
- |
- HandleScope scope(isolate_); |
- // Create the event data object. |
- Handle<Object> event_data; |
- // Bail out and don't call debugger if exception. |
- if (!MakeBreakEvent(break_points_hit).ToHandle(&event_data)) return; |
- |
- // Process debug event. |
- ProcessDebugEvent(v8::Break, |
- Handle<JSObject>::cast(event_data), |
- auto_continue); |
-} |
- |
- |
-void Debug::OnBeforeCompile(Handle<Script> script) { |
- if (in_debug_scope() || ignore_events()) return; |
- |
- HandleScope scope(isolate_); |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return; |
- |
- // Create the event data object. |
- Handle<Object> event_data; |
- // Bail out and don't call debugger if exception. |
- if (!MakeCompileEvent(script, v8::BeforeCompile).ToHandle(&event_data)) |
- return; |
- |
- // Process debug event. |
- ProcessDebugEvent(v8::BeforeCompile, |
- Handle<JSObject>::cast(event_data), |
- true); |
-} |
- |
- |
-// Handle debugger actions when a new script is compiled. |
-void Debug::OnAfterCompile(Handle<Script> script) { |
- // Add the newly compiled script to the script cache. |
- if (script_cache_ != NULL) script_cache_->Add(script); |
- |
- if (ignore_events()) return; |
- |
- if (in_debug_scope()) { |
- ProcessCompileEventInDebugScope(v8::AfterCompile, script); |
- return; |
- } |
- |
- HandleScope scope(isolate_); |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return; |
- |
- // If debugging there might be script break points registered for this |
- // script. Make sure that these break points are set. |
- |
- // Get the function UpdateScriptBreakPoints (defined in debug-debugger.js). |
- Handle<String> update_script_break_points_string = |
- isolate_->factory()->InternalizeOneByteString( |
- STATIC_CHAR_VECTOR("UpdateScriptBreakPoints")); |
- Handle<GlobalObject> debug_global(debug_context()->global_object()); |
- Handle<Object> update_script_break_points = |
- Object::GetProperty( |
- debug_global, update_script_break_points_string).ToHandleChecked(); |
- if (!update_script_break_points->IsJSFunction()) { |
- return; |
- } |
- DCHECK(update_script_break_points->IsJSFunction()); |
- |
- // Wrap the script object in a proper JS object before passing it |
- // to JavaScript. |
- Handle<Object> wrapper = Script::GetWrapper(script); |
- |
- // Call UpdateScriptBreakPoints expect no exceptions. |
- Handle<Object> argv[] = { wrapper }; |
- if (Execution::TryCall(Handle<JSFunction>::cast(update_script_break_points), |
- isolate_->js_builtins_object(), |
- arraysize(argv), |
- argv).is_null()) { |
- return; |
- } |
- |
- // Create the compile state object. |
- Handle<Object> event_data; |
- // Bail out and don't call debugger if exception. |
- if (!MakeCompileEvent(script, v8::AfterCompile).ToHandle(&event_data)) return; |
- |
- // Process debug event. |
- ProcessDebugEvent(v8::AfterCompile, Handle<JSObject>::cast(event_data), true); |
-} |
- |
- |
-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; |
- |
- 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 (!MakeAsyncTaskEvent(data).ToHandle(&event_data)) return; |
- |
- // Process debug event. |
- ProcessDebugEvent(v8::AsyncTaskEvent, |
- Handle<JSObject>::cast(event_data), |
- true); |
-} |
- |
- |
-void Debug::ProcessDebugEvent(v8::DebugEvent event, |
- Handle<JSObject> event_data, |
- bool auto_continue) { |
- HandleScope scope(isolate_); |
- |
- // Create the execution state. |
- Handle<Object> exec_state; |
- // Bail out and don't call debugger if exception. |
- if (!MakeExecutionState().ToHandle(&exec_state)) return; |
- |
- // First notify the message handler if any. |
- if (message_handler_ != NULL) { |
- NotifyMessageHandler(event, |
- Handle<JSObject>::cast(exec_state), |
- event_data, |
- auto_continue); |
- } |
- // Notify registered debug event listener. This can be either a C or |
- // a JavaScript function. Don't call event listener for v8::Break |
- // here, if it's only a debug command -- they will be processed later. |
- if ((event != v8::Break || !auto_continue) && !event_listener_.is_null()) { |
- CallEventCallback(event, exec_state, event_data, NULL); |
- } |
-} |
- |
- |
-void Debug::CallEventCallback(v8::DebugEvent event, |
- Handle<Object> exec_state, |
- Handle<Object> event_data, |
- v8::Debug::ClientData* client_data) { |
- bool previous = in_debug_event_listener_; |
- in_debug_event_listener_ = true; |
- if (event_listener_->IsForeign()) { |
- // Invoke the C debug event listener. |
- v8::Debug::EventCallback callback = |
- FUNCTION_CAST<v8::Debug::EventCallback>( |
- Handle<Foreign>::cast(event_listener_)->foreign_address()); |
- EventDetailsImpl event_details(event, |
- Handle<JSObject>::cast(exec_state), |
- Handle<JSObject>::cast(event_data), |
- event_listener_data_, |
- client_data); |
- callback(event_details); |
- DCHECK(!isolate_->has_scheduled_exception()); |
- } else { |
- // Invoke the JavaScript debug event listener. |
- DCHECK(event_listener_->IsJSFunction()); |
- Handle<Object> argv[] = { Handle<Object>(Smi::FromInt(event), isolate_), |
- exec_state, |
- event_data, |
- event_listener_data_ }; |
- Handle<JSReceiver> global(isolate_->global_proxy()); |
- Execution::TryCall(Handle<JSFunction>::cast(event_listener_), |
- global, arraysize(argv), argv); |
- } |
- in_debug_event_listener_ = previous; |
-} |
- |
- |
-void Debug::ProcessCompileEventInDebugScope(v8::DebugEvent event, |
- Handle<Script> script) { |
- if (event_listener_.is_null()) return; |
- |
- SuppressDebug while_processing(this); |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return; |
- |
- Handle<Object> event_data; |
- // Bail out and don't call debugger if exception. |
- if (!MakeCompileEvent(script, event).ToHandle(&event_data)) return; |
- |
- // Create the execution state. |
- Handle<Object> exec_state; |
- // Bail out and don't call debugger if exception. |
- if (!MakeExecutionState().ToHandle(&exec_state)) return; |
- |
- CallEventCallback(event, exec_state, event_data, NULL); |
-} |
- |
- |
-Handle<Context> Debug::GetDebugContext() { |
- if (!is_loaded()) return Handle<Context>(); |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return Handle<Context>(); |
- // The global handle may be destroyed soon after. Return it reboxed. |
- return handle(*debug_context(), isolate_); |
-} |
- |
- |
-void Debug::NotifyMessageHandler(v8::DebugEvent event, |
- Handle<JSObject> exec_state, |
- Handle<JSObject> event_data, |
- bool auto_continue) { |
- // Prevent other interrupts from triggering, for example API callbacks, |
- // while dispatching message handler callbacks. |
- PostponeInterruptsScope no_interrupts(isolate_); |
- DCHECK(is_active_); |
- HandleScope scope(isolate_); |
- // Process the individual events. |
- bool sendEventMessage = false; |
- switch (event) { |
- case v8::Break: |
- sendEventMessage = !auto_continue; |
- break; |
- case v8::NewFunction: |
- case v8::BeforeCompile: |
- case v8::CompileError: |
- case v8::PromiseEvent: |
- case v8::AsyncTaskEvent: |
- break; |
- case v8::Exception: |
- case v8::AfterCompile: |
- sendEventMessage = true; |
- break; |
- } |
- |
- // The debug command interrupt flag might have been set when the command was |
- // added. It should be enough to clear the flag only once while we are in the |
- // debugger. |
- DCHECK(in_debug_scope()); |
- isolate_->stack_guard()->ClearDebugCommand(); |
- |
- // Notify the debugger that a debug event has occurred unless auto continue is |
- // active in which case no event is send. |
- if (sendEventMessage) { |
- MessageImpl message = MessageImpl::NewEvent( |
- event, |
- auto_continue, |
- Handle<JSObject>::cast(exec_state), |
- Handle<JSObject>::cast(event_data)); |
- InvokeMessageHandler(message); |
- } |
- |
- // If auto continue don't make the event cause a break, but process messages |
- // in the queue if any. For script collected events don't even process |
- // messages in the queue as the execution state might not be what is expected |
- // by the client. |
- if (auto_continue && !has_commands()) return; |
- |
- // DebugCommandProcessor goes here. |
- bool running = auto_continue; |
- |
- Handle<Object> cmd_processor_ctor = Object::GetProperty( |
- isolate_, exec_state, "debugCommandProcessor").ToHandleChecked(); |
- Handle<Object> ctor_args[] = { isolate_->factory()->ToBoolean(running) }; |
- Handle<Object> cmd_processor = Execution::Call( |
- isolate_, cmd_processor_ctor, exec_state, 1, ctor_args).ToHandleChecked(); |
- Handle<JSFunction> process_debug_request = Handle<JSFunction>::cast( |
- Object::GetProperty( |
- isolate_, cmd_processor, "processDebugRequest").ToHandleChecked()); |
- Handle<Object> is_running = Object::GetProperty( |
- isolate_, cmd_processor, "isRunning").ToHandleChecked(); |
- |
- // Process requests from the debugger. |
- do { |
- // Wait for new command in the queue. |
- command_received_.Wait(); |
- |
- // Get the command from the queue. |
- CommandMessage command = command_queue_.Get(); |
- isolate_->logger()->DebugTag( |
- "Got request from command queue, in interactive loop."); |
- if (!is_active()) { |
- // Delete command text and user data. |
- command.Dispose(); |
- return; |
- } |
- |
- Vector<const uc16> command_text( |
- const_cast<const uc16*>(command.text().start()), |
- command.text().length()); |
- Handle<String> request_text = isolate_->factory()->NewStringFromTwoByte( |
- command_text).ToHandleChecked(); |
- Handle<Object> request_args[] = { request_text }; |
- Handle<Object> answer_value; |
- Handle<String> answer; |
- MaybeHandle<Object> maybe_exception; |
- MaybeHandle<Object> maybe_result = |
- Execution::TryCall(process_debug_request, cmd_processor, 1, |
- request_args, &maybe_exception); |
- |
- if (maybe_result.ToHandle(&answer_value)) { |
- if (answer_value->IsUndefined()) { |
- answer = isolate_->factory()->empty_string(); |
- } else { |
- answer = Handle<String>::cast(answer_value); |
- } |
- |
- // Log the JSON request/response. |
- if (FLAG_trace_debug_json) { |
- PrintF("%s\n", request_text->ToCString().get()); |
- PrintF("%s\n", answer->ToCString().get()); |
- } |
- |
- Handle<Object> is_running_args[] = { answer }; |
- maybe_result = Execution::Call( |
- isolate_, is_running, cmd_processor, 1, is_running_args); |
- Handle<Object> result; |
- if (!maybe_result.ToHandle(&result)) break; |
- running = result->IsTrue(); |
- } else { |
- Handle<Object> exception; |
- if (!maybe_exception.ToHandle(&exception)) break; |
- Handle<Object> result; |
- if (!Execution::ToString(isolate_, exception).ToHandle(&result)) break; |
- answer = Handle<String>::cast(result); |
- } |
- |
- // Return the result. |
- MessageImpl message = MessageImpl::NewResponse( |
- event, running, exec_state, event_data, answer, command.client_data()); |
- InvokeMessageHandler(message); |
- command.Dispose(); |
- |
- // Return from debug event processing if either the VM is put into the |
- // running state (through a continue command) or auto continue is active |
- // and there are no more commands queued. |
- } while (!running || has_commands()); |
- command_queue_.Clear(); |
-} |
- |
- |
-void Debug::SetEventListener(Handle<Object> callback, |
- Handle<Object> data) { |
- GlobalHandles* global_handles = isolate_->global_handles(); |
- |
- // Remove existing entry. |
- GlobalHandles::Destroy(event_listener_.location()); |
- event_listener_ = Handle<Object>(); |
- GlobalHandles::Destroy(event_listener_data_.location()); |
- event_listener_data_ = Handle<Object>(); |
- |
- // Set new entry. |
- if (!callback->IsUndefined() && !callback->IsNull()) { |
- event_listener_ = global_handles->Create(*callback); |
- if (data.is_null()) data = isolate_->factory()->undefined_value(); |
- event_listener_data_ = global_handles->Create(*data); |
- } |
- |
- UpdateState(); |
-} |
- |
- |
-void Debug::SetMessageHandler(v8::Debug::MessageHandler handler) { |
- message_handler_ = handler; |
- UpdateState(); |
- if (handler == NULL && in_debug_scope()) { |
- // Send an empty command to the debugger if in a break to make JavaScript |
- // run again if the debugger is closed. |
- EnqueueCommandMessage(Vector<const uint16_t>::empty()); |
- } |
-} |
- |
- |
- |
-void Debug::UpdateState() { |
- bool is_active = message_handler_ != NULL || !event_listener_.is_null(); |
- if (is_active || in_debug_scope()) { |
- // Note that the debug context could have already been loaded to |
- // bootstrap test cases. |
- isolate_->compilation_cache()->Disable(); |
- is_active = Load(); |
- } else if (is_loaded()) { |
- isolate_->compilation_cache()->Enable(); |
- Unload(); |
- } |
- is_active_ = is_active; |
-} |
- |
- |
-// Calls the registered debug message handler. This callback is part of the |
-// public API. |
-void Debug::InvokeMessageHandler(MessageImpl message) { |
- if (message_handler_ != NULL) message_handler_(message); |
-} |
- |
- |
-// Puts a command coming from the public API on the queue. Creates |
-// a copy of the command string managed by the debugger. Up to this |
-// point, the command data was managed by the API client. Called |
-// by the API client thread. |
-void Debug::EnqueueCommandMessage(Vector<const uint16_t> command, |
- v8::Debug::ClientData* client_data) { |
- // Need to cast away const. |
- CommandMessage message = CommandMessage::New( |
- Vector<uint16_t>(const_cast<uint16_t*>(command.start()), |
- command.length()), |
- client_data); |
- isolate_->logger()->DebugTag("Put command on command_queue."); |
- command_queue_.Put(message); |
- command_received_.Signal(); |
- |
- // Set the debug command break flag to have the command processed. |
- if (!in_debug_scope()) isolate_->stack_guard()->RequestDebugCommand(); |
-} |
- |
- |
-MaybeHandle<Object> Debug::Call(Handle<JSFunction> fun, Handle<Object> data) { |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return isolate_->factory()->undefined_value(); |
- |
- // Create the execution state. |
- Handle<Object> exec_state; |
- if (!MakeExecutionState().ToHandle(&exec_state)) { |
- return isolate_->factory()->undefined_value(); |
- } |
- |
- Handle<Object> argv[] = { exec_state, data }; |
- return Execution::Call( |
- isolate_, |
- fun, |
- Handle<Object>(debug_context()->global_proxy(), isolate_), |
- arraysize(argv), |
- argv); |
-} |
- |
- |
-void Debug::HandleDebugBreak() { |
- // Ignore debug break during bootstrapping. |
- if (isolate_->bootstrapper()->IsActive()) return; |
- // Just continue if breaks are disabled. |
- if (break_disabled()) return; |
- // Ignore debug break if debugger is not active. |
- if (!is_active()) return; |
- |
- StackLimitCheck check(isolate_); |
- if (check.HasOverflowed()) return; |
- |
- { JavaScriptFrameIterator it(isolate_); |
- DCHECK(!it.done()); |
- Object* fun = it.frame()->function(); |
- if (fun && fun->IsJSFunction()) { |
- // Don't stop in builtin functions. |
- if (JSFunction::cast(fun)->IsBuiltin()) return; |
- GlobalObject* global = JSFunction::cast(fun)->context()->global_object(); |
- // Don't stop in debugger functions. |
- if (IsDebugGlobal(global)) return; |
- } |
- } |
- |
- // Collect the break state before clearing the flags. |
- bool debug_command_only = isolate_->stack_guard()->CheckDebugCommand() && |
- !isolate_->stack_guard()->CheckDebugBreak(); |
- |
- isolate_->stack_guard()->ClearDebugBreak(); |
- |
- ProcessDebugMessages(debug_command_only); |
-} |
- |
- |
-void Debug::ProcessDebugMessages(bool debug_command_only) { |
- isolate_->stack_guard()->ClearDebugCommand(); |
- |
- StackLimitCheck check(isolate_); |
- if (check.HasOverflowed()) return; |
- |
- HandleScope scope(isolate_); |
- DebugScope debug_scope(this); |
- if (debug_scope.failed()) return; |
- |
- // Notify the debug event listeners. Indicate auto continue if the break was |
- // a debug command break. |
- OnDebugBreak(isolate_->factory()->undefined_value(), debug_command_only); |
-} |
- |
- |
-DebugScope::DebugScope(Debug* debug) |
- : debug_(debug), |
- prev_(debug->debugger_entry()), |
- save_(debug_->isolate_), |
- no_termination_exceptons_(debug_->isolate_, |
- StackGuard::TERMINATE_EXECUTION) { |
- // Link recursive debugger entry. |
- base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_, |
- reinterpret_cast<base::AtomicWord>(this)); |
- |
- // Store the previous break id and frame id. |
- break_id_ = debug_->break_id(); |
- break_frame_id_ = debug_->break_frame_id(); |
- |
- // Create the new break info. If there is no JavaScript frames there is no |
- // break frame id. |
- JavaScriptFrameIterator it(isolate()); |
- bool has_js_frames = !it.done(); |
- debug_->thread_local_.break_frame_id_ = has_js_frames ? it.frame()->id() |
- : StackFrame::NO_ID; |
- debug_->SetNextBreakId(); |
- |
- debug_->UpdateState(); |
- // Make sure that debugger is loaded and enter the debugger context. |
- // The previous context is kept in save_. |
- failed_ = !debug_->is_loaded(); |
- if (!failed_) isolate()->set_context(*debug->debug_context()); |
-} |
- |
- |
- |
-DebugScope::~DebugScope() { |
- if (!failed_ && prev_ == NULL) { |
- // Clear mirror cache when leaving the debugger. Skip this if there is a |
- // pending exception as clearing the mirror cache calls back into |
- // JavaScript. This can happen if the v8::Debug::Call is used in which |
- // case the exception should end up in the calling code. |
- if (!isolate()->has_pending_exception()) debug_->ClearMirrorCache(); |
- |
- // If there are commands in the queue when leaving the debugger request |
- // that these commands are processed. |
- if (debug_->has_commands()) isolate()->stack_guard()->RequestDebugCommand(); |
- } |
- |
- // Leaving this debugger entry. |
- base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_, |
- reinterpret_cast<base::AtomicWord>(prev_)); |
- |
- // Restore to the previous break state. |
- debug_->thread_local_.break_frame_id_ = break_frame_id_; |
- debug_->thread_local_.break_id_ = break_id_; |
- |
- debug_->UpdateState(); |
-} |
- |
- |
-MessageImpl MessageImpl::NewEvent(DebugEvent event, |
- bool running, |
- Handle<JSObject> exec_state, |
- Handle<JSObject> event_data) { |
- MessageImpl message(true, event, running, |
- exec_state, event_data, Handle<String>(), NULL); |
- return message; |
-} |
- |
- |
-MessageImpl MessageImpl::NewResponse(DebugEvent event, |
- bool running, |
- Handle<JSObject> exec_state, |
- Handle<JSObject> event_data, |
- Handle<String> response_json, |
- v8::Debug::ClientData* client_data) { |
- MessageImpl message(false, event, running, |
- exec_state, event_data, response_json, client_data); |
- return message; |
-} |
- |
- |
-MessageImpl::MessageImpl(bool is_event, |
- DebugEvent event, |
- bool running, |
- Handle<JSObject> exec_state, |
- Handle<JSObject> event_data, |
- Handle<String> response_json, |
- v8::Debug::ClientData* client_data) |
- : is_event_(is_event), |
- event_(event), |
- running_(running), |
- exec_state_(exec_state), |
- event_data_(event_data), |
- response_json_(response_json), |
- client_data_(client_data) {} |
- |
- |
-bool MessageImpl::IsEvent() const { |
- return is_event_; |
-} |
- |
- |
-bool MessageImpl::IsResponse() const { |
- return !is_event_; |
-} |
- |
- |
-DebugEvent MessageImpl::GetEvent() const { |
- return event_; |
-} |
- |
- |
-bool MessageImpl::WillStartRunning() const { |
- return running_; |
-} |
- |
- |
-v8::Local<v8::Object> MessageImpl::GetExecutionState() const { |
- return v8::Utils::ToLocal(exec_state_); |
-} |
- |
- |
-v8::Isolate* MessageImpl::GetIsolate() const { |
- return reinterpret_cast<v8::Isolate*>(exec_state_->GetIsolate()); |
-} |
- |
- |
-v8::Local<v8::Object> MessageImpl::GetEventData() const { |
- return v8::Utils::ToLocal(event_data_); |
-} |
- |
- |
-v8::Local<v8::String> MessageImpl::GetJSON() const { |
- Isolate* isolate = event_data_->GetIsolate(); |
- v8::EscapableHandleScope scope(reinterpret_cast<v8::Isolate*>(isolate)); |
- |
- if (IsEvent()) { |
- // Call toJSONProtocol on the debug event object. |
- Handle<Object> fun = Object::GetProperty( |
- isolate, event_data_, "toJSONProtocol").ToHandleChecked(); |
- if (!fun->IsJSFunction()) { |
- return v8::Local<v8::String>(); |
- } |
- |
- MaybeHandle<Object> maybe_json = |
- Execution::TryCall(Handle<JSFunction>::cast(fun), event_data_, 0, NULL); |
- Handle<Object> json; |
- if (!maybe_json.ToHandle(&json) || !json->IsString()) { |
- return v8::Local<v8::String>(); |
- } |
- return scope.Escape(v8::Utils::ToLocal(Handle<String>::cast(json))); |
- } else { |
- return v8::Utils::ToLocal(response_json_); |
- } |
-} |
- |
- |
-v8::Local<v8::Context> MessageImpl::GetEventContext() const { |
- Isolate* isolate = event_data_->GetIsolate(); |
- v8::Local<v8::Context> context = GetDebugEventContext(isolate); |
- // Isolate::context() may be NULL when "script collected" event occurs. |
- DCHECK(!context.IsEmpty()); |
- return context; |
-} |
- |
- |
-v8::Debug::ClientData* MessageImpl::GetClientData() const { |
- return client_data_; |
-} |
- |
- |
-EventDetailsImpl::EventDetailsImpl(DebugEvent event, |
- Handle<JSObject> exec_state, |
- Handle<JSObject> event_data, |
- Handle<Object> callback_data, |
- v8::Debug::ClientData* client_data) |
- : event_(event), |
- exec_state_(exec_state), |
- event_data_(event_data), |
- callback_data_(callback_data), |
- client_data_(client_data) {} |
- |
- |
-DebugEvent EventDetailsImpl::GetEvent() const { |
- return event_; |
-} |
- |
- |
-v8::Local<v8::Object> EventDetailsImpl::GetExecutionState() const { |
- return v8::Utils::ToLocal(exec_state_); |
-} |
- |
- |
-v8::Local<v8::Object> EventDetailsImpl::GetEventData() const { |
- return v8::Utils::ToLocal(event_data_); |
-} |
- |
- |
-v8::Local<v8::Context> EventDetailsImpl::GetEventContext() const { |
- return GetDebugEventContext(exec_state_->GetIsolate()); |
-} |
- |
- |
-v8::Local<v8::Value> EventDetailsImpl::GetCallbackData() const { |
- return v8::Utils::ToLocal(callback_data_); |
-} |
- |
- |
-v8::Debug::ClientData* EventDetailsImpl::GetClientData() const { |
- return client_data_; |
-} |
- |
- |
-CommandMessage::CommandMessage() : text_(Vector<uint16_t>::empty()), |
- client_data_(NULL) { |
-} |
- |
- |
-CommandMessage::CommandMessage(const Vector<uint16_t>& text, |
- v8::Debug::ClientData* data) |
- : text_(text), |
- client_data_(data) { |
-} |
- |
- |
-void CommandMessage::Dispose() { |
- text_.Dispose(); |
- delete client_data_; |
- client_data_ = NULL; |
-} |
- |
- |
-CommandMessage CommandMessage::New(const Vector<uint16_t>& command, |
- v8::Debug::ClientData* data) { |
- return CommandMessage(command.Clone(), data); |
-} |
- |
- |
-CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0), |
- size_(size) { |
- messages_ = NewArray<CommandMessage>(size); |
-} |
- |
- |
-CommandMessageQueue::~CommandMessageQueue() { |
- while (!IsEmpty()) Get().Dispose(); |
- DeleteArray(messages_); |
-} |
- |
- |
-CommandMessage CommandMessageQueue::Get() { |
- DCHECK(!IsEmpty()); |
- int result = start_; |
- start_ = (start_ + 1) % size_; |
- return messages_[result]; |
-} |
- |
- |
-void CommandMessageQueue::Put(const CommandMessage& message) { |
- if ((end_ + 1) % size_ == start_) { |
- Expand(); |
- } |
- messages_[end_] = message; |
- end_ = (end_ + 1) % size_; |
-} |
- |
- |
-void CommandMessageQueue::Expand() { |
- CommandMessageQueue new_queue(size_ * 2); |
- while (!IsEmpty()) { |
- new_queue.Put(Get()); |
- } |
- CommandMessage* array_to_free = messages_; |
- *this = new_queue; |
- new_queue.messages_ = array_to_free; |
- // Make the new_queue empty so that it doesn't call Dispose on any messages. |
- new_queue.start_ = new_queue.end_; |
- // Automatic destructor called on new_queue, freeing array_to_free. |
-} |
- |
- |
-LockingCommandMessageQueue::LockingCommandMessageQueue(Logger* logger, int size) |
- : logger_(logger), queue_(size) {} |
- |
- |
-bool LockingCommandMessageQueue::IsEmpty() const { |
- base::LockGuard<base::Mutex> lock_guard(&mutex_); |
- return queue_.IsEmpty(); |
-} |
- |
- |
-CommandMessage LockingCommandMessageQueue::Get() { |
- base::LockGuard<base::Mutex> lock_guard(&mutex_); |
- CommandMessage result = queue_.Get(); |
- logger_->DebugEvent("Get", result.text()); |
- return result; |
-} |
- |
- |
-void LockingCommandMessageQueue::Put(const CommandMessage& message) { |
- base::LockGuard<base::Mutex> lock_guard(&mutex_); |
- queue_.Put(message); |
- logger_->DebugEvent("Put", message.text()); |
-} |
- |
- |
-void LockingCommandMessageQueue::Clear() { |
- base::LockGuard<base::Mutex> lock_guard(&mutex_); |
- queue_.Clear(); |
-} |
- |
-} // namespace internal |
-} // namespace v8 |