From 94acd9251e8863c01adc30e864c04eb3833ca4eb Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Sat, 1 Aug 2015 15:54:59 -0400 Subject: [PATCH] Debugger: Add dedicated expression evaluation window. - Rather than dropping one-off expression evaluation results into the current team's variables view, they will now be handled in a dedicated window, which also allows to control the context in which the expression is evaluated. --- src/apps/debugger/Jamfile | 4 + .../ExpressionEvaluationWindow.cpp | 382 ++++++++++++++++++ .../ExpressionEvaluationWindow.h | 84 ++++ 3 files changed, 470 insertions(+) create mode 100644 src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.cpp create mode 100644 src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.h diff --git a/src/apps/debugger/Jamfile b/src/apps/debugger/Jamfile index 773da5f236..3e9e88618f 100644 --- a/src/apps/debugger/Jamfile +++ b/src/apps/debugger/Jamfile @@ -38,6 +38,7 @@ SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface cli ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface cli commands ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui ] ; +SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui expression_eval_window ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui inspector_window ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui model ] ; SEARCH_SOURCE += [ FDirName $(SUBDIR) user_interface gui team_window ] ; @@ -267,6 +268,9 @@ local sources = VariablesViewState.cpp VariablesViewStateHistory.cpp + # user_interface/gui/expression_eval_window + ExpressionEvaluationWindow.cpp + # user_interface/gui/inspector_window InspectorWindow.cpp MemoryView.cpp diff --git a/src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.cpp b/src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.cpp new file mode 100644 index 0000000000..b1877c447e --- /dev/null +++ b/src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.cpp @@ -0,0 +1,382 @@ +/* + * Copyright 2014-2015, Rene Gollent, rene@gollent.com. + * Distributed under the terms of the MIT License. + */ +#include "ExpressionEvaluationWindow.h" + +#include +#include +#include +#include +#include + +#include "AutoLocker.h" +#include "CppLanguage.h" +#include "FunctionDebugInfo.h" +#include "FunctionInstance.h" +#include "MessageCodes.h" +#include "SourceLanguage.h" +#include "SpecificImageDebugInfo.h" +#include "StackFrame.h" +#include "StackTrace.h" +#include "Thread.h" +#include "UiUtils.h" +#include "UserInterface.h" +#include "ValueNodeManager.h" + + +enum { + MSG_THREAD_SELECTION_CHANGED = 'thsc', + MSG_FRAME_SELECTION_CHANGED = 'frsc' +}; + + +ExpressionEvaluationWindow::ExpressionEvaluationWindow(BHandler* closeTarget, + ::Team* team, UserInterfaceListener* listener) + : + BWindow(BRect(), "Evaluate Expression", B_TITLED_WINDOW, + B_AUTO_UPDATE_SIZE_LIMITS | B_CLOSE_ON_ESCAPE), + fExpressionInput(NULL), + fThreadList(NULL), + fFrameList(NULL), + fVariablesView(NULL), + fCloseButton(NULL), + fEvaluateButton(NULL), + fCloseTarget(closeTarget), + fCurrentLanguage(NULL), + fFallbackLanguage(NULL), + fTeam(team), + fSelectedThread(NULL), + fSelectedFrame(NULL), + fListener(listener) +{ + team->AddListener(this); +} + + +ExpressionEvaluationWindow::~ExpressionEvaluationWindow() +{ + fTeam->RemoveListener(this); + + if (fCurrentLanguage != NULL) + fCurrentLanguage->ReleaseReference(); + + if (fFallbackLanguage != NULL) + fFallbackLanguage->ReleaseReference(); + + if (fSelectedThread != NULL) + fSelectedThread->ReleaseReference(); + + if (fSelectedFrame != NULL) + fSelectedFrame->ReleaseReference(); +} + + +ExpressionEvaluationWindow* +ExpressionEvaluationWindow::Create(BHandler* closeTarget, ::Team* team, + UserInterfaceListener* listener) +{ + ExpressionEvaluationWindow* self = new ExpressionEvaluationWindow( + closeTarget, team, listener); + + try { + self->_Init(); + } catch (...) { + delete self; + throw; + } + + return self; + +} + + +void +ExpressionEvaluationWindow::Show() +{ + CenterOnScreen(); + BWindow::Show(); +} + + +bool +ExpressionEvaluationWindow::QuitRequested() +{ + BMessenger messenger(fCloseTarget); + messenger.SendMessage(MSG_EXPRESSION_WINDOW_CLOSED); + + return BWindow::QuitRequested(); +} + + +void +ExpressionEvaluationWindow::MessageReceived(BMessage* message) +{ + switch (message->what) { + case MSG_THREAD_SELECTION_CHANGED: + { + int32 threadID; + if (message->FindInt32("thread", &threadID) != B_OK) + threadID = -1; + + _HandleThreadSelectionChanged(threadID); + break; + } + + case MSG_FRAME_SELECTION_CHANGED: + { + if (fSelectedThread == NULL) + break; + + int32 frameIndex; + if (message->FindInt32("frame", &frameIndex) != B_OK) + frameIndex = -1; + + _HandleFrameSelectionChanged(frameIndex); + break; + } + + case MSG_EVALUATE_EXPRESSION: + { + BMessage message(MSG_ADD_NEW_EXPRESSION); + message.AddString("expression", fExpressionInput->Text()); + BMessenger(fVariablesView).SendMessage(&message); + break; + } + + default: + BWindow::MessageReceived(message); + break; + } + +} + + +void +ExpressionEvaluationWindow::ValueNodeValueRequested(CpuState* cpuState, + ValueNodeContainer* container, ValueNode* valueNode) +{ + fListener->ValueNodeValueRequested(cpuState, container, valueNode); +} + + +void +ExpressionEvaluationWindow::ExpressionEvaluationRequested(ExpressionInfo* info, + StackFrame* frame, ::Thread* thread) +{ + SourceLanguage* language = fCurrentLanguage; + if (fCurrentLanguage == NULL) + language = fFallbackLanguage; + fListener->ExpressionEvaluationRequested(language, info, frame, thread); +} + + +void +ExpressionEvaluationWindow::ValueNodeWriteRequested(ValueNode* node, + CpuState* state, Value* newValue) +{ +} + + +void +ExpressionEvaluationWindow::_Init() +{ + ValueNodeManager* nodeManager = new ValueNodeManager(false); + fExpressionInput = new BTextControl("Expression:", NULL, NULL); + BLayoutItem* labelItem = fExpressionInput->CreateLabelLayoutItem(); + BLayoutItem* inputItem = fExpressionInput->CreateTextViewLayoutItem(); + inputItem->SetExplicitMinSize(BSize(200.0, B_SIZE_UNSET)); + inputItem->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET)); + labelItem->View()->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + + BLayoutBuilder::Group<>(this, B_VERTICAL) + .SetInsets(B_USE_DEFAULT_SPACING) + .AddGroup(B_HORIZONTAL, 4.0f) + .Add((fThreadList = new BMenuField("threadList", "Thread:", + new BMenu("Thread")))) + .Add((fFrameList = new BMenuField("frameList", "Frame:", + new BMenu("Frame")))) + .End() + .AddGroup(B_HORIZONTAL, 4.0f) + .Add(labelItem) + .Add(inputItem) + .End() + .Add(fVariablesView = VariablesView::Create(this, nodeManager)) + .AddGroup(B_HORIZONTAL, 4.0f) + .AddGlue() + .Add((fCloseButton = new BButton("Close", + new BMessage(B_QUIT_REQUESTED)))) + .Add((fEvaluateButton = new BButton("Evaluate", + new BMessage(MSG_EVALUATE_EXPRESSION)))) + .End(); + + fCloseButton->SetTarget(this); + fEvaluateButton->SetTarget(this); + fExpressionInput->TextView()->MakeFocus(true); + + fThreadList->Menu()->SetLabelFromMarked(true); + fFrameList->Menu()->SetLabelFromMarked(true); + + fThreadList->Menu()->AddItem(new BMenuItem("", + new BMessage(MSG_THREAD_SELECTION_CHANGED))); + fFrameList->Menu()->AddItem(new BMenuItem("", + new BMessage(MSG_FRAME_SELECTION_CHANGED))); + + _UpdateThreadList(); + + fFallbackLanguage = new CppLanguage(); +} + + +void +ExpressionEvaluationWindow::_HandleThreadSelectionChanged(int32 threadID) +{ + if (fSelectedThread != NULL) { + fSelectedThread->ReleaseReference(); + fSelectedThread = NULL; + } + + AutoLocker< ::Team> teamLocker(fTeam); + fSelectedThread = fTeam->ThreadByID(threadID); + if (fSelectedThread != NULL) + fSelectedThread->AcquireReference(); + + _UpdateFrameList(); + + fVariablesView->SetStackFrame(fSelectedThread, fSelectedFrame); +} + + +void +ExpressionEvaluationWindow::_HandleFrameSelectionChanged(int32 index) +{ + if (fSelectedFrame != NULL) { + fSelectedFrame->ReleaseReference(); + fSelectedFrame = NULL; + } + + if (fCurrentLanguage != NULL) { + fCurrentLanguage->ReleaseReference(); + fCurrentLanguage = NULL; + } + + AutoLocker< ::Team> teamLocker(fTeam); + StackTrace* stackTrace = fSelectedThread->GetStackTrace(); + if (stackTrace != NULL) { + fSelectedFrame = stackTrace->FrameAt(index); + if (fSelectedFrame != NULL) { + fSelectedFrame->AcquireReference(); + + FunctionInstance* instance = fSelectedFrame->Function(); + if (instance != NULL) { + FunctionDebugInfo* functionInfo + = instance->GetFunctionDebugInfo(); + SpecificImageDebugInfo* imageInfo = + functionInfo->GetSpecificImageDebugInfo(); + + if (imageInfo->GetSourceLanguage(functionInfo, + fCurrentLanguage) == B_OK) { + fCurrentLanguage->AcquireReference(); + } + } + } + } + + fVariablesView->SetStackFrame(fSelectedThread, fSelectedFrame); +} + + +void +ExpressionEvaluationWindow::_UpdateThreadList() +{ + AutoLocker< ::Team> teamLocker(fTeam); + + BMenu* frameMenu = fFrameList->Menu(); + while (frameMenu->CountItems() > 1) + delete frameMenu->RemoveItem(1); + + BMenu* threadMenu = fThreadList->Menu(); + while (threadMenu->CountItems() > 1) + delete threadMenu->RemoveItem(1); + + const ThreadList& threads = fTeam->Threads(); + for (ThreadList::ConstIterator it = threads.GetIterator(); + ::Thread* thread = it.Next();) { + if (thread->State() != THREAD_STATE_STOPPED) + continue; + + BString nameString; + nameString.SetToFormat("%" B_PRId32 ": %s", thread->ID(), + thread->Name()); + + BMessage* message = new(std::nothrow) BMessage( + MSG_THREAD_SELECTION_CHANGED); + if (message == NULL) + return; + + message->AddInt32("thread", thread->ID()); + + BMenuItem* item = new(std::nothrow) BMenuItem(nameString, + message); + if (item == NULL) + return; + + if (!threadMenu->AddItem(item)) + return; + + if (fSelectedThread == NULL) { + item->SetMarked(true); + _HandleThreadSelectionChanged(thread->ID()); + } + } + + if (fSelectedThread == NULL) + frameMenu->ItemAt(0L)->SetMarked(true); + +} + + +void +ExpressionEvaluationWindow::_UpdateFrameList() +{ + AutoLocker< ::Team> teamLocker(fTeam); + + BMenu* frameMenu = fFrameList->Menu(); + while (frameMenu->CountItems() > 1) + delete frameMenu->RemoveItem(1); + + frameMenu->ItemAt(0L)->SetMarked(true); + + if (fSelectedThread == NULL) + return; + + StackTrace* stackTrace = fSelectedThread->GetStackTrace(); + if (stackTrace == NULL) + return; + + char buffer[128]; + for (int32 i = 0; i < stackTrace->CountFrames(); i++) { + StackFrame* frame = stackTrace->FrameAt(i); + UiUtils::FunctionNameForFrame(frame, buffer, sizeof(buffer)); + + BMessage* message = new(std::nothrow) BMessage( + MSG_FRAME_SELECTION_CHANGED); + if (message == NULL) + return; + + message->AddInt32("frame", i); + + BMenuItem* item = new(std::nothrow) BMenuItem(buffer, + message); + if (item == NULL) + return; + + if (!frameMenu->AddItem(item)) + return; + + if (fSelectedFrame == NULL) { + item->SetMarked(true); + _HandleFrameSelectionChanged(i); + } + } +} diff --git a/src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.h b/src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.h new file mode 100644 index 0000000000..f104f212b8 --- /dev/null +++ b/src/apps/debugger/user_interface/gui/expression_eval_window/ExpressionEvaluationWindow.h @@ -0,0 +1,84 @@ +/* + * Copyright 2014-2015, Rene Gollent, rene@gollent.com. + * Distributed under the terms of the MIT License. + */ +#ifndef EXPRESSION_EVALUATION_WINDOW_H +#define EXPRESSION_EVALUATION_WINDOW_H + + +#include + +#include "Team.h" +#include "VariablesView.h" + + +class BButton; +class BMenuField; +class BTextControl; +class StackFrame; +class Team; +class UserInterfaceListener; +class SourceLanguage; + +class ExpressionEvaluationWindow : public BWindow, private Team::Listener, + private VariablesView::Listener { +public: + ExpressionEvaluationWindow( + BHandler* fCloseTarget, + ::Team* team, + UserInterfaceListener* listener); + + ~ExpressionEvaluationWindow(); + + static ExpressionEvaluationWindow* Create(BHandler* fCloseTarget, + ::Team* team, + UserInterfaceListener* listener); + // throws + + + virtual void Show(); + virtual bool QuitRequested(); + + virtual void MessageReceived(BMessage* message); + + // VariablesView::Listener + virtual void ValueNodeValueRequested(CpuState* cpuState, + ValueNodeContainer* container, + ValueNode* valueNode); + + virtual void ExpressionEvaluationRequested( + ExpressionInfo* info, + StackFrame* frame, + ::Thread* thread); + + virtual void ValueNodeWriteRequested( + ValueNode* node, + CpuState* state, + Value* newValue); + +private: + void _Init(); + + void _HandleThreadSelectionChanged(int32 threadID); + void _HandleFrameSelectionChanged(int32 index); + + void _UpdateThreadList(); + void _UpdateFrameList(); + +private: + BTextControl* fExpressionInput; + BMenuField* fThreadList; + BMenuField* fFrameList; + VariablesView* fVariablesView; + BButton* fCloseButton; + BButton* fEvaluateButton; + BHandler* fCloseTarget; + SourceLanguage* fCurrentLanguage; + SourceLanguage* fFallbackLanguage; + ::Team* fTeam; + ::Thread* fSelectedThread; + StackFrame* fSelectedFrame; + UserInterfaceListener* fListener; +}; + +#endif // EXPRESSION_EVALUATION_WINDOW_H