Some more debugger-related refactorings.
R=ulan@chromium.org Review URL: https://codereview.chromium.org/297303006 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21607 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
77130247ac
commit
19c71f9e47
291
src/debug.cc
291
src/debug.cc
@ -80,6 +80,22 @@ BreakLocationIterator::~BreakLocationIterator() {
|
||||
}
|
||||
|
||||
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// point location when looking for source break locations.
|
||||
static bool IsSourceBreakStub(Code* code) {
|
||||
CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
||||
return major_key == CodeStub::CallFunction;
|
||||
}
|
||||
|
||||
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// location.
|
||||
static bool IsBreakStub(Code* code) {
|
||||
CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
||||
return major_key == CodeStub::CallFunction;
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::Next() {
|
||||
DisallowHeapAllocation no_gc;
|
||||
ASSERT(!RinfoDone());
|
||||
@ -129,15 +145,14 @@ void BreakLocationIterator::Next() {
|
||||
if (IsDebuggerStatement()) {
|
||||
break_point_++;
|
||||
return;
|
||||
}
|
||||
if (type_ == ALL_BREAK_LOCATIONS) {
|
||||
if (Debug::IsBreakStub(code)) {
|
||||
} else if (type_ == ALL_BREAK_LOCATIONS) {
|
||||
if (IsBreakStub(code)) {
|
||||
break_point_++;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ASSERT(type_ == SOURCE_BREAK_LOCATIONS);
|
||||
if (Debug::IsSourceBreakStub(code)) {
|
||||
if (IsSourceBreakStub(code)) {
|
||||
break_point_++;
|
||||
return;
|
||||
}
|
||||
@ -422,6 +437,53 @@ bool BreakLocationIterator::IsDebugBreak() {
|
||||
}
|
||||
|
||||
|
||||
// Find the builtin to use for invoking the debug break
|
||||
static Handle<Code> DebugBreakForIC(Handle<Code> code, RelocInfo::Mode mode) {
|
||||
Isolate* isolate = code->GetIsolate();
|
||||
|
||||
// Find the builtin debug break function matching the calling convention
|
||||
// used by the call site.
|
||||
if (code->is_inline_cache_stub()) {
|
||||
switch (code->kind()) {
|
||||
case Code::CALL_IC:
|
||||
return isolate->builtins()->CallICStub_DebugBreak();
|
||||
|
||||
case Code::LOAD_IC:
|
||||
return isolate->builtins()->LoadIC_DebugBreak();
|
||||
|
||||
case Code::STORE_IC:
|
||||
return isolate->builtins()->StoreIC_DebugBreak();
|
||||
|
||||
case Code::KEYED_LOAD_IC:
|
||||
return isolate->builtins()->KeyedLoadIC_DebugBreak();
|
||||
|
||||
case Code::KEYED_STORE_IC:
|
||||
return isolate->builtins()->KeyedStoreIC_DebugBreak();
|
||||
|
||||
case Code::COMPARE_NIL_IC:
|
||||
return isolate->builtins()->CompareNilIC_DebugBreak();
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (RelocInfo::IsConstructCall(mode)) {
|
||||
if (code->has_function_cache()) {
|
||||
return isolate->builtins()->CallConstructStub_Recording_DebugBreak();
|
||||
} else {
|
||||
return isolate->builtins()->CallConstructStub_DebugBreak();
|
||||
}
|
||||
}
|
||||
if (code->kind() == Code::STUB) {
|
||||
ASSERT(code->major_key() == CodeStub::CallFunction);
|
||||
return isolate->builtins()->CallFunctionStub_DebugBreak();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
|
||||
void BreakLocationIterator::SetDebugBreakAtIC() {
|
||||
// Patch the original code with the current address as the current address
|
||||
// might have changed by the inline caching since the code was copied.
|
||||
@ -434,7 +496,7 @@ void BreakLocationIterator::SetDebugBreakAtIC() {
|
||||
|
||||
// Patch the code to invoke the builtin debug break function matching the
|
||||
// calling convention used by the call site.
|
||||
Handle<Code> dbgbrk_code(Debug::FindDebugBreak(target_code, mode));
|
||||
Handle<Code> dbgbrk_code = DebugBreakForIC(target_code, mode);
|
||||
rinfo()->set_target_address(dbgbrk_code->entry());
|
||||
}
|
||||
}
|
||||
@ -504,7 +566,6 @@ void Debug::ThreadInit() {
|
||||
thread_local_.step_out_fp_ = 0;
|
||||
// TODO(isolates): frames_are_dropped_?
|
||||
thread_local_.debugger_entry_ = NULL;
|
||||
thread_local_.has_pending_interrupt_ = false;
|
||||
thread_local_.restarter_frame_function_pointer_ = NULL;
|
||||
thread_local_.promise_on_stack_ = NULL;
|
||||
}
|
||||
@ -530,6 +591,30 @@ int Debug::ArchiveSpacePerThread() {
|
||||
}
|
||||
|
||||
|
||||
ScriptCache::ScriptCache(Isolate* isolate) : HashMap(HashMap::PointersMatch),
|
||||
isolate_(isolate),
|
||||
collected_scripts_(10) {
|
||||
Heap* heap = isolate_->heap();
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Perform two GCs to get rid of all unreferenced scripts. The first GC gets
|
||||
// rid of all the cached script wrappers and the second gets rid of the
|
||||
// scripts which are no longer referenced.
|
||||
heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "ScriptCache");
|
||||
heap->CollectAllGarbage(Heap::kMakeHeapIterableMask, "ScriptCache");
|
||||
|
||||
// Scan heap for Script objects.
|
||||
HeapIterator iterator(heap);
|
||||
DisallowHeapAllocation no_allocation;
|
||||
|
||||
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
||||
if (obj->IsScript() && Script::cast(obj)->HasValidSource()) {
|
||||
Add(Handle<Script>(Script::cast(obj)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ScriptCache::Add(Handle<Script> script) {
|
||||
GlobalHandles* global_handles = isolate_->global_handles();
|
||||
// Create an entry in the hash map for the script.
|
||||
@ -785,7 +870,10 @@ void Debug::Unload() {
|
||||
if (!is_loaded()) return;
|
||||
|
||||
// Clear the script cache.
|
||||
DestroyScriptCache();
|
||||
if (script_cache_ != NULL) {
|
||||
delete script_cache_;
|
||||
script_cache_ = NULL;
|
||||
}
|
||||
|
||||
// Clear debugger context global handle.
|
||||
GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location());
|
||||
@ -843,7 +931,8 @@ void Debug::Break(Arguments args, JavaScriptFrame* frame) {
|
||||
|
||||
// If step out is active skip everything until the frame where we need to step
|
||||
// out to is reached, unless real breakpoint is hit.
|
||||
if (StepOutActive() && frame->fp() != step_out_fp() &&
|
||||
if (StepOutActive() &&
|
||||
frame->fp() != thread_local_.step_out_fp_ &&
|
||||
break_points_hit->IsUndefined() ) {
|
||||
// Step count should always be 0 for StepOut.
|
||||
ASSERT(thread_local_.step_count_ == 0);
|
||||
@ -1192,7 +1281,7 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) {
|
||||
}
|
||||
|
||||
|
||||
Debug::PromiseOnStack::PromiseOnStack(Isolate* isolate,
|
||||
PromiseOnStack::PromiseOnStack(Isolate* isolate,
|
||||
PromiseOnStack* prev,
|
||||
Handle<JSFunction> getter)
|
||||
: isolate_(isolate), prev_(prev) {
|
||||
@ -1203,7 +1292,7 @@ Debug::PromiseOnStack::PromiseOnStack(Isolate* isolate,
|
||||
}
|
||||
|
||||
|
||||
Debug::PromiseOnStack::~PromiseOnStack() {
|
||||
PromiseOnStack::~PromiseOnStack() {
|
||||
isolate_->global_handles()->Destroy(Handle<Object>::cast(getter_).location());
|
||||
}
|
||||
|
||||
@ -1530,68 +1619,8 @@ bool Debug::IsDebugBreak(Address addr) {
|
||||
}
|
||||
|
||||
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// point location when looking for source break locations.
|
||||
bool Debug::IsSourceBreakStub(Code* code) {
|
||||
CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
||||
return major_key == CodeStub::CallFunction;
|
||||
}
|
||||
|
||||
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// location.
|
||||
bool Debug::IsBreakStub(Code* code) {
|
||||
CodeStub::Major major_key = CodeStub::GetMajorKey(code);
|
||||
return major_key == CodeStub::CallFunction;
|
||||
}
|
||||
|
||||
|
||||
// Find the builtin to use for invoking the debug break
|
||||
Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
|
||||
Isolate* isolate = code->GetIsolate();
|
||||
|
||||
// Find the builtin debug break function matching the calling convention
|
||||
// used by the call site.
|
||||
if (code->is_inline_cache_stub()) {
|
||||
switch (code->kind()) {
|
||||
case Code::CALL_IC:
|
||||
return isolate->builtins()->CallICStub_DebugBreak();
|
||||
|
||||
case Code::LOAD_IC:
|
||||
return isolate->builtins()->LoadIC_DebugBreak();
|
||||
|
||||
case Code::STORE_IC:
|
||||
return isolate->builtins()->StoreIC_DebugBreak();
|
||||
|
||||
case Code::KEYED_LOAD_IC:
|
||||
return isolate->builtins()->KeyedLoadIC_DebugBreak();
|
||||
|
||||
case Code::KEYED_STORE_IC:
|
||||
return isolate->builtins()->KeyedStoreIC_DebugBreak();
|
||||
|
||||
case Code::COMPARE_NIL_IC:
|
||||
return isolate->builtins()->CompareNilIC_DebugBreak();
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
if (RelocInfo::IsConstructCall(mode)) {
|
||||
if (code->has_function_cache()) {
|
||||
return isolate->builtins()->CallConstructStub_Recording_DebugBreak();
|
||||
} else {
|
||||
return isolate->builtins()->CallConstructStub_DebugBreak();
|
||||
}
|
||||
}
|
||||
if (code->kind() == Code::STUB) {
|
||||
ASSERT(code->major_key() == CodeStub::CallFunction);
|
||||
return isolate->builtins()->CallFunctionStub_DebugBreak();
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
return Handle<Code>::null();
|
||||
}
|
||||
|
||||
|
||||
// Simple function for returning the source positions for active break points.
|
||||
Handle<Object> Debug::GetSourceBreakLocations(
|
||||
@ -1655,7 +1684,7 @@ void Debug::HandleStepIn(Handle<JSFunction> function,
|
||||
|
||||
// Flood the function with one-shot break points if it is called from where
|
||||
// step into was requested.
|
||||
if (fp == step_in_fp()) {
|
||||
if (fp == thread_local_.step_into_fp_) {
|
||||
if (function->shared()->bound()) {
|
||||
// Handle Function.prototype.bind
|
||||
Debug::FloodBoundFunctionWithOneShot(function);
|
||||
@ -1912,7 +1941,7 @@ class ActiveFunctionsRedirector : public ThreadVisitor {
|
||||
};
|
||||
|
||||
|
||||
void Debug::EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function) {
|
||||
static void EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function) {
|
||||
if (function->code()->kind() == Code::FUNCTION &&
|
||||
function->code()->has_debug_break_slots()) {
|
||||
// Nothing to do. Function code already had debug break slots.
|
||||
@ -1931,7 +1960,7 @@ void Debug::EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function) {
|
||||
}
|
||||
|
||||
|
||||
void Debug::RecompileAndRelocateSuspendedGenerators(
|
||||
static void RecompileAndRelocateSuspendedGenerators(
|
||||
const List<Handle<JSGeneratorObject> > &generators) {
|
||||
for (int i = 0; i < generators.length(); i++) {
|
||||
Handle<JSFunction> fun(generators[i]->function());
|
||||
@ -2444,60 +2473,10 @@ void Debug::ClearMirrorCache() {
|
||||
}
|
||||
|
||||
|
||||
void Debug::CreateScriptCache() {
|
||||
Heap* heap = isolate_->heap();
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Perform two GCs to get rid of all unreferenced scripts. The first GC gets
|
||||
// rid of all the cached script wrappers and the second gets rid of the
|
||||
// scripts which are no longer referenced. The second also sweeps precisely,
|
||||
// which saves us doing yet another GC to make the heap iterable.
|
||||
heap->CollectAllGarbage(Heap::kNoGCFlags, "Debug::CreateScriptCache");
|
||||
|
||||
ASSERT(script_cache_ == NULL);
|
||||
script_cache_ = new ScriptCache(isolate_);
|
||||
|
||||
// Scan heap for Script objects.
|
||||
int count = 0;
|
||||
HeapIterator iterator(heap);
|
||||
|
||||
for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
|
||||
if (obj->IsScript() && Script::cast(obj)->HasValidSource()) {
|
||||
script_cache_->Add(Handle<Script>(Script::cast(obj)));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Debug::DestroyScriptCache() {
|
||||
// Get rid of the script cache if it was created.
|
||||
if (script_cache_ != NULL) {
|
||||
delete script_cache_;
|
||||
script_cache_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Debug::AddScriptToScriptCache(Handle<Script> script) {
|
||||
if (script_cache_ != NULL) {
|
||||
script_cache_->Add(script);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Handle<FixedArray> Debug::GetLoadedScripts() {
|
||||
// Create and fill the script cache when the loaded scripts is requested for
|
||||
// the first time.
|
||||
if (script_cache_ == NULL) {
|
||||
CreateScriptCache();
|
||||
}
|
||||
|
||||
// If the script cache is not active just return an empty array.
|
||||
ASSERT(script_cache_ != NULL);
|
||||
if (script_cache_ == NULL) {
|
||||
isolate_->factory()->NewFixedArray(0);
|
||||
}
|
||||
if (script_cache_ == NULL) script_cache_ = new ScriptCache(isolate_);
|
||||
|
||||
// Perform GC to get unreferenced scripts evicted from the cache before
|
||||
// returning the content.
|
||||
@ -2604,22 +2583,19 @@ MaybeHandle<Object> Debug::MakeScriptCollectedEvent(int id) {
|
||||
|
||||
|
||||
void Debug::OnException(Handle<Object> exception, bool uncaught) {
|
||||
if (is_entered() || ignore_events()) return;
|
||||
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Bail out based on state or if there is no listener for this event
|
||||
if (is_entered()) return;
|
||||
if (!EventActive()) return;
|
||||
|
||||
Handle<Object> promise = GetPromiseForUncaughtException();
|
||||
uncaught |= !promise->IsUndefined();
|
||||
|
||||
// Bail out if exception breaks are not active
|
||||
if (uncaught) {
|
||||
// Uncaught exceptions are reported by either flags.
|
||||
if (!(break_on_uncaught_exception() || break_on_exception())) return;
|
||||
if (!(break_on_uncaught_exception_ || break_on_exception_)) return;
|
||||
} else {
|
||||
// Caught exceptions are reported is activated.
|
||||
if (!break_on_exception()) return;
|
||||
if (!break_on_exception_) return;
|
||||
}
|
||||
|
||||
// Enter the debugger.
|
||||
@ -2645,14 +2621,12 @@ void Debug::OnException(Handle<Object> exception, bool uncaught) {
|
||||
|
||||
void Debug::OnDebugBreak(Handle<Object> break_points_hit,
|
||||
bool auto_continue) {
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Debugger has already been entered by caller.
|
||||
ASSERT(isolate_->context() == *debug_context());
|
||||
|
||||
// Bail out if there is no listener for this event
|
||||
if (!EventActive()) return;
|
||||
if (ignore_events()) return;
|
||||
|
||||
HandleScope scope(isolate_);
|
||||
// Create the event data object.
|
||||
Handle<Object> event_data;
|
||||
// Bail out and don't call debugger if exception.
|
||||
@ -2666,12 +2640,9 @@ void Debug::OnDebugBreak(Handle<Object> break_points_hit,
|
||||
|
||||
|
||||
void Debug::OnBeforeCompile(Handle<Script> script) {
|
||||
if (is_entered() || ignore_events()) return;
|
||||
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Bail out based on state or if there is no listener for this event
|
||||
if (is_entered()) return;
|
||||
if (!EventActive()) return;
|
||||
|
||||
// Enter the debugger.
|
||||
EnterDebugger debugger(isolate_);
|
||||
if (debugger.FailedToEnter()) return;
|
||||
@ -2690,15 +2661,14 @@ void Debug::OnBeforeCompile(Handle<Script> script) {
|
||||
|
||||
// Handle debugger actions when a new script is compiled.
|
||||
void Debug::OnAfterCompile(Handle<Script> script,
|
||||
AfterCompileFlags after_compile_flags) {
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
AfterCompileFlags after_compile_flags) {
|
||||
// Add the newly compiled script to the script cache.
|
||||
AddScriptToScriptCache(script);
|
||||
if (script_cache_ != NULL) script_cache_->Add(script);
|
||||
|
||||
// No more to do if not debugging.
|
||||
if (!EventActive()) return;
|
||||
if (is_entered() || ignore_events()) return;
|
||||
|
||||
HandleScope scope(isolate_);
|
||||
// Store whether in debugger before entering debugger.
|
||||
bool in_debugger = is_entered();
|
||||
|
||||
@ -2748,12 +2718,9 @@ void Debug::OnAfterCompile(Handle<Script> script,
|
||||
|
||||
|
||||
void Debug::OnScriptCollected(int id) {
|
||||
if (is_entered() || ignore_events()) return;
|
||||
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// No more to do if not debugging.
|
||||
if (is_entered()) return;
|
||||
if (!EventActive()) return;
|
||||
|
||||
// Enter the debugger.
|
||||
EnterDebugger debugger(isolate_);
|
||||
if (debugger.FailedToEnter()) return;
|
||||
@ -2775,9 +2742,6 @@ void Debug::ProcessDebugEvent(v8::DebugEvent event,
|
||||
bool auto_continue) {
|
||||
HandleScope scope(isolate_);
|
||||
|
||||
// Clear any pending debug break if this is a real break.
|
||||
if (!auto_continue) thread_local_.has_pending_interrupt_ = false;
|
||||
|
||||
// Create the execution state.
|
||||
Handle<Object> exec_state;
|
||||
// Bail out and don't call debugger if exception.
|
||||
@ -3095,7 +3059,7 @@ MaybeHandle<Object> Debug::Call(Handle<JSFunction> fun, Handle<Object> data) {
|
||||
}
|
||||
|
||||
|
||||
void Debug::DebugBreakHelper() {
|
||||
void Debug::HandleDebugBreak() {
|
||||
// Ignore debug break during bootstrapping.
|
||||
if (isolate_->bootstrapper()->IsActive()) return;
|
||||
// Just continue if breaks are disabled.
|
||||
@ -3161,9 +3125,9 @@ EnterDebugger::EnterDebugger(Isolate* isolate)
|
||||
// Create the new break info. If there is no JavaScript frames there is no
|
||||
// break frame id.
|
||||
JavaScriptFrameIterator it(isolate_);
|
||||
has_js_frames_ = !it.done();
|
||||
debug->thread_local_.break_frame_id_ = has_js_frames_ ? it.frame()->id()
|
||||
: StackFrame::NO_ID;
|
||||
bool has_js_frames = !it.done();
|
||||
debug->thread_local_.break_frame_id_ = has_js_frames ? it.frame()->id()
|
||||
: StackFrame::NO_ID;
|
||||
debug->SetNextBreakId();
|
||||
|
||||
debug->UpdateState();
|
||||
@ -3187,9 +3151,7 @@ EnterDebugger::~EnterDebugger() {
|
||||
// pending exception as clearing the mirror cache calls back into
|
||||
// JavaScript. This can happen if the v8::Debug::Call is used in which
|
||||
// case the exception should end up in the calling code.
|
||||
if (!isolate_->has_pending_exception()) {
|
||||
debug->ClearMirrorCache();
|
||||
}
|
||||
if (!isolate_->has_pending_exception()) debug->ClearMirrorCache();
|
||||
|
||||
// If there are commands in the queue when leaving the debugger request
|
||||
// that these commands are processed.
|
||||
@ -3369,10 +3331,6 @@ CommandMessage::CommandMessage(const Vector<uint16_t>& text,
|
||||
}
|
||||
|
||||
|
||||
CommandMessage::~CommandMessage() {
|
||||
}
|
||||
|
||||
|
||||
void CommandMessage::Dispose() {
|
||||
text_.Dispose();
|
||||
delete client_data_;
|
||||
@ -3393,10 +3351,7 @@ CommandMessageQueue::CommandMessageQueue(int size) : start_(0), end_(0),
|
||||
|
||||
|
||||
CommandMessageQueue::~CommandMessageQueue() {
|
||||
while (!IsEmpty()) {
|
||||
CommandMessage m = Get();
|
||||
m.Dispose();
|
||||
}
|
||||
while (!IsEmpty()) Get().Dispose();
|
||||
DeleteArray(messages_);
|
||||
}
|
||||
|
||||
|
224
src/debug.h
224
src/debug.h
@ -150,10 +150,7 @@ class BreakLocationIterator {
|
||||
// the cache is the script id.
|
||||
class ScriptCache : private HashMap {
|
||||
public:
|
||||
explicit ScriptCache(Isolate* isolate)
|
||||
: HashMap(HashMap::PointersMatch),
|
||||
isolate_(isolate),
|
||||
collected_scripts_(10) {}
|
||||
explicit ScriptCache(Isolate* isolate);
|
||||
virtual ~ScriptCache() { Clear(); }
|
||||
|
||||
// Add script to the cache.
|
||||
@ -287,7 +284,6 @@ class CommandMessage {
|
||||
static CommandMessage New(const Vector<uint16_t>& command,
|
||||
v8::Debug::ClientData* data);
|
||||
CommandMessage();
|
||||
~CommandMessage();
|
||||
|
||||
// Deletes user data and disposes of the text.
|
||||
void Dispose();
|
||||
@ -342,6 +338,23 @@ class LockingCommandMessageQueue BASE_EMBEDDED {
|
||||
};
|
||||
|
||||
|
||||
class PromiseOnStack {
|
||||
public:
|
||||
PromiseOnStack(Isolate* isolate,
|
||||
PromiseOnStack* prev,
|
||||
Handle<JSFunction> getter);
|
||||
~PromiseOnStack();
|
||||
StackHandler* handler() { return handler_; }
|
||||
Handle<JSFunction> getter() { return getter_; }
|
||||
PromiseOnStack* prev() { return prev_; }
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
StackHandler* handler_;
|
||||
Handle<JSFunction> getter_;
|
||||
PromiseOnStack* prev_;
|
||||
};
|
||||
|
||||
|
||||
// This class contains the debugger support. The main purpose is to handle
|
||||
// setting break points in the code.
|
||||
//
|
||||
@ -374,29 +387,18 @@ class Debug {
|
||||
MUST_USE_RESULT MaybeHandle<Object> Call(Handle<JSFunction> fun,
|
||||
Handle<Object> data);
|
||||
Handle<Context> GetDebugContext();
|
||||
void DebugBreakHelper();
|
||||
void HandleDebugBreak();
|
||||
void ProcessDebugMessages(bool debug_command_only);
|
||||
|
||||
// Flags and states.
|
||||
void set_live_edit_enabled(bool v) { live_edit_enabled_ = v; }
|
||||
bool live_edit_enabled() const {
|
||||
return FLAG_enable_liveedit && live_edit_enabled_ ;
|
||||
}
|
||||
|
||||
inline bool is_active() const { return is_active_; }
|
||||
inline bool is_loaded() const { return !debug_context_.is_null(); }
|
||||
inline bool has_break_points() const { return has_break_points_; }
|
||||
inline bool is_entered() const {
|
||||
return thread_local_.debugger_entry_ != NULL;
|
||||
}
|
||||
|
||||
void set_disable_break(bool v) { break_disabled_ = v; }
|
||||
|
||||
// Internal logic
|
||||
bool Load();
|
||||
|
||||
void Break(Arguments args, JavaScriptFrame*);
|
||||
void SetAfterBreakTarget(JavaScriptFrame* frame);
|
||||
|
||||
// Scripts handling.
|
||||
Handle<FixedArray> GetLoadedScripts();
|
||||
|
||||
// Break point handling.
|
||||
bool SetBreakPoint(Handle<JSFunction> function,
|
||||
Handle<Object> break_point_object,
|
||||
int* source_position);
|
||||
@ -412,11 +414,7 @@ class Debug {
|
||||
void ChangeBreakOnException(ExceptionBreakType type, bool enable);
|
||||
bool IsBreakOnException(ExceptionBreakType type);
|
||||
|
||||
void PromiseHandlePrologue(Handle<JSFunction> promise_getter);
|
||||
void PromiseHandleEpilogue();
|
||||
// Returns a promise if it does not have a reject handler.
|
||||
Handle<Object> GetPromiseForUncaughtException();
|
||||
|
||||
// Stepping handling.
|
||||
void PrepareStep(StepAction step_action,
|
||||
int step_count,
|
||||
StackFrame::Id frame_id);
|
||||
@ -425,6 +423,15 @@ class Debug {
|
||||
bool IsStepping() { return thread_local_.step_count_ > 0; }
|
||||
bool StepNextContinue(BreakLocationIterator* break_location_iterator,
|
||||
JavaScriptFrame* frame);
|
||||
bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
|
||||
void HandleStepIn(Handle<JSFunction> function,
|
||||
Handle<Object> holder,
|
||||
Address fp,
|
||||
bool is_constructor);
|
||||
bool StepOutActive() { return thread_local_.step_out_fp_ != 0; }
|
||||
|
||||
// Purge all code objects that have no debug break slots.
|
||||
void PrepareForBreakPoints();
|
||||
|
||||
// Returns whether the operation succeeded. Compilation can only be triggered
|
||||
// if a valid closure is passed as the second argument, otherwise the shared
|
||||
@ -434,74 +441,70 @@ class Debug {
|
||||
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
|
||||
static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
|
||||
|
||||
void PrepareForBreakPoints();
|
||||
|
||||
// This function is used in FunctionNameUsing* tests.
|
||||
Object* FindSharedFunctionInfoInScript(Handle<Script> script, int position);
|
||||
|
||||
|
||||
// Returns true if the current stub call is patched to call the debugger.
|
||||
static bool IsDebugBreak(Address addr);
|
||||
// Returns true if the current return statement has been patched to be
|
||||
// a debugger breakpoint.
|
||||
static bool IsDebugBreakAtReturn(RelocInfo* rinfo);
|
||||
// Check whether a code stub with the specified major key is a possible break
|
||||
// point location.
|
||||
static bool IsSourceBreakStub(Code* code);
|
||||
static bool IsBreakStub(Code* code);
|
||||
// Find the builtin to use for invoking the debug break
|
||||
static Handle<Code> FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode);
|
||||
|
||||
static Handle<Object> GetSourceBreakLocations(
|
||||
Handle<SharedFunctionInfo> shared,
|
||||
BreakPositionAlignment position_aligment);
|
||||
|
||||
// Getter for the debug_context.
|
||||
inline Handle<Context> debug_context() { return debug_context_; }
|
||||
|
||||
// Check whether a global object is the debug global object.
|
||||
bool IsDebugGlobal(GlobalObject* global);
|
||||
|
||||
// Check whether this frame is just about to return.
|
||||
bool IsBreakAtReturn(JavaScriptFrame* frame);
|
||||
|
||||
StackFrame::Id break_frame_id() { return thread_local_.break_frame_id_; }
|
||||
int break_id() { return thread_local_.break_id_; }
|
||||
|
||||
bool StepInActive() { return thread_local_.step_into_fp_ != 0; }
|
||||
void HandleStepIn(Handle<JSFunction> function,
|
||||
Handle<Object> holder,
|
||||
Address fp,
|
||||
bool is_constructor);
|
||||
Address step_in_fp() { return thread_local_.step_into_fp_; }
|
||||
Address* step_in_fp_addr() { return &thread_local_.step_into_fp_; }
|
||||
|
||||
bool StepOutActive() { return thread_local_.step_out_fp_ != 0; }
|
||||
Address step_out_fp() { return thread_local_.step_out_fp_; }
|
||||
|
||||
EnterDebugger* debugger_entry() { return thread_local_.debugger_entry_; }
|
||||
|
||||
// Check whether any of the specified interrupts are pending.
|
||||
bool has_pending_interrupt() {
|
||||
return thread_local_.has_pending_interrupt_;
|
||||
}
|
||||
|
||||
// Set specified interrupts as pending.
|
||||
void set_has_pending_interrupt(bool value) {
|
||||
thread_local_.has_pending_interrupt_ = value;
|
||||
}
|
||||
|
||||
// Getters for the current exception break state.
|
||||
bool break_on_exception() { return break_on_exception_; }
|
||||
bool break_on_uncaught_exception() {
|
||||
return break_on_uncaught_exception_;
|
||||
}
|
||||
// Promise handling.
|
||||
void PromiseHandlePrologue(Handle<JSFunction> promise_getter);
|
||||
void PromiseHandleEpilogue();
|
||||
|
||||
// Support for LiveEdit
|
||||
void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
|
||||
LiveEdit::FrameDropMode mode,
|
||||
Object** restarter_frame_function_pointer);
|
||||
|
||||
// Support for setting the address to jump to when returning from break point.
|
||||
// Passed to MakeWeak.
|
||||
static void HandleWeakDebugInfo(
|
||||
const v8::WeakCallbackData<v8::Value, void>& data);
|
||||
|
||||
// Threading support.
|
||||
char* ArchiveDebug(char* to);
|
||||
char* RestoreDebug(char* from);
|
||||
static int ArchiveSpacePerThread();
|
||||
void FreeThreadResources() { }
|
||||
|
||||
// Record function from which eval was called.
|
||||
static void RecordEvalCaller(Handle<Script> script);
|
||||
|
||||
// Garbage collection notifications.
|
||||
void AfterGarbageCollection();
|
||||
|
||||
// Flags and states.
|
||||
EnterDebugger* debugger_entry() { return thread_local_.debugger_entry_; }
|
||||
inline Handle<Context> debug_context() { return debug_context_; }
|
||||
void set_live_edit_enabled(bool v) { live_edit_enabled_ = v; }
|
||||
bool live_edit_enabled() const {
|
||||
return FLAG_enable_liveedit && live_edit_enabled_ ;
|
||||
}
|
||||
|
||||
inline bool is_active() const { return is_active_; }
|
||||
inline bool is_loaded() const { return !debug_context_.is_null(); }
|
||||
inline bool has_break_points() const { return has_break_points_; }
|
||||
inline bool is_entered() const {
|
||||
return thread_local_.debugger_entry_ != NULL;
|
||||
}
|
||||
void set_disable_break(bool v) { break_disabled_ = v; }
|
||||
|
||||
StackFrame::Id break_frame_id() { return thread_local_.break_frame_id_; }
|
||||
int break_id() { return thread_local_.break_id_; }
|
||||
|
||||
// Support for embedding into generated code.
|
||||
Address after_break_target_address() {
|
||||
return reinterpret_cast<Address>(&after_break_target_);
|
||||
}
|
||||
@ -511,48 +514,22 @@ class Debug {
|
||||
return reinterpret_cast<Address>(address);
|
||||
}
|
||||
|
||||
static const int kEstimatedNofBreakPointsInFunction = 16;
|
||||
|
||||
// Passed to MakeWeak.
|
||||
static void HandleWeakDebugInfo(
|
||||
const v8::WeakCallbackData<v8::Value, void>& data);
|
||||
|
||||
friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc
|
||||
friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc
|
||||
|
||||
// Threading support.
|
||||
char* ArchiveDebug(char* to);
|
||||
char* RestoreDebug(char* from);
|
||||
static int ArchiveSpacePerThread();
|
||||
void FreeThreadResources() { }
|
||||
|
||||
// Script cache handling.
|
||||
void CreateScriptCache();
|
||||
void DestroyScriptCache();
|
||||
void AddScriptToScriptCache(Handle<Script> script);
|
||||
Handle<FixedArray> GetLoadedScripts();
|
||||
|
||||
// Record function from which eval was called.
|
||||
static void RecordEvalCaller(Handle<Script> script);
|
||||
|
||||
// Garbage collection notifications.
|
||||
void AfterGarbageCollection();
|
||||
Address step_in_fp_addr() {
|
||||
return reinterpret_cast<Address>(&thread_local_.step_into_fp_);
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Debug(Isolate* isolate);
|
||||
|
||||
void UpdateState();
|
||||
void Unload();
|
||||
|
||||
// Mirror cache handling.
|
||||
void ClearMirrorCache();
|
||||
|
||||
void SetNextBreakId() {
|
||||
thread_local_.break_id_ = ++thread_local_.break_count_;
|
||||
}
|
||||
|
||||
// Check whether there are commands in the command queue.
|
||||
inline bool has_commands() const { return !command_queue_.IsEmpty(); }
|
||||
inline bool ignore_events() const { return is_suppressed_ || !is_active_; }
|
||||
|
||||
// Constructors for debug event objects.
|
||||
MUST_USE_RESULT MaybeHandle<Object> MakeJSObject(
|
||||
@ -570,12 +547,16 @@ class Debug {
|
||||
Handle<Script> script, bool before);
|
||||
MUST_USE_RESULT MaybeHandle<Object> MakeScriptCollectedEvent(int id);
|
||||
|
||||
// Mirror cache handling.
|
||||
void ClearMirrorCache();
|
||||
|
||||
// Returns a promise if it does not have a reject handler.
|
||||
Handle<Object> GetPromiseForUncaughtException();
|
||||
|
||||
void CallEventCallback(v8::DebugEvent event,
|
||||
Handle<Object> exec_state,
|
||||
Handle<Object> event_data,
|
||||
v8::Debug::ClientData* client_data);
|
||||
|
||||
void ProcessDebugEvent(v8::DebugEvent event,
|
||||
Handle<JSObject> event_data,
|
||||
bool auto_continue);
|
||||
@ -583,17 +564,8 @@ class Debug {
|
||||
Handle<JSObject> exec_state,
|
||||
Handle<JSObject> event_data,
|
||||
bool auto_continue);
|
||||
|
||||
// Invoke the message handler function.
|
||||
void InvokeMessageHandler(MessageImpl message);
|
||||
|
||||
inline bool EventActive() {
|
||||
// Check whether the message handler was been cleared.
|
||||
// TODO(yangguo): handle loading and unloading of the debugger differently.
|
||||
// Currently argument event is not used.
|
||||
return !is_suppressed_ && is_active_;
|
||||
}
|
||||
|
||||
static bool CompileDebuggerScript(Isolate* isolate, int index);
|
||||
void ClearOneShot();
|
||||
void ActivateStepIn(StackFrame* frame);
|
||||
@ -605,28 +577,8 @@ class Debug {
|
||||
Handle<Object> CheckBreakPoints(Handle<Object> break_point);
|
||||
bool CheckBreakPoint(Handle<Object> break_point_object);
|
||||
|
||||
void EnsureFunctionHasDebugBreakSlots(Handle<JSFunction> function);
|
||||
void RecompileAndRelocateSuspendedGenerators(
|
||||
const List<Handle<JSGeneratorObject> > &suspended_generators);
|
||||
|
||||
void ThreadInit();
|
||||
|
||||
class PromiseOnStack {
|
||||
public:
|
||||
PromiseOnStack(Isolate* isolate,
|
||||
PromiseOnStack* prev,
|
||||
Handle<JSFunction> getter);
|
||||
~PromiseOnStack();
|
||||
StackHandler* handler() { return handler_; }
|
||||
Handle<JSFunction> getter() { return getter_; }
|
||||
PromiseOnStack* prev() { return prev_; }
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
StackHandler* handler_;
|
||||
Handle<JSFunction> getter_;
|
||||
PromiseOnStack* prev_;
|
||||
};
|
||||
|
||||
// Global handles.
|
||||
Handle<Context> debug_context_;
|
||||
Handle<Object> event_listener_;
|
||||
@ -692,9 +644,6 @@ class Debug {
|
||||
// step out action is completed.
|
||||
Address step_out_fp_;
|
||||
|
||||
// Pending interrupts scheduled while debugging.
|
||||
bool has_pending_interrupt_;
|
||||
|
||||
// Stores the way how LiveEdit has patched the stack. It is used when
|
||||
// debugger returns control back to user script.
|
||||
LiveEdit::FrameDropMode frame_drop_mode_;
|
||||
@ -723,6 +672,9 @@ class Debug {
|
||||
friend class DisableBreak;
|
||||
friend class SuppressDebug;
|
||||
|
||||
friend Handle<FixedArray> GetDebuggedFunctions(); // In test-debug.cc
|
||||
friend void CheckDebuggerUnloaded(bool check_functions); // In test-debug.cc
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Debug);
|
||||
};
|
||||
|
||||
@ -742,16 +694,12 @@ class EnterDebugger BASE_EMBEDDED {
|
||||
// Check whether the debugger could be entered.
|
||||
inline bool FailedToEnter() { return load_failed_; }
|
||||
|
||||
// Check whether there are any JavaScript frames on the stack.
|
||||
inline bool HasJavaScriptFrames() { return has_js_frames_; }
|
||||
|
||||
// Get the active context from before entering the debugger.
|
||||
inline Handle<Context> GetContext() { return save_.context(); }
|
||||
|
||||
private:
|
||||
Isolate* isolate_;
|
||||
EnterDebugger* prev_; // Previous debugger entry if entered recursively.
|
||||
bool has_js_frames_; // Were there any JavaScript frames?
|
||||
StackFrame::Id break_frame_id_; // Previous break frame id.
|
||||
int break_id_; // Previous break id.
|
||||
bool load_failed_; // Did the debugger fail to load?
|
||||
|
@ -667,7 +667,7 @@ Object* StackGuard::HandleInterrupts() {
|
||||
}
|
||||
|
||||
if (CheckDebugBreak() || CheckDebugCommand()) {
|
||||
isolate_->debug()->DebugBreakHelper();
|
||||
isolate_->debug()->HandleDebugBreak();
|
||||
}
|
||||
|
||||
if (CheckAndClearInterrupt(TERMINATE_EXECUTION)) {
|
||||
|
@ -2039,7 +2039,7 @@ Handle<DebugInfo> Factory::NewDebugInfo(Handle<SharedFunctionInfo> shared) {
|
||||
// debug info object to avoid allocation while setting up the debug info
|
||||
// object.
|
||||
Handle<FixedArray> break_points(
|
||||
NewFixedArray(Debug::kEstimatedNofBreakPointsInFunction));
|
||||
NewFixedArray(DebugInfo::kEstimatedNofBreakPointsInFunction));
|
||||
|
||||
// Create and set up the debug info object. Debug info contains function, a
|
||||
// copy of the original code, the executing code and initial fixed array for
|
||||
|
@ -1850,8 +1850,8 @@ static const char* DropActivationsInActiveThreadImpl(
|
||||
break;
|
||||
}
|
||||
}
|
||||
debug->FramesHaveBeenDropped(new_id, drop_mode,
|
||||
restarter_frame_function_pointer);
|
||||
debug->FramesHaveBeenDropped(
|
||||
new_id, drop_mode, restarter_frame_function_pointer);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -16696,7 +16696,7 @@ void DebugInfo::SetBreakPoint(Handle<DebugInfo> debug_info,
|
||||
Handle<FixedArray> new_break_points =
|
||||
isolate->factory()->NewFixedArray(
|
||||
old_break_points->length() +
|
||||
Debug::kEstimatedNofBreakPointsInFunction);
|
||||
DebugInfo::kEstimatedNofBreakPointsInFunction);
|
||||
|
||||
debug_info->set_break_points(*new_break_points);
|
||||
for (int i = 0; i < old_break_points->length(); i++) {
|
||||
|
@ -10882,6 +10882,8 @@ class DebugInfo: public Struct {
|
||||
kActiveBreakPointsCountIndex + kPointerSize;
|
||||
static const int kSize = kBreakPointsStateIndex + kPointerSize;
|
||||
|
||||
static const int kEstimatedNofBreakPointsInFunction = 16;
|
||||
|
||||
private:
|
||||
static const int kNoBreakPointInfo = -1;
|
||||
|
||||
|
@ -10698,7 +10698,7 @@ RUNTIME_FUNCTION(Runtime_LookupAccessor) {
|
||||
RUNTIME_FUNCTION(Runtime_DebugBreak) {
|
||||
SealHandleScope shs(isolate);
|
||||
ASSERT(args.length() == 0);
|
||||
isolate->debug()->DebugBreakHelper();
|
||||
isolate->debug()->HandleDebugBreak();
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user