// Expose the debug global object in global if a name for it is specified.
if (FLAG_expose_debug_as != NULL && strlen(FLAG_expose_debug_as) != 0) {
- Debug* debug = isolate->debug();
// If loading fails we just bail out without installing the
// debugger but without tanking the whole context.
+ Debug* debug = isolate->debug();
if (!debug->Load()) return true;
+ Handle<Context> debug_context = debug->debug_context();
// Set the security token for the debugger context to the same as
// the shell native context to allow calling between these (otherwise
// exposing debug global object doesn't make much sense).
- debug->debug_context()->set_security_token(
- native_context->security_token());
-
+ debug_context->set_security_token(native_context->security_token());
Handle<String> debug_string =
factory->InternalizeUtf8String(FLAG_expose_debug_as);
- Handle<Object> global_proxy(
- debug->debug_context()->global_proxy(), isolate);
+ Handle<Object> global_proxy(debug_context->global_proxy(), isolate);
RETURN_ON_EXCEPTION_VALUE(
isolate,
JSObject::SetLocalPropertyIgnoreAttributes(
// Install the debugger object in the utility scope
i::Debug* debug = reinterpret_cast<i::Isolate*>(isolate)->debug();
debug->Load();
+ i::Handle<i::Context> debug_context = debug->debug_context();
i::Handle<i::JSObject> js_debug
- = i::Handle<i::JSObject>(debug->debug_context()->global_object());
+ = i::Handle<i::JSObject>(debug_context->global_object());
utility_context->Global()->Set(String::NewFromUtf8(isolate, "$debug"),
Utils::ToLocal(js_debug));
- debug->debug_context()->set_security_token(
+ debug_context->set_security_token(
reinterpret_cast<i::Isolate*>(isolate)->heap()->undefined_value());
// Run the d8 shell utility script in the utility context
}
-Debug::~Debug() {
-}
-
-
static v8::Handle<v8::Context> GetDebugEventContext(Isolate* isolate) {
Handle<Context> context = isolate->debug()->debugger_entry()->GetContext();
// Isolate::context() may have been NULL when "script collected" event
HandleScope scope(isolate);
// Bail out if the index is invalid.
- if (index == -1) {
- return false;
- }
+ if (index == -1) return false;
// Find source and name for the requested script.
Handle<String> source_code =
// Return if debugger is already loaded.
if (IsLoaded()) return true;
- Debugger* debugger = isolate_->debugger();
-
// Bail out if we're already in the process of compiling the native
// JavaScript source code for the debugger.
- if (debugger->ignore_debugger()) return false;
+ if (isolate_->debugger()->ignore_debugger()) return false;
+ Debugger::IgnoreScope during_create(isolate_->debugger());
- Debugger::IgnoreScope during_load(debugger);
// Disable breakpoints and interrupts while compiling and running the
// debugger scripts including the context creation code.
DisableBreak disable(isolate_, true);
// Expose the builtins object in the debugger context.
Handle<String> key = isolate_->factory()->InternalizeOneByteString(
STATIC_ASCII_VECTOR("builtins"));
- Handle<GlobalObject> global = Handle<GlobalObject>(context->global_object());
+ Handle<GlobalObject> global =
+ Handle<GlobalObject>(context->global_object(), isolate_);
+ Handle<JSBuiltinsObject> builtin =
+ Handle<JSBuiltinsObject>(global->builtins(), isolate_);
RETURN_ON_EXCEPTION_VALUE(
isolate_,
- JSReceiver::SetProperty(global,
- key,
- Handle<Object>(global->builtins(), isolate_),
- NONE,
- SLOPPY),
+ JSReceiver::SetProperty(global, key, builtin, NONE, SLOPPY),
false);
// Compile the JavaScript for the debugger in the debugger context.
caught_exception = caught_exception ||
!CompileDebuggerScript(isolate_, Natives::GetIndex("liveedit"));
}
-
- // Make sure we mark the debugger as not loading before we might
- // return.
-
// Check for caught exceptions.
if (caught_exception) return false;
- // Debugger loaded, create debugger context global handle.
debug_context_ = Handle<Context>::cast(
isolate_->global_handles()->Create(*context));
-
return true;
}
void Debug::Unload() {
// Return debugger is not loaded.
- if (!IsLoaded()) {
- return;
- }
+ if (!IsLoaded()) return;
// Clear the script cache.
DestroyScriptCache();
// Clear debugger context global handle.
- GlobalHandles::Destroy(reinterpret_cast<Object**>(debug_context_.location()));
+ GlobalHandles::Destroy(Handle<Object>::cast(debug_context_).location());
debug_context_ = Handle<Context>();
}
JavaScriptFrame* frame = it.frame();
// Just continue if breaks are disabled or debugger cannot be loaded.
- if (disable_break() || !Load()) {
+ if (disable_break()) {
SetAfterBreakTarget(frame);
return heap->undefined_value();
}
is_active_(false),
ignore_debugger_(false),
live_edit_enabled_(true),
- never_unload_debugger_(false),
message_handler_(NULL),
- debugger_unload_pending_(false),
command_queue_(isolate->logger(), kQueueInitialSize),
command_received_(0),
event_command_queue_(isolate->logger(), kQueueInitialSize),
Handle<Context> Debugger::GetDebugContext() {
- never_unload_debugger_ = true;
EnterDebugger debugger(isolate_);
return isolate_->debug()->debug_context();
}
-void Debugger::UnloadDebugger() {
- Debug* debug = isolate_->debug();
-
- // Make sure that there are no breakpoints left.
- debug->ClearAllBreakPoints();
-
- // Unload the debugger if feasible.
- if (!never_unload_debugger_) {
- debug->Unload();
- }
-
- // Clear the flag indicating that the debugger should be unloaded.
- debugger_unload_pending_ = false;
-}
-
-
void Debugger::NotifyMessageHandler(v8::DebugEvent event,
Handle<JSObject> exec_state,
Handle<JSObject> event_data,
bool auto_continue) {
+ ASSERT(is_active_);
HandleScope scope(isolate_);
-
- if (!isolate_->debug()->Load()) return;
-
// Process the individual events.
bool sendEventMessage = false;
switch (event) {
void Debugger::SetEventListener(Handle<Object> callback,
Handle<Object> data) {
- HandleScope scope(isolate_);
GlobalHandles* global_handles = isolate_->global_handles();
- // Clear the global handles for the event listener and the event listener data
- // object.
- if (!event_listener_.is_null()) {
- GlobalHandles::Destroy(
- reinterpret_cast<Object**>(event_listener_.location()));
- event_listener_ = Handle<Object>();
- }
- if (!event_listener_data_.is_null()) {
- GlobalHandles::Destroy(
- reinterpret_cast<Object**>(event_listener_data_.location()));
- event_listener_data_ = Handle<Object>();
- }
+ // Remove existing entry.
+ GlobalHandles::Destroy(event_listener_.location());
+ event_listener_ = Handle<Object>();
+ GlobalHandles::Destroy(event_listener_data_.location());
+ event_listener_data_ = Handle<Object>();
- // If there is a new debug event listener register it together with its data
- // object.
+ // Set new entry.
if (!callback->IsUndefined() && !callback->IsNull()) {
- event_listener_ = Handle<Object>::cast(
- global_handles->Create(*callback));
- if (data.is_null()) {
- data = isolate_->factory()->undefined_value();
- }
- event_listener_data_ = Handle<Object>::cast(
- global_handles->Create(*data));
+ event_listener_ = global_handles->Create(*callback);
+ if (data.is_null()) data = isolate_->factory()->undefined_value();
+ event_listener_data_ = global_handles->Create(*data);
}
- ListenersChanged();
+ UpdateState();
}
void Debugger::SetMessageHandler(v8::Debug::MessageHandler handler) {
- LockGuard<RecursiveMutex> with(&debugger_access_);
-
message_handler_ = handler;
- ListenersChanged();
- if (handler == NULL) {
+ UpdateState();
+ if (handler == NULL && isolate_->debug()->InDebugger()) {
// Send an empty command to the debugger if in a break to make JavaScript
// run again if the debugger is closed.
- if (isolate_->debug()->InDebugger()) {
- EnqueueCommandMessage(Vector<const uint16_t>::empty());
- }
+ EnqueueCommandMessage(Vector<const uint16_t>::empty());
}
}
-void Debugger::ListenersChanged() {
- LockGuard<RecursiveMutex> with(&debugger_access_);
- is_active_ = message_handler_ != NULL || !event_listener_.is_null();
- if (is_active_) {
- // Disable the compilation cache when the debugger is active.
+void Debugger::UpdateState() {
+ bool activate = message_handler_ != NULL ||
+ !event_listener_.is_null() ||
+ isolate_->debug()->InDebugger();
+ if (!is_active_ && activate) {
+ // Note that the debug context could have already been loaded to
+ // bootstrap test cases.
isolate_->compilation_cache()->Disable();
- debugger_unload_pending_ = false;
- } else {
+ activate = isolate_->debug()->Load();
+ } else if (is_active_ && !activate) {
isolate_->compilation_cache()->Enable();
- // Unload the debugger if event listener and message handler cleared.
- // Schedule this for later, because we may be in non-V8 thread.
- debugger_unload_pending_ = true;
+ isolate_->debug()->ClearAllBreakPoints();
+ isolate_->debug()->Unload();
}
+ is_active_ = activate;
+ // At this point the debug context is loaded iff the debugger is active.
+ ASSERT(isolate_->debug()->IsLoaded() == is_active_);
}
// Calls the registered debug message handler. This callback is part of the
// public API.
void Debugger::InvokeMessageHandler(MessageImpl message) {
- LockGuard<RecursiveMutex> with(&debugger_access_);
-
- if (message_handler_ != NULL) {
- message_handler_(message);
- }
+ if (message_handler_ != NULL) message_handler_(message);
}
MaybeHandle<Object> Debugger::Call(Handle<JSFunction> fun,
Handle<Object> data) {
- // When calling functions in the debugger prevent it from beeing unloaded.
- Debugger::never_unload_debugger_ = true;
-
// Enter the debugger.
EnterDebugger debugger(isolate_);
if (debugger.FailedToEnter()) {
EnterDebugger::EnterDebugger(Isolate* isolate)
: isolate_(isolate),
prev_(isolate_->debug()->debugger_entry()),
- it_(isolate_),
- has_js_frames_(!it_.done()),
save_(isolate_) {
Debug* debug = isolate_->debug();
+
// Link recursive debugger entry.
debug->set_debugger_entry(this);
// Create the new break info. If there is no JavaScript frames there is no
// break frame id.
- if (has_js_frames_) {
- debug->NewBreak(it_.frame()->id());
- } else {
- debug->NewBreak(StackFrame::NO_ID);
- }
+ JavaScriptFrameIterator it(isolate_);
+ has_js_frames_ = !it.done();
+ debug->NewBreak(has_js_frames_ ? it.frame()->id() : StackFrame::NO_ID);
+ isolate_->debugger()->UpdateState();
// Make sure that debugger is loaded and enter the debugger context.
- load_failed_ = !debug->Load();
- if (!load_failed_) {
- // NOTE the member variable save which saves the previous context before
- // this change.
- isolate_->set_context(*debug->debug_context());
- }
+ // The previous context is kept in save_.
+ load_failed_ = !debug->IsLoaded();
+ if (!load_failed_) isolate_->set_context(*debug->debug_context());
}
EnterDebugger::~EnterDebugger() {
Debug* debug = isolate_->debug();
+ // Leaving this debugger entry.
+ debug->set_debugger_entry(prev_);
+
// Restore to the previous break state.
debug->SetBreak(break_frame_id_, break_id_);
if (isolate_->debugger()->HasCommands()) {
isolate_->stack_guard()->RequestDebugCommand();
}
-
- // If leaving the debugger with the debugger no longer active unload it.
- if (!isolate_->debugger()->is_active()) {
- isolate_->debugger()->UnloadDebugger();
- }
}
- // Leaving this debugger entry.
- debug->set_debugger_entry(prev_);
+ isolate_->debugger()->UpdateState();
}
private:
explicit Debug(Isolate* isolate);
- ~Debug();
static bool CompileDebuggerScript(Isolate* isolate, int index);
void ClearOneShot();
class Debugger {
public:
- ~Debugger();
-
- void DebugRequest(const uint16_t* json_request, int length);
-
- MUST_USE_RESULT MaybeHandle<Object> MakeJSObject(
- Vector<const char> constructor_name,
- int argc,
- Handle<Object> argv[]);
- MUST_USE_RESULT MaybeHandle<Object> MakeExecutionState();
- MUST_USE_RESULT MaybeHandle<Object> MakeBreakEvent(
- Handle<Object> break_points_hit);
- MUST_USE_RESULT MaybeHandle<Object> MakeExceptionEvent(
- Handle<Object> exception,
- bool uncaught,
- Handle<Object> promise);
- MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent(
- Handle<Script> script, bool before);
- MUST_USE_RESULT MaybeHandle<Object> MakeScriptCollectedEvent(int id);
-
void OnDebugBreak(Handle<Object> break_points_hit, bool auto_continue);
void OnException(Handle<Object> exception, bool uncaught);
void OnBeforeCompile(Handle<Script> script);
void OnAfterCompile(Handle<Script> script,
AfterCompileFlags after_compile_flags);
void OnScriptCollected(int id);
- void ProcessDebugEvent(v8::DebugEvent event,
- Handle<JSObject> event_data,
- bool auto_continue);
- void NotifyMessageHandler(v8::DebugEvent event,
- Handle<JSObject> exec_state,
- Handle<JSObject> event_data,
- bool auto_continue);
+
void SetEventListener(Handle<Object> callback, Handle<Object> data);
void SetMessageHandler(v8::Debug::MessageHandler handler);
Handle<Context> GetDebugContext();
- // Unload the debugger if possible. Only called when no debugger is currently
- // active.
- void UnloadDebugger();
- friend void ForceUnloadDebugger(); // In test-debug.cc
-
- inline bool EventActive() {
- LockGuard<RecursiveMutex> lock_guard(&debugger_access_);
-
- // Check whether the message handler was been cleared.
- // TODO(yangguo): handle loading and unloading of the debugger differently.
- if (debugger_unload_pending_) {
- if (isolate_->debug()->debugger_entry() == NULL) {
- UnloadDebugger();
- }
- }
-
- // Currently argument event is not used.
- return !ignore_debugger_ && is_active_;
- }
-
bool ignore_debugger() const { return ignore_debugger_; }
void set_live_edit_enabled(bool v) { live_edit_enabled_ = v; }
bool live_edit_enabled() const {
return FLAG_enable_liveedit && live_edit_enabled_ ;
}
- bool is_active() {
- LockGuard<RecursiveMutex> lock_guard(&debugger_access_);
- return is_active_;
- }
+ bool is_active() { return is_active_; }
class IgnoreScope {
public:
private:
explicit Debugger(Isolate* isolate);
+ ~Debugger();
+
+ MUST_USE_RESULT MaybeHandle<Object> MakeJSObject(
+ Vector<const char> constructor_name,
+ int argc,
+ Handle<Object> argv[]);
+ MUST_USE_RESULT MaybeHandle<Object> MakeExecutionState();
+ MUST_USE_RESULT MaybeHandle<Object> MakeBreakEvent(
+ Handle<Object> break_points_hit);
+ MUST_USE_RESULT MaybeHandle<Object> MakeExceptionEvent(
+ Handle<Object> exception,
+ bool uncaught,
+ Handle<Object> promise);
+ MUST_USE_RESULT MaybeHandle<Object> MakeCompileEvent(
+ Handle<Script> script, bool before);
+ MUST_USE_RESULT MaybeHandle<Object> MakeScriptCollectedEvent(int id);
void CallEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
void CallJSEventCallback(v8::DebugEvent event,
Handle<Object> exec_state,
Handle<Object> event_data);
- void ListenersChanged();
+ void UpdateState();
+
+ void ProcessDebugEvent(v8::DebugEvent event,
+ Handle<JSObject> event_data,
+ bool auto_continue);
+ void NotifyMessageHandler(v8::DebugEvent event,
+ Handle<JSObject> exec_state,
+ Handle<JSObject> event_data,
+ bool auto_continue);
// Invoke the message handler function.
void InvokeMessageHandler(MessageImpl message);
- RecursiveMutex debugger_access_; // Mutex guarding debugger variables.
+ 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 !ignore_debugger_ && is_active_;
+ }
+
Handle<Object> event_listener_; // Global handle to listener.
Handle<Object> event_listener_data_;
bool is_active_;
bool ignore_debugger_; // Are we temporarily ignoring the debugger?
bool live_edit_enabled_; // Enable LiveEdit.
- bool never_unload_debugger_; // Can we unload the debugger?
v8::Debug::MessageHandler message_handler_;
bool debugger_unload_pending_; // Was message handler cleared?
private:
Isolate* isolate_;
EnterDebugger* prev_; // Previous debugger entry if entered recursively.
- JavaScriptFrameIterator it_;
- const bool has_js_frames_; // Were there any JavaScript frames?
+ 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?
void Execution::ProcessDebugMessages(Isolate* isolate,
bool debug_command_only) {
- // Assert that we are on the main thread of the isolate.
- ASSERT(ThreadId::Current().Equals(isolate->thread_id()));
-
isolate->stack_guard()->ClearDebugCommand();
StackLimitCheck check(isolate);
if (state_ == INITIALIZED) {
TRACE_ISOLATE(deinit);
- debugger()->UnloadDebugger();
-
if (concurrent_recompilation_enabled()) {
optimizing_compiler_thread_->Stop();
delete optimizing_compiler_thread_;
static bool CheckExecutionState(Isolate* isolate, int break_id) {
- return (isolate->debug()->break_id() != 0 &&
- break_id == isolate->debug()->break_id());
+ return !isolate->debug()->debug_context().is_null() &&
+ isolate->debug()->break_id() != 0 &&
+ isolate->debug()->break_id() == break_id;
}
v8::internal::Isolate* isolate =
reinterpret_cast<v8::internal::Isolate*>(context_->GetIsolate());
v8::internal::Factory* factory = isolate->factory();
- v8::internal::Debug* debug = isolate->debug();
// Expose the debug context global object in the global object for testing.
- debug->Load();
- debug->debug_context()->set_security_token(
+ CHECK(isolate->debug()->Load());
+ Handle<v8::internal::Context> debug_context =
+ isolate->debug()->debug_context();
+ debug_context->set_security_token(
v8::Utils::OpenHandle(*context_)->security_token());
Handle<JSGlobalProxy> global(Handle<JSGlobalProxy>::cast(
Handle<v8::internal::String> debug_string =
factory->InternalizeOneByteString(STATIC_ASCII_VECTOR("debug"));
v8::internal::Runtime::SetObjectProperty(isolate, global, debug_string,
- Handle<Object>(debug->debug_context()->global_proxy(), isolate),
+ Handle<Object>(debug_context->global_proxy(), isolate),
DONT_ENUM,
::v8::internal::SLOPPY).Check();
}
}
-void ForceUnloadDebugger() {
- CcTest::i_isolate()->debugger()->never_unload_debugger_ = false;
- CcTest::i_isolate()->debugger()->UnloadDebugger();
-}
-
-
} } // namespace v8::internal
v8::String::NewFromUtf8(args.GetIsolate(), "Test");
CHECK(v8::Debug::Call(debugger_call_with_data, data)->IsString());
- CHECK(v8::Debug::Call(debugger_call_with_data).IsEmpty());
- CHECK(v8::Debug::Call(debugger_call_with_data).IsEmpty());
-
- v8::TryCatch catcher;
- v8::Debug::Call(debugger_call_with_data);
- CHECK(catcher.HasCaught());
- CHECK(catcher.Exception()->IsString());
+ for (int i = 0; i < 3; i++) {
+ v8::TryCatch catcher;
+ CHECK(v8::Debug::Call(debugger_call_with_data).IsEmpty());
+ CHECK(catcher.HasCaught());
+ CHECK(catcher.Exception()->IsString());
+ }
}
TEST(DebugContextIsPreservedBetweenAccesses) {
v8::HandleScope scope(CcTest::isolate());
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount);
v8::Local<v8::Context> context1 = v8::Debug::GetDebugContext();
v8::Local<v8::Context> context2 = v8::Debug::GetDebugContext();
CHECK_EQ(*context1, *context2);
+ v8::Debug::SetDebugEventListener(NULL);
}
v8::HandleScope scope(env->GetIsolate());
v8::HeapProfiler* heap_profiler = env->GetIsolate()->GetHeapProfiler();
- CcTest::i_isolate()->debug()->Load();
+ CHECK(CcTest::i_isolate()->debug()->Load());
CompileRun("foo = {};");
const v8::HeapSnapshot* snapshot =
heap_profiler->TakeHeapSnapshot(v8_str("snapshot"));
var exception = false;
var codeSnippet = undefined;
var resultPositions = undefined;
+ var step = 0;
function listener(event, exec_state, event_data, data) {
try {
if (event == Debug.DebugEvent.Break ||
event == Debug.DebugEvent.Exception) {
- Debug.setListener(null);
+ if (step++ > 0) return;
assertHasLineMark(/pause/, exec_state.frame(0));
assertHasLineMark(/positions/, exec_state.frame(frame_number));
var frame = exec_state.frame(frame_number);