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 1ed17992e1..df0416f84e 100644 --- a/src/apps/debugger/user_interface/gui/team_window/VariablesView.cpp +++ b/src/apps/debugger/user_interface/gui/team_window/VariablesView.cpp @@ -26,7 +26,6 @@ #include "ActionMenuItem.h" #include "Architecture.h" #include "ExpressionInfo.h" -#include "ExpressionValueNode.h" #include "ExpressionValues.h" #include "FileSourceCode.h" #include "Function.h" @@ -41,6 +40,8 @@ #include "StackTrace.h" #include "StackFrame.h" #include "StackFrameValues.h" +#include "StringUtils.h" +#include "StringValue.h" #include "SyntheticPrimitiveType.h" #include "TableCellValueRenderer.h" #include "Team.h" @@ -205,6 +206,47 @@ private: }; +// #pragma mark - ExpressionVariableID + + +class VariablesView::ExpressionVariableID : public ObjectID { +public: + ExpressionVariableID(ExpressionInfo* info) + : + fInfo(info) + { + fInfo->AcquireReference(); + } + + virtual ~ExpressionVariableID() + { + fInfo->ReleaseReference(); + } + + virtual bool operator==(const ObjectID& other) const + { + const ExpressionVariableID* otherID + = dynamic_cast(&other); + if (otherID == NULL) + return false; + + return fInfo == otherID->fInfo; + } + +protected: + virtual uint32 ComputeHashValue() const + { + uint32 hash = *(uint32*)(&fInfo); + hash = hash * 19 + StringUtils::HashValue(fInfo->Expression()); + + return hash; + } + +private: + ExpressionInfo* fInfo; +}; + + // #pragma mark - ModelNode @@ -593,7 +635,8 @@ public: const TreeTablePath& path, int32 columnIndex, BToolTip** _tip); - status_t AddSyntheticNode(ModelNode* node); + status_t AddSyntheticNode(Variable* variable, + ValueNodeChild*& _child); void RemoveSyntheticNode(ModelNode* node); private: @@ -1467,24 +1510,50 @@ VariablesView::VariableTableModel::GetToolTipForTablePath( status_t -VariablesView::VariableTableModel::AddSyntheticNode(ModelNode* node) +VariablesView::VariableTableModel::AddSyntheticNode(Variable* variable, + ValueNodeChild*& _child) { - status_t error = node->Init(); + ValueNodeContainer* container = fNodeManager->GetContainer(); + AutoLocker containerLocker(container); + + _child = new(std::nothrow) VariableValueNodeChild(variable); + if (_child == NULL) + return B_NO_MEMORY; + + BReference childReference(_child, true); + ValueNode* valueNode; + status_t error; + if (_child->IsInternal()) + error = _child->CreateInternalNode(valueNode); + else { + error = TypeHandlerRoster::Default()->CreateValueNode(_child, + _child->GetType(), valueNode); + } + if (error != B_OK) return error; - int32 index = fNodes.CountItems(); + _child->SetNode(valueNode); + valueNode->ReleaseReference(); + container->AddChild(_child); - if (!fNodes.AddItem(node)) { - return B_NO_MEMORY; - // NB: we take over the caller's reference + error = _AddNode(variable, NULL, _child); + if (error != B_OK) { + container->RemoveChild(_child); + return error; } - fNodeTable.Insert(node); + // since we're injecting these nodes synthetically, + // we have to manually ask the node manager to create any + // applicable children; this would normally be done implicitly + // for top level nodes, as they're added from the parameters/locals, + // but not here. + fNodeManager->AddChildNodes(_child); - node->NodeChild()->Node()->SetContainer(fNodeManager->GetContainer()); - - NotifyNodesAdded(TreeTablePath(), index, 1); + ModelNode* childNode = fNodeTable.Lookup(_child); + if (childNode != NULL) + fContainerListener->ModelNodeValueRequested(childNode); + ValueNodeChildrenCreated(_child->Node()); return B_OK; } @@ -1615,6 +1684,7 @@ VariablesView::VariablesView(Listener* listener) fPreviousViewState(NULL), fViewStateHistory(NULL), fExpressions(NULL), + fExpressionChildren(10, false), fTableCellContextMenuTracker(NULL), fFrameClearPending(false), fListener(listener) @@ -1676,6 +1746,10 @@ VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame) _FinishContextMenu(true); + for (int32 i = 0; i < fExpressionChildren.CountItems(); i++) + fExpressionChildren.ItemAt(i)->ReleaseReference(); + fExpressionChildren.MakeEmpty(); + if (fThread != NULL) fThread->ReleaseReference(); if (fStackFrame != NULL) @@ -1975,7 +2049,7 @@ VariablesView::MessageReceived(BMessage* message) valueReference.SetTo(value, true); } - _SetExpressionNodeValue(info, result, value); + _AddExpressionNode(info, result, value); break; } case MSG_VALUE_NODE_CHANGED: @@ -2410,20 +2484,18 @@ VariablesView::_GetContextActionsForNode(ModelNode* node, BPrivate::ObjectDeleter postActionListDeleter( _postActions); -#if 0 result = _AddContextAction("Add watch expression" B_UTF8_ELLIPSIS, MSG_ADD_WATCH_EXPRESSION, _postActions, message); if (result != B_OK) return result; - if (dynamic_cast(node->NodeChild()) != NULL) { + if (fExpressionChildren.HasItem(node->NodeChild())) { result = _AddContextAction("Remove watch expression", MSG_REMOVE_WATCH_EXPRESSION, _postActions, message); if (result != B_OK) return result; message->AddPointer("node", node); } -#endif preActionListDeleter.Detach(); postActionListDeleter.Detach(); @@ -2599,47 +2671,28 @@ VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState, Value* value = node->GetValue(); Variable* variable = node->GetVariable(); - if (variable != NULL) { - TypeComponentPath* componentPath = node->GetPath(); - ObjectID* id = variable->ID(); + TypeComponentPath* componentPath = node->GetPath(); + ObjectID* id = variable->ID(); - status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo); + status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo); + if (error != B_OK) + return error; + + if (value != NULL && updateValues) { + BVariant variableValueData; + if (value->ToVariant(variableValueData)) + error = viewState->Values()->SetValue(id, componentPath, + variableValueData); if (error != B_OK) return error; - - if (value != NULL && updateValues) { - BVariant variableValueData; - if (value->ToVariant(variableValueData)) - error = viewState->Values()->SetValue(id, componentPath, - variableValueData); - if (error != B_OK) - return error; - } - - // recurse - error = _AddViewStateDescendentNodeInfos(viewState, node, path, - updateValues); - if (error != B_OK) - return error; - } else { - ExpressionValueNodeChild* child - = dynamic_cast(node->NodeChild()); - if (child != NULL && value != NULL && updateValues) { - BVariant variableValueData; - if (value->ToVariant(variableValueData)) { - FunctionID* id = fStackFrame->Function()->GetFunctionID(); - if (id == NULL) - return B_NO_MEMORY; - BReference functionReference(id, true); - status_t error = viewState->GetExpressionValues() - ->SetValue(id, fThread, child->GetExpression(), - variableValueData); - if (error != B_OK) - return error; - } - } } + // recurse + error = _AddViewStateDescendentNodeInfos(viewState, node, path, + updateValues); + if (error != B_OK) + return error; + path.RemoveLastComponent(); } @@ -2658,54 +2711,37 @@ VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState, return B_NO_MEMORY; // apply the node's info, if any - Variable* variable = node->GetVariable(); - if (variable != NULL) { - ObjectID* objectID = node->GetVariable()->ID(); - TypeComponentPath* componentPath = node->GetPath(); - const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( - objectID, componentPath); - if (nodeInfo != NULL) { - // NB: if the node info indicates that the node in question - // was being cast to a different type, this *must* be applied - // before any other view state restoration, since it - // potentially changes the child hierarchy under that node. - Type* type = nodeInfo->GetCastedType(); - if (type != NULL) { - ValueNode* valueNode = NULL; - if (TypeHandlerRoster::Default()->CreateValueNode( - node->NodeChild(), type, valueNode) == B_OK) { - node->NodeChild()->SetNode(valueNode); - node->SetCastedType(type); - } - } - - // we don't have a renderer yet so we can't apply the settings - // at this stage. Store them on the model node so we can lazily - // apply them once the value is retrieved. - node->SetLastRendererSettings(nodeInfo->GetRendererSettings()); - - fVariableTable->SetNodeExpanded(path, - nodeInfo->IsNodeExpanded()); - - BVariant previousValue; - if (viewState->Values()->GetValue(objectID, componentPath, - previousValue)) { - node->SetPreviousValue(previousValue); + ObjectID* objectID = node->GetVariable()->ID(); + TypeComponentPath* componentPath = node->GetPath(); + const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo( + objectID, componentPath); + if (nodeInfo != NULL) { + // NB: if the node info indicates that the node in question + // was being cast to a different type, this *must* be applied + // before any other view state restoration, since it + // potentially changes the child hierarchy under that node. + Type* type = nodeInfo->GetCastedType(); + if (type != NULL) { + ValueNode* valueNode = NULL; + if (TypeHandlerRoster::Default()->CreateValueNode( + node->NodeChild(), type, valueNode) == B_OK) { + node->NodeChild()->SetNode(valueNode); + node->SetCastedType(type); } } - } else { - ExpressionValueNodeChild* child - = dynamic_cast(node->NodeChild()); - if (child != NULL) { - BVariant previousValue; - FunctionID* id = fStackFrame->Function()->GetFunctionID(); - if (id == NULL) - return B_NO_MEMORY; - BReference idReference(id, true); - if (viewState->GetExpressionValues()->GetValue(id, fThread, - child->GetExpression(), previousValue)) { - node->SetPreviousValue(previousValue); - } + + // we don't have a renderer yet so we can't apply the settings + // at this stage. Store them on the model node so we can lazily + // apply them once the value is retrieved. + node->SetLastRendererSettings(nodeInfo->GetRendererSettings()); + + fVariableTable->SetNodeExpanded(path, + nodeInfo->IsNodeExpanded()); + + BVariant previousValue; + if (viewState->Values()->GetValue(objectID, componentPath, + previousValue)) { + node->SetPreviousValue(previousValue); } } @@ -2776,10 +2812,6 @@ VariablesView::_AddExpression(const char* expression, ExpressionInfo*& _info) BReference infoReference(info, true); - status_t error = _AddExpressionNode(info); - if (error != B_OK) - return error; - if (!entry->AddItem(info)) return B_NO_MEMORY; @@ -2793,9 +2825,7 @@ VariablesView::_AddExpression(const char* expression, ExpressionInfo*& _info) void VariablesView::_RemoveExpression(ModelNode* node) { - ExpressionValueNodeChild* child - = dynamic_cast(node->NodeChild()); - if (child == NULL) + if (!fExpressionChildren.HasItem(node->NodeChild())) return; FunctionID* id = fStackFrame->Function()->GetFunctionID(); @@ -2807,7 +2837,7 @@ VariablesView::_RemoveExpression(ModelNode* node) for (int32 i = 0; i < entry->CountItems(); i++) { ExpressionInfo* info = entry->ItemAt(i); - if (info->Expression() == child->GetExpression()) { + if (info->Expression() == node->Name()) { entry->RemoveItemAt(i); info->RemoveListener(this); info->ReleaseReference(); @@ -2819,46 +2849,6 @@ VariablesView::_RemoveExpression(ModelNode* node) } -status_t -VariablesView::_AddExpressionNode(ExpressionInfo* info) -{ -#if 0 - 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; -#endif - return B_NOT_SUPPORTED; -} - - void VariablesView::_RestoreExpressionNodes() { @@ -2878,45 +2868,90 @@ VariablesView::_RestoreExpressionNodes() for (int32 i = 0; i < entry->CountItems(); i++) { ExpressionInfo* info = entry->ItemAt(i); - _AddExpressionNode(info); fListener->ExpressionEvaluationRequested(info, fStackFrame, fThread); } } void -VariablesView::_SetExpressionNodeValue(ExpressionInfo* info, status_t result, +VariablesView::_AddExpressionNode(ExpressionInfo* info, status_t result, ExpressionResult* value) { - FunctionInstance* instance = fStackFrame->Function(); - if (instance == NULL) - return; + Variable* variable = NULL; + BReference variableReference; + BVariant valueData; - FunctionID* id = instance->GetFunctionID(); + Value* primitive = value->PrimitiveValue(); + if (primitive != NULL) { + if (!primitive->ToVariant(valueData)) + return; + } else + valueData.SetTo("Unsupported expression result type."); + + ExpressionVariableID* id + = new(std::nothrow) ExpressionVariableID(info); if (id == NULL) return; + BReference idReference(id, true); - BReference idReference(id, true); + Type* type = NULL; + if (_GetTypeForTypeCode(valueData.Type(), type) != B_OK) + return; + BReference typeReference(type, true); - ExpressionInfoEntry* entry = fExpressions->Lookup(FunctionKey(id)); - if (entry == NULL) + ValueLocation* location = new(std::nothrow) ValueLocation(); + if (location == NULL) + return; + BReference locationReference(location, true); + if (valueData.IsNumber()) { + + ValuePieceLocation piece; + if (!piece.SetToValue(valueData.Bytes(), valueData.Size()) + || !location->AddPiece(piece)) { + return; + } + } + + variable = new(std::nothrow) Variable(id, + info->Expression(), type, location); + if (variable == NULL) + return; + variableReference.SetTo(variable, true); + + ValueNodeChild* child = NULL; + status_t error = fVariableTableModel->AddSyntheticNode(variable, child); + if (error != B_OK) 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; + // In the case of either an evaluation error, or an unsupported result + // type, set an explanatory string for the result directly. + if (result != B_OK || valueData.Type() == B_STRING_TYPE) { + StringValue* explicitValue = new(std::nothrow) StringValue( + valueData.ToString()); + if (explicitValue == NULL) + return; - if (child->GetExpression() != info->Expression()) - continue; + child->Node()->SetLocationAndValue(NULL, explicitValue, B_OK); + } - child->Node()->SetLocationAndValue(NULL, value->PrimitiveValue(), - result); - return; + if (fExpressionChildren.AddItem(child)) { + child->AcquireReference(); + + // attempt to restore our newly added node's view state, + // if applicable. + FunctionID* functionID = fStackFrame->Function() + ->GetFunctionID(); + if (functionID == NULL) + return; + BReference functionIDReference(functionID, + true); + VariablesViewState* viewState = fViewStateHistory + ->GetState(fThread->ID(), functionID); + if (viewState != NULL) { + TreeTablePath path; + _ApplyViewStateDescendentNodeInfos(viewState, + fVariableTableModel->Root(), path); + } } } @@ -2924,7 +2959,7 @@ VariablesView::_SetExpressionNodeValue(ExpressionInfo* info, status_t result, status_t VariablesView::_GetTypeForTypeCode(int32 type, Type*& _resultType) const { - if (BVariant::TypeIsNumber(type)) { + if (BVariant::TypeIsNumber(type) || type == B_STRING_TYPE) { _resultType = new(std::nothrow) SyntheticPrimitiveType(type); if (_resultType == NULL) return B_NO_MEMORY; 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 0ce8062e40..5427740233 100644 --- a/src/apps/debugger/user_interface/gui/team_window/VariablesView.h +++ b/src/apps/debugger/user_interface/gui/team_window/VariablesView.h @@ -23,6 +23,7 @@ class Thread; class Type; class TypeComponentPath; class ValueNode; +class ValueNodeChild; class ValueNodeContainer; class Value; class Variable; @@ -70,6 +71,7 @@ private: private: class ContainerListener; + class ExpressionVariableID; class ModelNode; class VariableValueColumn; class VariableTableModel; @@ -77,6 +79,7 @@ private: class TableCellContextMenuTracker; typedef BObjectList ContextActionList; typedef BObjectList ExpressionInfoList; + typedef BObjectList ExpressionChildList; struct FunctionKey; struct ExpressionInfoEntry; @@ -113,10 +116,9 @@ private: ExpressionInfo*& _info); void _RemoveExpression(ModelNode* node); - status_t _AddExpressionNode(ExpressionInfo* info); void _RestoreExpressionNodes(); - void _SetExpressionNodeValue(ExpressionInfo* info, + void _AddExpressionNode(ExpressionInfo* info, status_t result, ExpressionResult* value); status_t _GetTypeForTypeCode(int32 typeCode, @@ -131,6 +133,7 @@ private: VariablesViewState* fPreviousViewState; VariablesViewStateHistory* fViewStateHistory; ExpressionInfoTable* fExpressions; + ExpressionChildList fExpressionChildren; TableCellContextMenuTracker* fTableCellContextMenuTracker; bool fFrameClearPending; Listener* fListener; diff --git a/src/apps/debugger/user_interface/util/UiUtils.cpp b/src/apps/debugger/user_interface/util/UiUtils.cpp index b6c9904b8c..2d4167037f 100644 --- a/src/apps/debugger/user_interface/util/UiUtils.cpp +++ b/src/apps/debugger/user_interface/util/UiUtils.cpp @@ -479,6 +479,8 @@ UiUtils::TypeCodeToString(type_code type) return "float"; case B_DOUBLE_TYPE: return "double"; + case B_STRING_TYPE: + return "string"; default: return "unknown"; }