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.
This commit is contained in:
@@ -58,6 +58,7 @@
|
|||||||
#include "TypeComponentPath.h"
|
#include "TypeComponentPath.h"
|
||||||
#include "UiUtils.h"
|
#include "UiUtils.h"
|
||||||
#include "UserInterface.h"
|
#include "UserInterface.h"
|
||||||
|
#include "Value.h"
|
||||||
#include "Variable.h"
|
#include "Variable.h"
|
||||||
#include "WatchPromptWindow.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<SourceLanguage> languageReference(language, true);
|
||||||
|
fListener->ExpressionEvaluationRequested(language, expression, resultType,
|
||||||
|
frame, thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void
|
void
|
||||||
TeamWindow::ThreadStateChanged(const Team::ThreadEvent& event)
|
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<Value> 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
|
void
|
||||||
TeamWindow::DebugReportChanged(const Team::DebugReportEvent& event)
|
TeamWindow::DebugReportChanged(const Team::DebugReportEvent& event)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ private:
|
|||||||
virtual void ValueNodeValueRequested(CpuState* cpuState,
|
virtual void ValueNodeValueRequested(CpuState* cpuState,
|
||||||
ValueNodeContainer* container,
|
ValueNodeContainer* container,
|
||||||
ValueNode* valueNode);
|
ValueNode* valueNode);
|
||||||
|
virtual void ExpressionEvaluationRequested(
|
||||||
|
const char* expression,
|
||||||
|
type_code resultType,
|
||||||
|
StackFrame* frame,
|
||||||
|
::Thread* thread);
|
||||||
|
|
||||||
// Team::Listener
|
// Team::Listener
|
||||||
virtual void ThreadStateChanged(
|
virtual void ThreadStateChanged(
|
||||||
@@ -143,9 +148,13 @@ private:
|
|||||||
const Team::UserBreakpointEvent& event);
|
const Team::UserBreakpointEvent& event);
|
||||||
virtual void WatchpointChanged(
|
virtual void WatchpointChanged(
|
||||||
const Team::WatchpointEvent& event);
|
const Team::WatchpointEvent& event);
|
||||||
|
virtual void ExpressionEvaluated(
|
||||||
|
const Team::ExpressionEvaluationEvent&
|
||||||
|
event);
|
||||||
virtual void DebugReportChanged(
|
virtual void DebugReportChanged(
|
||||||
const Team::DebugReportEvent& event);
|
const Team::DebugReportEvent& event);
|
||||||
|
|
||||||
|
|
||||||
// Function::Listener
|
// Function::Listener
|
||||||
virtual void FunctionSourceCodeChanged(Function* function);
|
virtual void FunctionSourceCodeChanged(Function* function);
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,9 @@
|
|||||||
|
|
||||||
#include "ActionMenuItem.h"
|
#include "ActionMenuItem.h"
|
||||||
#include "Architecture.h"
|
#include "Architecture.h"
|
||||||
|
#include "ExpressionInfo.h"
|
||||||
|
#include "ExpressionValueNode.h"
|
||||||
|
#include "ExpressionValues.h"
|
||||||
#include "FileSourceCode.h"
|
#include "FileSourceCode.h"
|
||||||
#include "Function.h"
|
#include "Function.h"
|
||||||
#include "FunctionID.h"
|
#include "FunctionID.h"
|
||||||
@@ -38,6 +41,7 @@
|
|||||||
#include "StackTrace.h"
|
#include "StackTrace.h"
|
||||||
#include "StackFrame.h"
|
#include "StackFrame.h"
|
||||||
#include "StackFrameValues.h"
|
#include "StackFrameValues.h"
|
||||||
|
#include "SyntheticPrimitiveType.h"
|
||||||
#include "TableCellValueRenderer.h"
|
#include "TableCellValueRenderer.h"
|
||||||
#include "Team.h"
|
#include "Team.h"
|
||||||
#include "TeamDebugInfo.h"
|
#include "TeamDebugInfo.h"
|
||||||
@@ -67,7 +71,9 @@ enum {
|
|||||||
enum {
|
enum {
|
||||||
MSG_MODEL_NODE_HIDDEN = 'monh',
|
MSG_MODEL_NODE_HIDDEN = 'monh',
|
||||||
MSG_VALUE_NODE_NEEDS_VALUE = 'mvnv',
|
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;
|
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 {
|
class VariablesView::ContainerListener : public ValueNodeContainer::Listener {
|
||||||
public:
|
public:
|
||||||
ContainerListener(BHandler* indirectTarget);
|
ContainerListener(BHandler* indirectTarget);
|
||||||
@@ -99,6 +205,9 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// #pragma mark - ModelNode
|
||||||
|
|
||||||
|
|
||||||
class VariablesView::ModelNode : public BReferenceable {
|
class VariablesView::ModelNode : public BReferenceable {
|
||||||
public:
|
public:
|
||||||
ModelNode(ModelNode* parent, Variable* variable, ValueNodeChild* nodeChild,
|
ModelNode(ModelNode* parent, Variable* variable, ValueNodeChild* nodeChild,
|
||||||
@@ -377,6 +486,7 @@ private:
|
|||||||
bool fIsPresentationNode;
|
bool fIsPresentationNode;
|
||||||
bool fHidden;
|
bool fHidden;
|
||||||
bool fValueChanged;
|
bool fValueChanged;
|
||||||
|
bool fIsExpression;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ModelNode* fNext;
|
ModelNode* fNext;
|
||||||
@@ -483,6 +593,9 @@ public:
|
|||||||
const TreeTablePath& path,
|
const TreeTablePath& path,
|
||||||
int32 columnIndex, BToolTip** _tip);
|
int32 columnIndex, BToolTip** _tip);
|
||||||
|
|
||||||
|
status_t AddSyntheticNode(ModelNode* node);
|
||||||
|
void RemoveSyntheticNode(ModelNode* node);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct NodeHashDefinition {
|
struct NodeHashDefinition {
|
||||||
typedef ValueNodeChild* KeyType;
|
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
|
status_t
|
||||||
VariablesView::VariableTableModel::_AddNode(Variable* variable,
|
VariablesView::VariableTableModel::_AddNode(Variable* variable,
|
||||||
ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode,
|
ModelNode* parent, ValueNodeChild* nodeChild, bool isPresentationNode,
|
||||||
@@ -1460,6 +1614,7 @@ VariablesView::VariablesView(Listener* listener)
|
|||||||
fContainerListener(NULL),
|
fContainerListener(NULL),
|
||||||
fPreviousViewState(NULL),
|
fPreviousViewState(NULL),
|
||||||
fViewStateHistory(NULL),
|
fViewStateHistory(NULL),
|
||||||
|
fExpressions(NULL),
|
||||||
fTableCellContextMenuTracker(NULL),
|
fTableCellContextMenuTracker(NULL),
|
||||||
fFrameClearPending(false),
|
fFrameClearPending(false),
|
||||||
fListener(listener)
|
fListener(listener)
|
||||||
@@ -1539,6 +1694,8 @@ VariablesView::SetStackFrame(Thread* thread, StackFrame* stackFrame)
|
|||||||
ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i);
|
ModelNode* node = (ModelNode*)fVariableTableModel->ChildAt(root, i);
|
||||||
_RequestNodeValue(node);
|
_RequestNodeValue(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_RestoreExpressionNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
_RestoreViewState();
|
_RestoreViewState();
|
||||||
@@ -1759,6 +1916,66 @@ VariablesView::MessageReceived(BMessage* message)
|
|||||||
Looper()->PostMessage(&looperMessage);
|
Looper()->PostMessage(&looperMessage);
|
||||||
break;
|
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<void**>(&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<Type> 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<Value> valueReference;
|
||||||
|
if (message->FindPointer("value", reinterpret_cast<void**>(&value))
|
||||||
|
== B_OK) {
|
||||||
|
valueReference.SetTo(value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_SetExpressionNodeValue(expression, result, value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case MSG_VALUE_NODE_CHANGED:
|
case MSG_VALUE_NODE_CHANGED:
|
||||||
{
|
{
|
||||||
ValueNodeChild* nodeChild;
|
ValueNodeChild* nodeChild;
|
||||||
@@ -1980,19 +2197,23 @@ VariablesView::TreeTableCellMouseDown(TreeTable* table,
|
|||||||
TableCellContextMenuTracker(node, Looper(), this);
|
TableCellContextMenuTracker(node, Looper(), this);
|
||||||
BReference<TableCellContextMenuTracker> trackerReference(tracker);
|
BReference<TableCellContextMenuTracker> trackerReference(tracker);
|
||||||
|
|
||||||
ContextActionList* preActionList = new(std::nothrow) ContextActionList;
|
ContextActionList* preActionList;
|
||||||
if (preActionList == NULL)
|
ContextActionList* postActionList;
|
||||||
|
|
||||||
|
error = _GetContextActionsForNode(node, preActionList, postActionList);
|
||||||
|
if (error != B_OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter(
|
BPrivate::ObjectDeleter<ContextActionList> preActionListDeleter(
|
||||||
preActionList);
|
preActionList);
|
||||||
|
|
||||||
error = _GetContextActionsForNode(node, preActionList);
|
BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter(
|
||||||
if (error != B_OK)
|
postActionList);
|
||||||
return;
|
|
||||||
|
|
||||||
if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList) != B_OK)
|
if (tracker == NULL || tracker->Init(settings, settingsMenu, preActionList,
|
||||||
|
postActionList) != B_OK) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
fTableCellContextMenuTracker = trackerReference.Detach();
|
fTableCellContextMenuTracker = trackerReference.Detach();
|
||||||
fTableCellContextMenuTracker->ShowMenu(screenWhere);
|
fTableCellContextMenuTracker->ShowMenu(screenWhere);
|
||||||
@@ -2028,6 +2249,10 @@ VariablesView::_Init()
|
|||||||
fViewStateHistory = new VariablesViewStateHistory;
|
fViewStateHistory = new VariablesViewStateHistory;
|
||||||
if (fViewStateHistory->Init() != B_OK)
|
if (fViewStateHistory->Init() != B_OK)
|
||||||
throw std::bad_alloc();
|
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
|
status_t
|
||||||
VariablesView::_GetContextActionsForNode(ModelNode* node,
|
VariablesView::_GetContextActionsForNode(ModelNode* node,
|
||||||
ContextActionList* actions)
|
ContextActionList*& _preActions, ContextActionList*& _postActions)
|
||||||
{
|
{
|
||||||
|
_preActions = NULL;
|
||||||
|
_postActions = NULL;
|
||||||
|
|
||||||
ValueLocation* location = node->NodeChild()->Location();
|
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<ContextActionList> preActionListDeleter(
|
||||||
|
_preActions);
|
||||||
|
|
||||||
status_t result = B_OK;
|
status_t result = B_OK;
|
||||||
BMessage* message = NULL;
|
BMessage* message = NULL;
|
||||||
|
|
||||||
// only show the Inspect option if the value is in fact located
|
// only show the Inspect option if the value is in fact located
|
||||||
// in memory.
|
// in memory.
|
||||||
if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) {
|
if (location != NULL) {
|
||||||
result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW,
|
if (location->PieceAt(0).type == VALUE_PIECE_LOCATION_MEMORY) {
|
||||||
actions, message);
|
result = _AddContextAction("Inspect", MSG_SHOW_INSPECTOR_WINDOW,
|
||||||
if (result != B_OK)
|
_preActions, message);
|
||||||
return result;
|
if (result != B_OK)
|
||||||
message->AddUInt64("address", location->PieceAt(0).address);
|
return result;
|
||||||
}
|
message->AddUInt64("address", location->PieceAt(0).address);
|
||||||
|
}
|
||||||
ValueNode* valueNode = node->NodeChild()->Node();
|
|
||||||
|
ValueNode* valueNode = node->NodeChild()->Node();
|
||||||
if (valueNode != NULL) {
|
|
||||||
AddressType* type = dynamic_cast<AddressType*>(valueNode->GetType());
|
if (valueNode != NULL) {
|
||||||
if (type != NULL && type->BaseType() != NULL) {
|
AddressType* type = dynamic_cast<AddressType*>(valueNode->GetType());
|
||||||
result = _AddContextAction("Cast to array", MSG_TYPECAST_TO_ARRAY,
|
if (type != NULL && type->BaseType() != NULL) {
|
||||||
actions, message);
|
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)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
message->AddPointer("node", node);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result = _AddContextAction("Cast as" B_UTF8_ELLIPSIS,
|
_postActions = new(std::nothrow) ContextActionList;
|
||||||
MSG_SHOW_TYPECAST_NODE_PROMPT, actions, message);
|
if (_postActions == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
BPrivate::ObjectDeleter<ContextActionList> postActionListDeleter(
|
||||||
|
_postActions);
|
||||||
|
|
||||||
|
result = _AddContextAction("Add watch expression" B_UTF8_ELLIPSIS,
|
||||||
|
MSG_ADD_WATCH_EXPRESSION, _postActions, message);
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
|
|
||||||
result = _AddContextAction("Watch" B_UTF8_ELLIPSIS,
|
if (dynamic_cast<ExpressionValueNodeChild*>(node->NodeChild()) != NULL) {
|
||||||
MSG_SHOW_WATCH_VARIABLE_PROMPT, actions, message);
|
result = _AddContextAction("Remove watch expression",
|
||||||
if (result != B_OK)
|
MSG_REMOVE_WATCH_EXPRESSION, _postActions, message);
|
||||||
return result;
|
|
||||||
|
|
||||||
if (valueNode == NULL)
|
|
||||||
return B_OK;
|
|
||||||
|
|
||||||
if (valueNode->LocationAndValueResolutionState() == B_OK) {
|
|
||||||
result = _AddContextAction("Copy Value", B_COPY, actions, message);
|
|
||||||
if (result != B_OK)
|
if (result != B_OK)
|
||||||
return result;
|
return result;
|
||||||
|
message->AddPointer("node", node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the current node isn't itself a ranged container, check if it
|
preActionListDeleter.Detach();
|
||||||
// contains a hidden node which is, since in the latter case we
|
postActionListDeleter.Detach();
|
||||||
// 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;
|
|
||||||
|
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
@@ -2286,7 +2546,13 @@ VariablesView::_AddViewStateDescendentNodeInfos(VariablesViewState* viewState,
|
|||||||
nodeInfo.SetRendererSettings(settings->Message());
|
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();
|
TypeComponentPath* componentPath = node->GetPath();
|
||||||
|
|
||||||
status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo);
|
status_t error = viewState->SetNodeInfo(id, componentPath, nodeInfo);
|
||||||
@@ -2326,6 +2592,11 @@ VariablesView::_ApplyViewStateDescendentNodeInfos(VariablesViewState* viewState,
|
|||||||
return B_NO_MEMORY;
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
// apply the node's info, if any
|
// 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();
|
ObjectID* objectID = node->GetVariable()->ID();
|
||||||
TypeComponentPath* componentPath = node->GetPath();
|
TypeComponentPath* componentPath = node->GetPath();
|
||||||
const VariablesViewNodeInfo* nodeInfo = viewState->GetNodeInfo(
|
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<FunctionID> 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<ExpressionInfo> 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<ExpressionValueNodeChild*>(node->NodeChild());
|
||||||
|
if (child == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
FunctionID* id = fStackFrame->Function()->GetFunctionID();
|
||||||
|
BReference<FunctionID> 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<ValueNodeChild> childReference(child, true);
|
||||||
|
|
||||||
|
ExpressionValueNode* expressionNode
|
||||||
|
= new(std::nothrow) ExpressionValueNode(child, type);
|
||||||
|
if (expressionNode == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
BReference<ValueNode> expressionNodeReference(expressionNode, true);
|
||||||
|
|
||||||
|
child->SetNode(expressionNode);
|
||||||
|
|
||||||
|
ModelNode* modelNode = new(std::nothrow) ModelNode(NULL, NULL,
|
||||||
|
child, true);
|
||||||
|
if (modelNode == NULL)
|
||||||
|
return B_NO_MEMORY;
|
||||||
|
|
||||||
|
BReference<ModelNode> 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<FunctionID> 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<SyntheticPrimitiveType*>(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<FunctionID> 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<ExpressionValueNodeChild*>(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
|
// #pragma mark - Listener
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,20 +8,22 @@
|
|||||||
|
|
||||||
|
|
||||||
#include <GroupView.h>
|
#include <GroupView.h>
|
||||||
|
#include <util/OpenHashTable.h>
|
||||||
|
|
||||||
#include "table/TreeTable.h"
|
#include "table/TreeTable.h"
|
||||||
|
|
||||||
|
|
||||||
class ActionMenuItem;
|
class ActionMenuItem;
|
||||||
class CpuState;
|
class CpuState;
|
||||||
|
class ExpressionInfo;
|
||||||
class SettingsMenu;
|
class SettingsMenu;
|
||||||
class StackFrame;
|
class StackFrame;
|
||||||
class StackFrameValues;
|
|
||||||
class Thread;
|
class Thread;
|
||||||
class Type;
|
class Type;
|
||||||
class TypeComponentPath;
|
class TypeComponentPath;
|
||||||
class ValueNode;
|
class ValueNode;
|
||||||
class ValueNodeContainer;
|
class ValueNodeContainer;
|
||||||
|
class Value;
|
||||||
class Variable;
|
class Variable;
|
||||||
class VariablesViewState;
|
class VariablesViewState;
|
||||||
class VariablesViewStateHistory;
|
class VariablesViewStateHistory;
|
||||||
@@ -68,13 +70,22 @@ private:
|
|||||||
class ContextMenu;
|
class ContextMenu;
|
||||||
class TableCellContextMenuTracker;
|
class TableCellContextMenuTracker;
|
||||||
typedef BObjectList<ActionMenuItem> ContextActionList;
|
typedef BObjectList<ActionMenuItem> ContextActionList;
|
||||||
|
typedef BObjectList<ExpressionInfo> ExpressionInfoList;
|
||||||
|
|
||||||
|
struct FunctionKey;
|
||||||
|
struct ExpressionInfoEntry;
|
||||||
|
struct ExpressionInfoEntryHashDefinition;
|
||||||
|
|
||||||
|
typedef BOpenHashTable<ExpressionInfoEntryHashDefinition>
|
||||||
|
ExpressionInfoTable;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _Init();
|
void _Init();
|
||||||
|
|
||||||
void _RequestNodeValue(ModelNode* node);
|
void _RequestNodeValue(ModelNode* node);
|
||||||
status_t _GetContextActionsForNode(ModelNode* node,
|
status_t _GetContextActionsForNode(ModelNode* node,
|
||||||
ContextActionList* actions);
|
ContextActionList*& _preActions,
|
||||||
|
ContextActionList*& _postActions);
|
||||||
status_t _AddContextAction(const char* action,
|
status_t _AddContextAction(const char* action,
|
||||||
uint32 what, ContextActionList* actions,
|
uint32 what, ContextActionList* actions,
|
||||||
BMessage*& _message);
|
BMessage*& _message);
|
||||||
@@ -91,6 +102,19 @@ private:
|
|||||||
TreeTablePath& path);
|
TreeTablePath& path);
|
||||||
void _CopyVariableValueToClipboard();
|
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:
|
private:
|
||||||
Thread* fThread;
|
Thread* fThread;
|
||||||
StackFrame* fStackFrame;
|
StackFrame* fStackFrame;
|
||||||
@@ -99,6 +123,7 @@ private:
|
|||||||
ContainerListener* fContainerListener;
|
ContainerListener* fContainerListener;
|
||||||
VariablesViewState* fPreviousViewState;
|
VariablesViewState* fPreviousViewState;
|
||||||
VariablesViewStateHistory* fViewStateHistory;
|
VariablesViewStateHistory* fViewStateHistory;
|
||||||
|
ExpressionInfoTable* fExpressions;
|
||||||
TableCellContextMenuTracker* fTableCellContextMenuTracker;
|
TableCellContextMenuTracker* fTableCellContextMenuTracker;
|
||||||
bool fFrameClearPending;
|
bool fFrameClearPending;
|
||||||
Listener* fListener;
|
Listener* fListener;
|
||||||
@@ -112,6 +137,12 @@ public:
|
|||||||
virtual void ValueNodeValueRequested(CpuState* cpuState,
|
virtual void ValueNodeValueRequested(CpuState* cpuState,
|
||||||
ValueNodeContainer* container,
|
ValueNodeContainer* container,
|
||||||
ValueNode* valueNode) = 0;
|
ValueNode* valueNode) = 0;
|
||||||
|
|
||||||
|
virtual void ExpressionEvaluationRequested(
|
||||||
|
const char* expression,
|
||||||
|
type_code resultType,
|
||||||
|
StackFrame* frame,
|
||||||
|
Thread* thread) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user