From b7e72db3cc4ff5bb929a83cdf8a47fbd347615ed Mon Sep 17 00:00:00 2001 From: Rene Gollent Date: Sun, 2 Nov 2014 09:14:31 -0500 Subject: [PATCH] Debugger: Implement first part of #11387. VariablesView: - The view now keeps a mapping of a list of expressions associated with functions. - Add actions for adding/removing expressions to variables context menu. - When setting up to show a new stack frame, also add corresponding expression nodes and request their evaluation. - Extend VariablesView::Listener interface to also encompass expression evaluation requests. - Don't add context actions for expression nodes. TeamWindow: - Implement VariablesView::Listener expression evaluation hook and forward responses along. --- .../gui/team_window/TeamWindow.cpp | 41 ++ .../gui/team_window/TeamWindow.h | 9 + .../gui/team_window/VariablesView.cpp | 558 ++++++++++++++++-- .../gui/team_window/VariablesView.h | 35 +- 4 files changed, 586 insertions(+), 57 deletions(-) diff --git a/src/apps/debugger/user_interface/gui/team_window/TeamWindow.cpp b/src/apps/debugger/user_interface/gui/team_window/TeamWindow.cpp index f1e54b7352..404b044794 100644 --- a/src/apps/debugger/user_interface/gui/team_window/TeamWindow.cpp +++ b/src/apps/debugger/user_interface/gui/team_window/TeamWindow.cpp @@ -58,6 +58,7 @@ #include "TypeComponentPath.h" #include "UiUtils.h" #include "UserInterface.h" +#include "Value.h" #include "Variable.h" #include "WatchPromptWindow.h" @@ -906,6 +907,20 @@ TeamWindow::ValueNodeValueRequested(CpuState* cpuState, } +void +TeamWindow::ExpressionEvaluationRequested(const char* expression, + type_code resultType, StackFrame* frame, ::Thread* thread) +{ + SourceLanguage* language; + if (_GetActiveSourceLanguage(language) != B_OK) + return; + + BReference languageReference(language, true); + fListener->ExpressionEvaluationRequested(language, expression, resultType, + frame, thread); +} + + void TeamWindow::ThreadStateChanged(const Team::ThreadEvent& event) { @@ -976,6 +991,32 @@ TeamWindow::WatchpointChanged(const Team::WatchpointEvent& event) } +void +TeamWindow::ExpressionEvaluated(const Team::ExpressionEvaluationEvent& event) +{ + BMessage message(MSG_EXPRESSION_EVALUATED); + if (message.AddString("expression", event.GetExpression()) != B_OK + || message.AddInt32("result", event.GetResult()) != B_OK) { + return; + } + + BReference reference; + Value* value = event.GetValue(); + if (value != NULL) { + if (message.AddPointer("value", value) != B_OK) + return; + reference.SetTo(value); + } + + // currently, the only circumstance in which TeamWindow cares about + // expression evaluation results is when handling them on behalf of + // VariablesView. As such, simply forward them on. + BMessenger messenger(fVariablesView); + if (messenger.SendMessage(&message) == B_OK) + reference.Detach(); +} + + void TeamWindow::DebugReportChanged(const Team::DebugReportEvent& event) { diff --git a/src/apps/debugger/user_interface/gui/team_window/TeamWindow.h b/src/apps/debugger/user_interface/gui/team_window/TeamWindow.h index 617187eea7..8f9c18a7c8 100644 --- a/src/apps/debugger/user_interface/gui/team_window/TeamWindow.h +++ b/src/apps/debugger/user_interface/gui/team_window/TeamWindow.h @@ -127,6 +127,11 @@ private: virtual void ValueNodeValueRequested(CpuState* cpuState, ValueNodeContainer* container, ValueNode* valueNode); + virtual void ExpressionEvaluationRequested( + const char* expression, + type_code resultType, + StackFrame* frame, + ::Thread* thread); // Team::Listener virtual void ThreadStateChanged( @@ -143,9 +148,13 @@ private: const Team::UserBreakpointEvent& event); virtual void WatchpointChanged( const Team::WatchpointEvent& event); + virtual void ExpressionEvaluated( + const Team::ExpressionEvaluationEvent& + event); virtual void DebugReportChanged( const Team::DebugReportEvent& event); + // Function::Listener virtual void FunctionSourceCodeChanged(Function* function); diff --git a/src/apps/debugger/user_interface/gui/team_window/VariablesView.cpp b/src/apps/debugger/user_interface/gui/team_window/VariablesView.cpp index 78943fd7c5..d8e7815c86 100644 --- a/src/apps/debugger/user_interface/gui/team_window/VariablesView.cpp +++ b/src/apps/debugger/user_interface/gui/team_window/VariablesView.cpp @@ -25,6 +25,9 @@ #include "ActionMenuItem.h" #include "Architecture.h" +#include "ExpressionInfo.h" +#include "ExpressionValueNode.h" +#include "ExpressionValues.h" #include "FileSourceCode.h" #include "Function.h" #include "FunctionID.h" @@ -38,6 +41,7 @@ #include "StackTrace.h" #include "StackFrame.h" #include "StackFrameValues.h" +#include "SyntheticPrimitiveType.h" #include "TableCellValueRenderer.h" #include "Team.h" #include "TeamDebugInfo.h" @@ -67,7 +71,9 @@ enum { enum { MSG_MODEL_NODE_HIDDEN = 'monh', MSG_VALUE_NODE_NEEDS_VALUE = 'mvnv', - MSG_RESTORE_PARTIAL_VIEW_STATE = 'mpvs' + MSG_RESTORE_PARTIAL_VIEW_STATE = 'mpvs', + MSG_ADD_WATCH_EXPRESSION = 'awex', + MSG_REMOVE_WATCH_EXPRESSION = 'rwex' }; @@ -75,6 +81,106 @@ enum { static const uint64 kMaxArrayElementCount = 10; +// #pragma mark - FunctionKey + + +struct VariablesView::FunctionKey { + FunctionID* function; + + FunctionKey(FunctionID* function) + : + function(function) + { + } + + uint32 HashValue() const + { + return function->HashValue(); + } + + bool operator==(const FunctionKey& other) const + { + return *function == *other.function; + } +}; + + +// #pragma mark - ExpressionInfoEntry + + +struct VariablesView::ExpressionInfoEntry : FunctionKey, ExpressionInfoList { + ExpressionInfoEntry* next; + + ExpressionInfoEntry(FunctionID* function) + : + FunctionKey(function), + ExpressionInfoList(10, false) + { + function->AcquireReference(); + } + + ~ExpressionInfoEntry() + { + _Cleanup(); + } + + void SetInfo(const ExpressionInfoList& infoList) + { + _Cleanup(); + + for (int32 i = 0; i < infoList.CountItems(); i++) { + ExpressionInfo* info = infoList.ItemAt(i); + if (!AddItem(info)) + break; + + info->AcquireReference(); + } + } + +private: + void _Cleanup() + { + for (int32 i = 0; i < CountItems(); i++) + ItemAt(i)->ReleaseReference(); + + MakeEmpty(); + } +}; + + +// #pragma mark - ExpressionInfoEntryHashDefinition + + +struct VariablesView::ExpressionInfoEntryHashDefinition { + typedef FunctionKey KeyType; + typedef ExpressionInfoEntry ValueType; + + size_t HashKey(const FunctionKey& key) const + { + return key.HashValue(); + } + + size_t Hash(const ExpressionInfoEntry* value) const + { + return value->HashValue(); + } + + bool Compare(const FunctionKey& key, + const ExpressionInfoEntry* value) const + { + return key == *value; + } + + ExpressionInfoEntry*& GetLink(ExpressionInfoEntry* value) const + { + return value->next; + } +}; + + +// #pragma mark - ContainerListener + + class VariablesView::ContainerListener : public ValueNodeContainer::Listener { public: ContainerListener(BHandler* indirectTarget); @@ -99,6 +205,9 @@ private: }; +// #pragma mark - ModelNode + + class VariablesView::ModelNode : public BReferenceable { public: ModelNode(ModelNode* parent, Variable* variable, ValueNodeChild* nodeChild, @@ -377,6 +486,7 @@ private: bool fIsPresentationNode; bool fHidden; bool fValueChanged; + bool fIsExpression; public: ModelNode* fNext; @@ -483,6 +593,9 @@ public: const TreeTablePath& path, int32 columnIndex, BToolTip** _tip); + status_t AddSyntheticNode(ModelNode* node); + void RemoveSyntheticNode(ModelNode* node); + private: struct NodeHashDefinition { typedef ValueNodeChild* KeyType; @@ -1353,6 +1466,47 @@ VariablesView::VariableTableModel::GetToolTipForTablePath( } +status_t +VariablesView::VariableTableModel::AddSyntheticNode(ModelNode* node) +{ + status_t error = node->Init(); + if (error != B_OK) + return error; + + int32 index = fNodes.CountItems(); + + if (!fNodes.AddItem(node)) { + return B_NO_MEMORY; + // NB: we take over the caller's reference + } + + fNodeTable.Insert(node); + + node->NodeChild()->Node()->SetContainer(fNodeManager->GetContainer()); + + NotifyNodesAdded(TreeTablePath(), index, 1); + + return B_OK; +} + + +void +VariablesView::VariableTableModel::RemoveSyntheticNode(ModelNode* node) +{ + int32 index = fNodes.IndexOf(node); + if (index < 0) + return; + + fNodeTable.Remove(node); + + fNodes.RemoveItemAt(index); + + NotifyNodesRemoved(TreeTablePath(), index, 1); + + node->ReleaseReference(); +} + + status_t VariablesView::VariableTableModel::_AddNode(Variable* variable, ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode, @@ -1460,6 +1614,7 @@ VariablesView::VariablesView(Listener* listener) fContainerListener(NULL), fPreviousViewState(NULL), fViewStateHistory(NULL), + fExpressions(NULL), fTableCellContextMenuTracker(NULL), fFrameClearPending(false), fListener(listener) @@ -1539,6 +1694,8 @@ VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame) ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i); _RequestNodeValue(node); } + + _RestoreExpressionNodes(); } _RestoreViewState(); @@ -1759,6 +1916,66 @@ VariablesView::MessageReceived(BMessage* message) Looper()->PostMessage(&looperMessage); break; } + case MSG_ADD_WATCH_EXPRESSION: + { + BMessage looperMessage(MSG_SHOW_EXPRESSION_PROMPT_WINDOW); + looperMessage.AddPointer("target", this); + Looper()->PostMessage(&looperMessage); + break; + } + case MSG_REMOVE_WATCH_EXPRESSION: + { + ModelNode* node; + if (message->FindPointer("node", reinterpret_cast(&node)) + != B_OK) { + break; + } + + _RemoveExpression(node); + break; + } + case MSG_ADD_NEW_EXPRESSION: + { + const char* expression; + int32 type; + Type* resultType; + if (message->FindString("expression", &expression) != B_OK + || message->FindInt32("type", &type) != B_OK + || _GetTypeForTypeCode(type, resultType) != B_OK) { + break; + } + + BReference typeReference(resultType, true); + + status_t error = _AddExpression(expression, resultType); + if (error != B_OK) { + // TODO: notify user of failure + break; + } + + fListener->ExpressionEvaluationRequested(expression, type, + fStackFrame, fThread); + break; + } + case MSG_EXPRESSION_EVALUATED: + { + const char* expression; + status_t result; + Value* value = NULL; + if (message->FindString("expression", &expression) != B_OK + || message->FindInt32("result", &result) != B_OK) { + break; + } + + BReference valueReference; + if (message->FindPointer("value", reinterpret_cast(&value)) + == B_OK) { + valueReference.SetTo(value, true); + } + + _SetExpressionNodeValue(expression, result, value); + break; + } case MSG_VALUE_NODE_CHANGED: { ValueNodeChild* nodeChild; @@ -1980,19 +2197,23 @@ VariablesView::TreeTableCellMouseDown(TreeTable* table, TableCellContextMenuTracker(node, Looper(), this); BReference trackerReference(tracker); - ContextActionList* preActionList = new(std::nothrow) ContextActionList; - if (preActionList == NULL) + ContextActionList* preActionList; + ContextActionList* postActionList; + + error = _GetContextActionsForNode(node, preActionList, postActionList); + if (error != B_OK) return; BPrivate::ObjectDeleter preActionListDeleter( preActionList); - error = _GetContextActionsForNode(node, preActionList); - if (error != B_OK) - return; + BPrivate::ObjectDeleter postActionListDeleter( + postActionList); - if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList) != B_OK) + if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList, + postActionList) != B_OK) { return; + } fTableCellContextMenuTracker = trackerReference.Detach(); fTableCellContextMenuTracker->ShowMenu(screenWhere); @@ -2028,6 +2249,10 @@ VariablesView::_Init() fViewStateHistory = new VariablesViewStateHistory; if (fViewStateHistory->Init() != B_OK) throw std::bad_alloc(); + + fExpressions = new ExpressionInfoTable(); + if (fExpressions->Init() != B_OK) + throw std::bad_alloc(); } @@ -2077,73 +2302,108 @@ VariablesView::_RequestNodeValue(ModelNode* node) status_t VariablesView::_GetContextActionsForNode(ModelNode* node, - ContextActionList* actions) + ContextActionList*& _preActions, ContextActionList*& _postActions) { + _preActions = NULL; + _postActions = NULL; + ValueLocation* location = node->NodeChild()->Location(); - if (location == NULL) - return B_OK; + + _preActions = new(std::nothrow) ContextActionList; + if (_preActions == NULL) + return B_NO_MEMORY; + + BPrivate::ObjectDeleter preActionListDeleter( + _preActions); status_t result = B_OK; BMessage* message = NULL; // only show the Inspect option if the value is in fact located // in memory. - if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) { - result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW, - actions, message); - if (result != B_OK) - return result; - message->AddUInt64("address", location->PieceAt(0).address); - } - - ValueNode* valueNode = node->NodeChild()->Node(); - - if (valueNode != NULL) { - AddressType* type = dynamic_cast(valueNode->GetType()); - if (type != NULL && type->BaseType() != NULL) { - result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY, - actions, message); + if (location != NULL) { + if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) { + result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW, + _preActions, message); + if (result != B_OK) + return result; + message->AddUInt64("address", location->PieceAt(0).address); + } + + ValueNode* valueNode = node->NodeChild()->Node(); + + if (valueNode != NULL) { + AddressType* type = dynamic_cast(valueNode->GetType()); + if (type != NULL && type->BaseType() != NULL) { + result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY, + _preActions, message); + if (result != B_OK) + return result; + message->AddPointer("node", node); + } + } + + result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS, + MSG_SHOW_TYPECAST_NODE_PROMPT, _preActions, message); + if (result != B_OK) + return result; + + result = _AddContextAction("Watch" B_UTF8_ELLIPSIS, + MSG_SHOW_WATCH_VARIABLE_PROMPT, _preActions, message); + if (result != B_OK) + return result; + + if (valueNode == NULL) + return B_OK; + + if (valueNode->LocationAndValueResolutionState() == B_OK) { + result = _AddContextAction("Copy Value", B_COPY, _preActions, message); + if (result != B_OK) + return result; + } + + bool addRangedContainerItem = false; + // if the current node isn't itself a ranged container, check if it + // contains a hidden node which is, since in the latter case we + // want to present the range selection as well. + if (valueNode->IsRangedContainer()) + addRangedContainerItem = true; + else if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { + valueNode = node->ChildAt(0)->NodeChild()->Node(); + if (valueNode != NULL && valueNode->IsRangedContainer()) + addRangedContainerItem = true; + } + + if (addRangedContainerItem) { + result = _AddContextAction("Set visible range" B_UTF8_ELLIPSIS, + MSG_SHOW_CONTAINER_RANGE_PROMPT, _preActions, message); if (result != B_OK) return result; - message->AddPointer("node", node); } } - result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS, - MSG_SHOW_TYPECAST_NODE_PROMPT, actions, message); + _postActions = new(std::nothrow) ContextActionList; + if (_postActions == NULL) + return B_NO_MEMORY; + + BPrivate::ObjectDeleter postActionListDeleter( + _postActions); + + result = _AddContextAction("Add watch expression" B_UTF8_ELLIPSIS, + MSG_ADD_WATCH_EXPRESSION, _postActions, message); if (result != B_OK) return result; - result = _AddContextAction("Watch" B_UTF8_ELLIPSIS, - MSG_SHOW_WATCH_VARIABLE_PROMPT, actions, message); - if (result != B_OK) - return result; - - if (valueNode == NULL) - return B_OK; - - if (valueNode->LocationAndValueResolutionState() == B_OK) { - result = _AddContextAction("Copy Value", B_COPY, actions, message); + if (dynamic_cast(node->NodeChild()) != NULL) { + result = _AddContextAction("Remove watch expression", + MSG_REMOVE_WATCH_EXPRESSION, _postActions, message); if (result != B_OK) return result; + message->AddPointer("node", node); } - // if the current node isn't itself a ranged container, check if it - // contains a hidden node which is, since in the latter case we - // want to present the range selection as well. - if (!valueNode->IsRangedContainer()) { - if (node->CountChildren() == 1 && node->ChildAt(0)->IsHidden()) { - valueNode = node->ChildAt(0)->NodeChild()->Node(); - if (valueNode == NULL || !valueNode->IsRangedContainer()) - return B_OK; - } else - return B_OK; - } - - result = _AddContextAction("Set visible range" B_UTF8_ELLIPSIS, - MSG_SHOW_CONTAINER_RANGE_PROMPT, actions, message); - if (result != B_OK) - return result; + preActionListDeleter.Detach(); + postActionListDeleter.Detach(); return B_OK; } @@ -2286,7 +2546,13 @@ VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, nodeInfo.SetRendererSettings(settings->Message()); } - ObjectID* id = node->GetVariable()->ID(); + Variable* variable = node->GetVariable(); + if (variable == NULL) { + // ignore synthetic nodes + continue; + } + + ObjectID* id = variable->ID(); TypeComponentPath* componentPath = node->GetPath(); status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo); @@ -2326,6 +2592,11 @@ VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, return B_NO_MEMORY; // apply the node's info, if any + Variable* variable = node->GetVariable(); + if (variable == NULL) { + // ignore synthetic nodes + return B_OK; + } ObjectID* objectID = node->GetVariable()->ID(); TypeComponentPath* componentPath = node->GetPath(); const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( @@ -2392,6 +2663,183 @@ VariablesView::_CopyVariableValueToClipboard() } +status_t +VariablesView::_AddExpression(const char* expression, Type* resultType) +{ + // if our stack frame doesn't have an associated function, + // we can't add an expression + FunctionInstance* function = fStackFrame->Function(); + if (function == NULL) + return B_NOT_ALLOWED; + + FunctionID* id = function->GetFunctionID(); + if (id == NULL) + return B_NO_MEMORY; + + BReference idReference(id, true); + + ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); + if (entry == NULL) { + entry = new(std::nothrow) ExpressionInfoEntry(id); + if (entry == NULL) + return B_NO_MEMORY; + status_t error = fExpressions->Insert(entry); + if (error != B_OK) { + delete entry; + return error; + } + } + + ExpressionInfo* info = new(std::nothrow) ExpressionInfo(expression, + resultType); + + if (info == NULL) + return B_NO_MEMORY; + + BReference infoReference(info, true); + + status_t error = _AddExpressionNode(*info); + if (error != B_OK) + return error; + + if (!entry->AddItem(info)) + return B_NO_MEMORY; + + infoReference.Detach(); + return B_OK; +} + + +void +VariablesView::_RemoveExpression(ModelNode* node) +{ + ExpressionValueNodeChild* child + = dynamic_cast(node->NodeChild()); + if (child == NULL) + return; + + FunctionID* id = fStackFrame->Function()->GetFunctionID(); + BReference idReference(id, true); + + ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); + if (entry == NULL) + return; + + for (int32 i = 0; i < entry->CountItems(); i++) { + ExpressionInfo* info = entry->ItemAt(i); + if (info->Expression() == child->GetExpression()) { + entry->RemoveItemAt(i); + info->ReleaseReference(); + break; + } + } + + fVariableTableModel->RemoveSyntheticNode(node); +} + + +status_t +VariablesView::_AddExpressionNode(const ExpressionInfo& info) +{ + Type* type = info.ResultType(); + ExpressionValueNodeChild* child + = new(std::nothrow) ExpressionValueNodeChild(info.Expression(), type); + if (child == NULL) + return B_NO_MEMORY; + + BReference childReference(child, true); + + ExpressionValueNode* expressionNode + = new(std::nothrow) ExpressionValueNode(child, type); + if (expressionNode == NULL) + return B_NO_MEMORY; + + BReference expressionNodeReference(expressionNode, true); + + child->SetNode(expressionNode); + + ModelNode* modelNode = new(std::nothrow) ModelNode(NULL, NULL, + child, true); + if (modelNode == NULL) + return B_NO_MEMORY; + + BReference modelNodeReference(modelNode, true); + + status_t error = fVariableTableModel->AddSyntheticNode(modelNode); + if (error != B_OK) + return error; + + expressionNodeReference.Detach(); + modelNodeReference.Detach(); + return B_OK; +} + + +void +VariablesView::_RestoreExpressionNodes() +{ + FunctionID* id = fStackFrame->Function()->GetFunctionID(); + BReference idReference(id, true); + + ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); + if (entry == NULL) + return; + + for (int32 i = 0; i < entry->CountItems(); i++) { + ExpressionInfo* info = entry->ItemAt(i); + _AddExpressionNode(*info); + SyntheticPrimitiveType* type + = dynamic_cast(info->ResultType()); + fListener->ExpressionEvaluationRequested(info->Expression(), + type->TypeConstant(), fStackFrame, fThread); + } +} + + +void +VariablesView::_SetExpressionNodeValue(const char* expression, + status_t finalResult, Value* value) +{ + FunctionID* id = fStackFrame->Function()->GetFunctionID(); + BReference idReference(id, true); + + ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); + if (entry == NULL) + return; + + void* rootNode = fVariableTableModel->Root(); + for (int32 i = 0; i < fVariableTableModel->CountChildren(rootNode); i++) { + ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(rootNode, + i); + ExpressionValueNodeChild* child + = dynamic_cast(node->NodeChild()); + if (child == NULL) + continue; + + if (child->GetExpression() != expression) + continue; + + child->Node()->SetLocationAndValue(NULL, value, finalResult); + return; + } +} + + +status_t +VariablesView::_GetTypeForTypeCode(int32 type, Type*& _resultType) const +{ + if (BVariant::TypeIsNumber(type)) { + _resultType = new(std::nothrow) SyntheticPrimitiveType(type); + if (_resultType == NULL) + return B_NO_MEMORY; + + return B_OK; + } + + return B_NOT_SUPPORTED; +} + + // #pragma mark - Listener diff --git a/src/apps/debugger/user_interface/gui/team_window/VariablesView.h b/src/apps/debugger/user_interface/gui/team_window/VariablesView.h index c07a868faa..4af102ad3b 100644 --- a/src/apps/debugger/user_interface/gui/team_window/VariablesView.h +++ b/src/apps/debugger/user_interface/gui/team_window/VariablesView.h @@ -8,20 +8,22 @@ #include +#include #include "table/TreeTable.h" class ActionMenuItem; class CpuState; +class ExpressionInfo; class SettingsMenu; class StackFrame; -class StackFrameValues; class Thread; class Type; class TypeComponentPath; class ValueNode; class ValueNodeContainer; +class Value; class Variable; class VariablesViewState; class VariablesViewStateHistory; @@ -68,13 +70,22 @@ private: class ContextMenu; class TableCellContextMenuTracker; typedef BObjectList ContextActionList; + typedef BObjectList ExpressionInfoList; + + struct FunctionKey; + struct ExpressionInfoEntry; + struct ExpressionInfoEntryHashDefinition; + + typedef BOpenHashTable + ExpressionInfoTable; private: void _Init(); void _RequestNodeValue(ModelNode* node); status_t _GetContextActionsForNode(ModelNode* node, - ContextActionList* actions); + ContextActionList*& _preActions, + ContextActionList*& _postActions); status_t _AddContextAction(const char* action, uint32 what, ContextActionList* actions, BMessage*& _message); @@ -91,6 +102,19 @@ private: TreeTablePath& path); void _CopyVariableValueToClipboard(); + status_t _AddExpression(const char* expression, + Type* resultType); + void _RemoveExpression(ModelNode* node); + + status_t _AddExpressionNode(const ExpressionInfo& info); + void _RestoreExpressionNodes(); + + void _SetExpressionNodeValue(const char* expression, + status_t finalResult, Value* value); + + status_t _GetTypeForTypeCode(int32 typeCode, + Type*& _resultType) const; + private: Thread* fThread; StackFrame* fStackFrame; @@ -99,6 +123,7 @@ private: ContainerListener* fContainerListener; VariablesViewState* fPreviousViewState; VariablesViewStateHistory* fViewStateHistory; + ExpressionInfoTable* fExpressions; TableCellContextMenuTracker* fTableCellContextMenuTracker; bool fFrameClearPending; Listener* fListener; @@ -112,6 +137,12 @@ public: virtual void ValueNodeValueRequested(CpuState* cpuState, ValueNodeContainer* container, ValueNode* valueNode) = 0; + + virtual void ExpressionEvaluationRequested( + const char* expression, + type_code resultType, + StackFrame* frame, + Thread* thread) = 0; };