Debugger: Update locals to use VirtualTree / FpDebug: return locals as new WatchResultData

This commit is contained in:
Martin 2023-03-14 15:25:46 +01:00
parent 40a5975659
commit 26f4160df9
7 changed files with 334 additions and 219 deletions

View File

@ -115,9 +115,6 @@ type
{ TFpThreadWorkerLocalsUpdate } { TFpThreadWorkerLocalsUpdate }
TFpThreadWorkerLocalsUpdate = class(TFpThreadWorkerLocals) TFpThreadWorkerLocalsUpdate = class(TFpThreadWorkerLocals)
private
FLocals: TLocalsListIntf;
procedure DoLocalsFreed_DecRef(Sender: TObject);
protected protected
procedure UpdateLocals_DecRef(Data: PtrInt = 0); override; procedure UpdateLocals_DecRef(Data: PtrInt = 0); override;
procedure DoRemovedFromLinkedList; override; // _DecRef procedure DoRemovedFromLinkedList; override; // _DecRef
@ -985,41 +982,15 @@ end;
{ TFpThreadWorkerLocalsUpdate } { TFpThreadWorkerLocalsUpdate }
procedure TFpThreadWorkerLocalsUpdate.DoLocalsFreed_DecRef(Sender: TObject);
begin
assert(system.ThreadID = classes.MainThreadID, 'TFpThreadWorkerLocals.DoLocalsFreed_DecRef: system.ThreadID = classes.MainThreadID');
FLocals := nil;
RequestStop;
UnQueue_DecRef;
end;
procedure TFpThreadWorkerLocalsUpdate.UpdateLocals_DecRef(Data: PtrInt); procedure TFpThreadWorkerLocalsUpdate.UpdateLocals_DecRef(Data: PtrInt);
var var
i: Integer;
r: TResultEntry;
dbg: TFpDebugDebugger; dbg: TFpDebugDebugger;
rv: TLzDbgWatchDataIntf;
begin begin
assert(system.ThreadID = classes.MainThreadID, 'TFpThreadWorkerLocals.UpdateLocals_DecRef: system.ThreadID = classes.MainThreadID'); assert(system.ThreadID = classes.MainThreadID, 'TFpThreadWorkerLocals.UpdateLocals_DecRef: system.ThreadID = classes.MainThreadID');
if FLocals <> nil then begin if FLocals <> nil then begin
FLocals.RemoveFreeNotification(@DoLocalsFreed_DecRef);
if FResults = nil then begin
FLocals.Validity := ddsInvalid;
FLocals := nil;
UnQueue_DecRef;
exit;
end;
FLocals.BeginUpdate;
for i := 0 to FResults.Count - 1 do begin
r := FResults[i];
rv := FLocals.Add(r.Name);
rv.CreatePrePrinted(r.Value);
end;
FLocals.Validity := ddsValid; FLocals.Validity := ddsValid;
FLocals.EndUpdate; FLocals.EndUpdate;
FLocals := nil; FLocals := nil;
end; end;
@ -1036,8 +1007,8 @@ begin
exit; exit;
end end
else begin else begin
FLocals.RemoveFreeNotification(@DoLocalsFreed_DecRef);
FLocals.Validity := ddsInvalid; FLocals.Validity := ddsInvalid;
FLocals.EndUpdate;
end; end;
FLocals := nil; FLocals := nil;
end; end;
@ -1050,7 +1021,7 @@ begin
// Runs in IDE thread (TThread.Queue) // Runs in IDE thread (TThread.Queue)
assert(system.ThreadID = classes.MainThreadID, 'TFpThreadWorkerLocals.Create: system.ThreadID = classes.MainThreadID'); assert(system.ThreadID = classes.MainThreadID, 'TFpThreadWorkerLocals.Create: system.ThreadID = classes.MainThreadID');
FLocals := ALocals; FLocals := ALocals;
FLocals.AddFreeNotification(@DoLocalsFreed_DecRef); FLocals.BeginUpdate;
FThreadId := ALocals.ThreadId; FThreadId := ALocals.ThreadId;
FStackFrame := ALocals.StackFrame; FStackFrame := ALocals.StackFrame;
inherited Create(ADebugger, twpLocal); inherited Create(ADebugger, twpLocal);

View File

@ -191,18 +191,11 @@ type
TFpThreadWorkerLocals = class(TFpDbgDebggerThreadWorkerLinkedItem) TFpThreadWorkerLocals = class(TFpDbgDebggerThreadWorkerLinkedItem)
protected type protected type
TResultEntry = record
Name, Value: String;
class operator = (a, b: TResultEntry): Boolean;
end;
TResultList = specialize TFPGList<TResultEntry>;
protected protected
FLocals: TLocalsListIntf;
FThreadId, FStackFrame: Integer; FThreadId, FStackFrame: Integer;
FResults: TResultList;
procedure UpdateLocals_DecRef(Data: PtrInt = 0); virtual; abstract; procedure UpdateLocals_DecRef(Data: PtrInt = 0); virtual; abstract;
procedure DoExecute; override; procedure DoExecute; override;
public
destructor Destroy; override;
end; end;
{ TFpThreadWorkerModify } { TFpThreadWorkerModify }
@ -657,24 +650,15 @@ begin
inherited Create(ADebugger, 1, twpThread); inherited Create(ADebugger, 1, twpThread);
end; end;
{ TFpThreadWorkerLocals.TResultEntry }
class operator TFpThreadWorkerLocals.TResultEntry. = (a, b: TResultEntry
): Boolean;
begin
Result := False;
assert(False, 'TFpThreadWorkerLocals.TResultEntry.=: False');
end;
{ TFpThreadWorkerLocals } { TFpThreadWorkerLocals }
procedure TFpThreadWorkerLocals.DoExecute; procedure TFpThreadWorkerLocals.DoExecute;
var var
LocalScope: TFpDbgSymbolScope; LocalScope: TFpDbgSymbolScope;
ProcVal, m: TFpValue; ProcVal, m: TFpValue;
PrettyPrinter: TFpPascalPrettyPrinter;
i: Integer; i: Integer;
r: TResultEntry; WatchResConv: TFpLazDbgWatchResultConvertor;
ResData: TLzDbgWatchDataIntf;
begin begin
LocalScope := FDebugger.DbgController.CurrentProcess.FindSymbolScope(FThreadId, FStackFrame); LocalScope := FDebugger.DbgController.CurrentProcess.FindSymbolScope(FThreadId, FStackFrame);
if (LocalScope = nil) or (LocalScope.SymbolAtAddress = nil) then begin if (LocalScope = nil) or (LocalScope.SymbolAtAddress = nil) then begin
@ -688,39 +672,33 @@ begin
exit; exit;
end; end;
PrettyPrinter := TFpPascalPrettyPrinter.Create(LocalScope.SizeOfAddress); WatchResConv := TFpLazDbgWatchResultConvertor.Create(LocalScope.LocationContext);
PrettyPrinter.Context := LocalScope.LocationContext; WatchResConv.MaxArrayConv := TFpDebugDebuggerProperties(FDebugger.GetProperties).MemLimits.MaxArrayConversionCnt;
// PrettyPrinter.MemManager.DefaultContext := LocalScope.LocationContext; WatchResConv.MaxTotalConv := TFpDebugDebuggerProperties(FDebugger.GetProperties).MemLimits.MaxTotalConversionCnt;
WatchResConv.Debugger := FDebugger;
WatchResConv.ExpressionScope := LocalScope;
FResults := TResultList.Create;
for i := 0 to ProcVal.MemberCount - 1 do begin for i := 0 to ProcVal.MemberCount - 1 do begin
m := ProcVal.Member[i]; m := ProcVal.Member[i];
if m <> nil then begin if m <> nil then begin
if m.DbgSymbol <> nil then ResData := FLocals.Add(m.DbgSymbol.Name);
r.Name := m.DbgSymbol.Name if not WatchResConv.WriteWatchResultData(m, ResData)
else then begin
r.Name := ''; ResData.CreateError('Unknown Error');
//if not StopRequested then // finish getting all names? end;
PrettyPrinter.PrintValue(r.Value, m);
m.ReleaseReference; m.ReleaseReference;
FResults.Add(r);
end; end;
if StopRequested then if StopRequested then
Break; Break;
end; end;
PrettyPrinter.Free;
WatchResConv.Free;
ProcVal.ReleaseReference; ProcVal.ReleaseReference;
LocalScope.ReleaseReference; LocalScope.ReleaseReference;
Queue(@UpdateLocals_DecRef); Queue(@UpdateLocals_DecRef);
end; end;
destructor TFpThreadWorkerLocals.Destroy;
begin
FResults.Free;
inherited Destroy;
end;
{ TFpThreadWorkerModify } { TFpThreadWorkerModify }
procedure TFpThreadWorkerModify.DoExecute; procedure TFpThreadWorkerModify.DoExecute;

View File

@ -7072,7 +7072,9 @@ begin
APath := APath + 'Entry'; APath := APath + 'Entry';
for i := 0 to c - 1 do begin for i := 0 to c - 1 do begin
Add(AConfig.GetValue(APath + IntToStr(i) + '/Expression', ''), Add(AConfig.GetValue(APath + IntToStr(i) + '/Expression', ''),
AConfig.GetValue(APath + IntToStr(i) + '/Value', '')); TWatchResultData.CreateFromXMLConfig(AConfig, APath + IntToStr(i) + '/')
);
end; end;
end; end;
@ -7086,7 +7088,7 @@ begin
APath := APath + 'Entry'; APath := APath + 'Entry';
for i := 0 to Count - 1 do begin for i := 0 to Count - 1 do begin
AConfig.SetValue(APath + IntToStr(i) + '/Expression', Names[i]); AConfig.SetValue(APath + IntToStr(i) + '/Expression', Names[i]);
AConfig.SetValue(APath + IntToStr(i) + '/Value', Values[i]); Values[i].SaveDataToXMLConfig(AConfig, APath + IntToStr(i) + '/');
end; end;
end; end;
@ -7177,14 +7179,17 @@ var
begin begin
FCurrentResData := FCurrentResData.RootResultData; FCurrentResData := FCurrentResData.RootResultData;
// TODO: maybe create an error entry, if only FNewResultData is missing // TODO: maybe create an error entry, if only FNewResultData is missing
if (FCurrentResData = nil) or (FCurrentResData.FNewResultData = nil) then if (FCurrentResData = nil) then
exit; exit;
if (FCurrentResData.FNewResultData = nil) then begin
FreeAndNil(FCurrentResData);
exit;
end;
FCurrentResData.Done; FCurrentResData.Done;
v := TLocalsValue(CreateEntry); v := TLocalsValue(CreateEntry);
v.Init(FCurrentResName, FCurrentResData.FNewResultData); v.Init(FCurrentResName, FCurrentResData.FNewResultData);
FCurrentResData.FNewResultData := nil;
if IsUpdating then if IsUpdating then
FCurrentResList.Add(v) FCurrentResList.Add(v)

View File

@ -56,6 +56,7 @@ type
procedure SelectNode(Node: PVirtualNode; ASetFocus: boolean = True); procedure SelectNode(Node: PVirtualNode; ASetFocus: boolean = True);
function FindNodeForItem(AnItem: TObject): PVirtualNode; function FindNodeForItem(AnItem: TObject): PVirtualNode;
function FindNodeForText(AText: String; AColumn: integer): PVirtualNode;
procedure DeleteNodeEx(Node: PVirtualNode; FreeItem: Boolean; Reindex: Boolean = True); procedure DeleteNodeEx(Node: PVirtualNode; FreeItem: Boolean; Reindex: Boolean = True);
property NodeItem[Node: PVirtualNode]: TObject read GetNodeItem write SetNodeItem; property NodeItem[Node: PVirtualNode]: TObject read GetNodeItem write SetNodeItem;
@ -350,6 +351,18 @@ begin
Result := nil; Result := nil;
end; end;
function TDbgTreeView.FindNodeForText(AText: String; AColumn: integer
): PVirtualNode;
var
VNode: PVirtualNode;
begin
for VNode in NoInitNodes do begin
if GetNodeText(VNode, AColumn) = AText then
exit(VNode);
end;
Result := nil;
end;
procedure TDbgTreeView.DeleteNodeEx(Node: PVirtualNode; FreeItem: Boolean; procedure TDbgTreeView.DeleteNodeEx(Node: PVirtualNode; FreeItem: Boolean;
Reindex: Boolean); Reindex: Boolean);
var var

View File

@ -238,13 +238,14 @@ type
TLocalsValue = class(TDbgEntityValue) TLocalsValue = class(TDbgEntityValue)
private private
FName: String; FName: String;
FValue: String; FValue: TWatchResultData;
protected protected
procedure DoAssign(AnOther: TDbgEntityValue); override; procedure DoAssign(AnOther: TDbgEntityValue); override;
public public
destructor Destroy; override;
procedure Init(AName: String; AValue: TWatchResultData); procedure Init(AName: String; AValue: TWatchResultData);
property Name: String read FName; property Name: String read FName;
property Value: String read FValue; property Value: TWatchResultData read FValue;
end; end;
{ TLocals } { TLocals }
@ -253,17 +254,17 @@ type
private private
function GetEntry(AnIndex: Integer): TLocalsValue; function GetEntry(AnIndex: Integer): TLocalsValue;
function GetName(const AnIndex: Integer): String; function GetName(const AnIndex: Integer): String;
function GetValue(const AnIndex: Integer): String; function GetValue(const AnIndex: Integer): TWatchResultData;
protected protected
function CreateEntry: TDbgEntityValue; override; function CreateEntry: TDbgEntityValue; override;
public public
procedure Add(const AName, AValue: String); overload; deprecated; procedure Add(const AName: String; AValue: TWatchResultData); overload; deprecated;
procedure SetDataValidity({%H-}AValidity: TDebuggerDataState); virtual; procedure SetDataValidity({%H-}AValidity: TDebuggerDataState); virtual;
public public
function Count: Integer;reintroduce; virtual; function Count: Integer;reintroduce; virtual;
property Entries[AnIndex: Integer]: TLocalsValue read GetEntry; property Entries[AnIndex: Integer]: TLocalsValue read GetEntry;
property Names[const AnIndex: Integer]: String read GetName; property Names[const AnIndex: Integer]: String read GetName;
property Values[const AnIndex: Integer]: String read GetValue; property Values[const AnIndex: Integer]: TWatchResultData read GetValue;
end; end;
{ TLocalsList } { TLocalsList }
@ -975,14 +976,20 @@ procedure TLocalsValue.DoAssign(AnOther: TDbgEntityValue);
begin begin
inherited DoAssign(AnOther); inherited DoAssign(AnOther);
FName := TLocalsValue(AnOther).FName; FName := TLocalsValue(AnOther).FName;
FValue := TLocalsValue(AnOther).FValue; FValue.Free;
FValue := TLocalsValue(AnOther).FValue.CreateCopy();
end;
destructor TLocalsValue.Destroy;
begin
inherited Destroy;
FreeAndNil(FValue);
end; end;
procedure TLocalsValue.Init(AName: String; AValue: TWatchResultData); procedure TLocalsValue.Init(AName: String; AValue: TWatchResultData);
begin begin
FName := AName; FName := AName;
FValue := AValue.AsString; FValue := AValue;
AValue.Free;
end; end;
{ TLocalsList } { TLocalsList }
@ -1009,7 +1016,7 @@ begin
Result := Entries[AnIndex].Name; Result := Entries[AnIndex].Name;
end; end;
function TLocals.GetValue(const AnIndex: Integer): String; function TLocals.GetValue(const AnIndex: Integer): TWatchResultData;
begin begin
Result := Entries[AnIndex].Value; Result := Entries[AnIndex].Value;
end; end;
@ -1019,7 +1026,7 @@ begin
Result := TLocalsValue.Create; Result := TLocalsValue.Create;
end; end;
procedure TLocals.Add(const AName, AValue: String); procedure TLocals.Add(const AName: String; AValue: TWatchResultData);
var var
v: TLocalsValue; v: TLocalsValue;
begin begin

View File

@ -5,33 +5,42 @@ object LocalsDlg: TLocalsDlg
Width = 500 Width = 500
HorzScrollBar.Page = 499 HorzScrollBar.Page = 499
VertScrollBar.Page = 199 VertScrollBar.Page = 199
ActiveControl = lvLocals
BorderStyle = bsSizeToolWin BorderStyle = bsSizeToolWin
Caption = 'Locals' Caption = 'Locals'
ClientHeight = 200 ClientHeight = 200
ClientWidth = 500 ClientWidth = 500
LCLVersion = '2.1.0.0' LCLVersion = '2.3.0.0'
object lvLocals: TListView object vtLocals: TDbgTreeView
Left = 0 Left = 0
Height = 200 Height = 200
Top = 0 Top = 0
Width = 500 Width = 500
Align = alClient Align = alClient
Columns = < Header.AutoSizeIndex = 0
Header.Columns = <
item item
Caption = 'Name' Position = 0
Width = 150
end end
item item
Caption = 'Value' Position = 2
end
item
MaxWidth = 300
Options = [coAllowClick, coDraggable, coEnabled, coParentBidiMode, coParentColor, coResizable, coShowDropMark, coAllowFocus, coEditable]
Position = 1
end> end>
MultiSelect = True Header.Options = [hoColumnResize, hoDrag, hoShowSortGlyphs, hoVisible]
PopupMenu = PopupMenu1 PopupMenu = PopupMenu1
ReadOnly = True
RowSelect = True
SortType = stText
TabOrder = 0 TabOrder = 0
ViewStyle = vsReport TreeOptions.AutoOptions = [toAutoScrollOnExpand, toAutoSort, toAutoTristateTracking, toAutoDeleteMovedNodes, toAutoChangeScale]
TreeOptions.MiscOptions = [toFullRepaintOnResize, toInitOnSave, toWheelPanning]
TreeOptions.PaintOptions = [toShowButtons, toShowDropmark, toShowRoot, toShowTreeLines, toThemeAware, toUseBlendedImages, toUseExplorerTheme]
TreeOptions.SelectionOptions = [toFullRowSelect, toMultiSelect, toRightClickSelect]
OnChange = vtLocalsChange
OnDragOver = vtLocalsDragOver
OnDragDrop = vtLocalsDragDrop
OnFocusChanged = vtLocalsFocusChanged
OnNodeDblClick = vtLocalsNodeDblClick
end end
object ActionList1: TActionList object ActionList1: TActionList
Left = 152 Left = 152
@ -83,13 +92,6 @@ object LocalsDlg: TLocalsDlg
OnUpdate = actInspectUpdate OnUpdate = actInspectUpdate
ShortCut = 16451 ShortCut = 16451
end end
object actEvaluateAll: TAction
Category = 'main'
Caption = 'actEvaluateAll'
OnExecute = actEvaluateAllExecute
OnUpdate = actCopyAllUpdate
ShortCut = 16466
end
end end
object PopupMenu1: TPopupMenu object PopupMenu1: TPopupMenu
Left = 38 Left = 38
@ -103,9 +105,6 @@ object LocalsDlg: TLocalsDlg
object MenuItem3: TMenuItem object MenuItem3: TMenuItem
Action = actEvaluate Action = actEvaluate
end end
object MenuItem9: TMenuItem
Action = actEvaluateAll
end
object MenuItem4: TMenuItem object MenuItem4: TMenuItem
Caption = '-' Caption = '-'
end end

View File

@ -44,9 +44,11 @@ uses
// IdeIntf // IdeIntf
IDEWindowIntf, IDEWindowIntf,
// DebuggerIntf // DebuggerIntf
DbgIntfDebuggerBase, LazDebuggerIntf, DbgIntfDebuggerBase, laz.VirtualTrees, LazDebuggerIntf,
// IDE // IDE Debugger
IdeDebuggerStringConstants, BaseDebugManager, EnvironmentOpts, Debugger, DebuggerDlg; IdeDebuggerStringConstants, BaseDebugManager, EnvironmentOpts, Debugger,
DebuggerDlg, IdeDebuggerWatchResPrinter, IdeDebuggerUtils, DebuggerTreeView,
IdeDebuggerWatchResult, IdeDebuggerBase, Controls, ActiveX;
type type
@ -59,10 +61,9 @@ type
actCopyValue: TAction; actCopyValue: TAction;
actCopyAll: TAction; actCopyAll: TAction;
actCopyRAWValue: TAction; actCopyRAWValue: TAction;
actEvaluateAll: TAction;
actWath: TAction; actWath: TAction;
ActionList1: TActionList; ActionList1: TActionList;
lvLocals: TListView; vtLocals: TDbgTreeView;
MenuItem1: TMenuItem; MenuItem1: TMenuItem;
MenuItem2: TMenuItem; MenuItem2: TMenuItem;
MenuItem3: TMenuItem; MenuItem3: TMenuItem;
@ -71,28 +72,38 @@ type
MenuItem6: TMenuItem; MenuItem6: TMenuItem;
MenuItem7: TMenuItem; MenuItem7: TMenuItem;
MenuItem8: TMenuItem; MenuItem8: TMenuItem;
MenuItem9: TMenuItem;
PopupMenu1: TPopupMenu; PopupMenu1: TPopupMenu;
procedure actCopyAllExecute(Sender: TObject); procedure actCopyAllExecute(Sender: TObject);
procedure actCopyAllUpdate(Sender: TObject); procedure actCopyAllUpdate(Sender: TObject);
procedure actCopyNameExecute(Sender: TObject); procedure actCopyNameExecute(Sender: TObject);
procedure actCopyValueExecute(Sender: TObject); procedure actCopyValueExecute(Sender: TObject);
procedure actEvaluateAllExecute(Sender: TObject);
procedure actEvaluateExecute(Sender: TObject); procedure actEvaluateExecute(Sender: TObject);
procedure actInspectExecute(Sender: TObject); procedure actInspectExecute(Sender: TObject);
procedure actInspectUpdate(Sender: TObject); procedure actInspectUpdate(Sender: TObject);
procedure actCopyRAWValueExecute(Sender: TObject); procedure actCopyRAWValueExecute(Sender: TObject);
procedure actWathExecute(Sender: TObject); procedure actWathExecute(Sender: TObject);
procedure vtLocalsChange(Sender: TBaseVirtualTree; Node: PVirtualNode);
procedure vtLocalsDragDrop(Sender: TBaseVirtualTree; Source: TObject;
DataObject: IDataObject; Formats: TFormatArray; Shift: TShiftState;
const Pt: TPoint; var Effect: LongWord; Mode: TDropMode);
procedure vtLocalsDragOver(Sender: TBaseVirtualTree; Source: TObject;
Shift: TShiftState; State: TDragState; const Pt: TPoint; Mode: TDropMode;
var Effect: LongWord; var Accept: Boolean);
procedure vtLocalsFocusChanged(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex);
procedure vtLocalsNodeDblClick(Sender: TBaseVirtualTree;
const HitInfo: THitInfo);
private private
FWatchPrinter: TWatchResultPrinter;
FUpdateFlags: set of (ufNeedUpdating); FUpdateFlags: set of (ufNeedUpdating);
EvaluateAllCallbackItem: TListItem; function GetSelected: TLocalsValue; // The focused Selected Node
procedure CopyRAWValueEvaluateCallback(Sender: TObject; ASuccess: Boolean; procedure CopyRAWValueEvaluateCallback(Sender: TObject; ASuccess: Boolean;
ResultText: String; ResultDBGType: TDBGType); ResultText: String; ResultDBGType: TDBGType);
procedure CopyValueEvaluateCallback(Sender: TObject; ASuccess: Boolean; procedure CopyValueEvaluateCallback(Sender: TObject; ASuccess: Boolean;
ResultText: String; ResultDBGType: TDBGType); ResultText: String; ResultDBGType: TDBGType);
procedure EvaluateAllCallback(Sender: TObject; ASuccess: Boolean; procedure DoLocalValueFreed(Sender: TObject);
ResultText: String; ResultDBGType: TDBGType);
procedure ClearTree(OnlyClearNodeData: boolean = False);
procedure LocalsChanged(Sender: TObject); procedure LocalsChanged(Sender: TObject);
function GetThreadId: Integer; function GetThreadId: Integer;
function GetSelectedThreads(Snap: TSnapshot): TIdeThreads; function GetSelectedThreads(Snap: TSnapshot): TIdeThreads;
@ -105,6 +116,7 @@ type
procedure ColSizeSetter(AColId: Integer; ASize: Integer); procedure ColSizeSetter(AColId: Integer; ASize: Integer);
public public
constructor Create(AOwner: TComponent); override; constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
property LocalsMonitor; property LocalsMonitor;
property ThreadsMonitor; property ThreadsMonitor;
property CallStackMonitor; property CallStackMonitor;
@ -112,7 +124,6 @@ type
end; end;
function ValueToRAW(const AValue: string): string; function ValueToRAW(const AValue: string): string;
function ExtractValue(const AValue: string; AType: string = ''): string;
implementation implementation
@ -277,34 +288,76 @@ begin
ThreadsNotification.OnCurrent := @LocalsChanged; ThreadsNotification.OnCurrent := @LocalsChanged;
CallstackNotification.OnCurrent := @LocalsChanged; CallstackNotification.OnCurrent := @LocalsChanged;
SnapshotNotification.OnCurrent := @LocalsChanged; SnapshotNotification.OnCurrent := @LocalsChanged;
FWatchPrinter := TWatchResultPrinter.Create;
Caption:= lisLocals; Caption:= lisLocals;
lvLocals.Columns[0].Caption:= lisName; vtLocals.Header.Columns[0].Text:= lisName;
lvLocals.Columns[1].Caption:= lisValue; vtLocals.Header.Columns[1].Text:= lisValue;
actInspect.Caption := lisInspect; actInspect.Caption := lisInspect;
actWath.Caption := lisWatch; actWath.Caption := lisWatch;
actEvaluate.Caption := lisEvaluateModify; actEvaluate.Caption := lisEvaluateModify;
actEvaluateAll.Caption := lisEvaluateAll;
actCopyName.Caption := lisLocalsDlgCopyName; actCopyName.Caption := lisLocalsDlgCopyName;
actCopyValue.Caption := lisLocalsDlgCopyValue; actCopyValue.Caption := lisLocalsDlgCopyValue;
actCopyRAWValue.Caption := lisLocalsDlgCopyRAWValue; actCopyRAWValue.Caption := lisLocalsDlgCopyRAWValue;
actCopyAll.Caption := lisCopyAll; actCopyAll.Caption := lisCopyAll;
for i := low(COL_WIDTHS) to high(COL_WIDTHS) do for i := low(COL_WIDTHS) to high(COL_WIDTHS) do
lvLocals.Column[i].Width := COL_WIDTHS[i]; vtLocals.Header.Columns[i].Width := COL_WIDTHS[i];
end;
destructor TLocalsDlg.Destroy;
begin
ClearTree;
inherited Destroy;
FWatchPrinter.free;
end; end;
procedure TLocalsDlg.actInspectUpdate(Sender: TObject); procedure TLocalsDlg.actInspectUpdate(Sender: TObject);
begin begin
(Sender as TAction).Enabled := Assigned(lvLocals.Selected); (Sender as TAction).Enabled := Assigned(GetSelected);
end; end;
procedure TLocalsDlg.actCopyRAWValueExecute(Sender: TObject); procedure TLocalsDlg.actCopyRAWValueExecute(Sender: TObject);
var
LVal: TLocalsValue;
ResVal: TWatchResultData;
begin begin
if not DebugBoss.Evaluate(lvLocals.Selected.Caption, @CopyRAWValueEvaluateCallback, []) then LVal := GetSelected;
if LVal = nil then
exit;
ResVal := LVal.Value;
if ResVal = nil then
exit;
if (ResVal.ValueKind <> rdkPrePrinted) then begin
while ResVal <> nil do begin
case ResVal.ValueKind of
rdkVariant: ResVal := ResVal.DerefData;
rdkConvertRes: ResVal := ResVal.ConvertedRes;
//rdkPCharOrString:
else break;
end;
end;
if ResVal.ValueKind in [rdkString, rdkWideString, rdkChar] then begin
Clipboard.Open;
Clipboard.AsText := ResVal.AsString;
Clipboard.Close;
end
else begin
Clipboard.Open;
Clipboard.AsText := ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault));
Clipboard.Close;
end;
exit;
end;
if not DebugBoss.Evaluate(LVal.Name, @CopyRAWValueEvaluateCallback, []) then
begin begin
Clipboard.Open; Clipboard.Open;
Clipboard.AsText := ValueToRAW(lvLocals.Selected.SubItems[0]); Clipboard.AsText := ValueToRAW(ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault)));
Clipboard.Close; Clipboard.Close;
end; end;
end; end;
@ -313,8 +366,13 @@ procedure TLocalsDlg.actWathExecute(Sender: TObject);
var var
S: String; S: String;
Watch: TCurrentWatch; Watch: TCurrentWatch;
LVal: TLocalsValue;
begin begin
S := lvLocals.Selected.Caption; LVal := GetSelected;
if LVal = nil then
exit;
S := LVal.Name;
if s = '' then if s = '' then
exit; exit;
if DebugBoss.Watches.CurrentWatches.Find(S) = nil then if DebugBoss.Watches.CurrentWatches.Find(S) = nil then
@ -332,76 +390,128 @@ begin
DebugBoss.ViewDebugDialog(ddtWatches); DebugBoss.ViewDebugDialog(ddtWatches);
end; end;
procedure TLocalsDlg.vtLocalsChange(Sender: TBaseVirtualTree; Node: PVirtualNode
);
begin
end;
procedure TLocalsDlg.vtLocalsDragDrop(Sender: TBaseVirtualTree;
Source: TObject; DataObject: IDataObject; Formats: TFormatArray;
Shift: TShiftState; const Pt: TPoint; var Effect: LongWord; Mode: TDropMode);
begin
end;
procedure TLocalsDlg.vtLocalsDragOver(Sender: TBaseVirtualTree;
Source: TObject; Shift: TShiftState; State: TDragState; const Pt: TPoint;
Mode: TDropMode; var Effect: LongWord; var Accept: Boolean);
begin
end;
procedure TLocalsDlg.vtLocalsFocusChanged(Sender: TBaseVirtualTree;
Node: PVirtualNode; Column: TColumnIndex);
begin
end;
procedure TLocalsDlg.vtLocalsNodeDblClick(Sender: TBaseVirtualTree;
const HitInfo: THitInfo);
begin
end;
function TLocalsDlg.GetSelected: TLocalsValue;
begin
Result := TLocalsValue(vtLocals.FocusedItem(True));
end;
procedure TLocalsDlg.actInspectExecute(Sender: TObject); procedure TLocalsDlg.actInspectExecute(Sender: TObject);
begin begin
DebugBoss.Inspect(lvLocals.Selected.Caption); if GetSelected <> nil then
DebugBoss.Inspect(GetSelected.Name);
end; end;
procedure TLocalsDlg.actEvaluateExecute(Sender: TObject); procedure TLocalsDlg.actEvaluateExecute(Sender: TObject);
begin begin
DebugBoss.EvaluateModify(lvLocals.Selected.Caption); if GetSelected <> nil then
DebugBoss.EvaluateModify(GetSelected.Name);
end; end;
procedure TLocalsDlg.actCopyNameExecute(Sender: TObject); procedure TLocalsDlg.actCopyNameExecute(Sender: TObject);
begin begin
Clipboard.Open; Clipboard.Open;
Clipboard.AsText := lvLocals.Selected.Caption; if GetSelected <> nil then
Clipboard.AsText := GetSelected.Name
else
Clipboard.AsText := '';
Clipboard.Close; Clipboard.Close;
end; end;
procedure TLocalsDlg.actCopyAllExecute(Sender: TObject); procedure TLocalsDlg.actCopyAllExecute(Sender: TObject);
Var Var
AStringList : TStringList; AStringList : TStringList;
I : Integer; LVal: TLocalsValue;
VNode: PVirtualNode;
begin begin
if lvLocals.Items.Count > 0 then begin AStringList := TStringList.Create;
AStringList := TStringList.Create; for VNode in vtLocals.NoInitNodes do begin
for I := 0 to lvLocals.Items.Count - 1 do LVal := TLocalsValue((vtLocals.NodeItem[VNode]));
AStringList.Values[lvLocals.Items[I].Caption] := lvLocals.Items[I].SubItems[0]; if LVal <> nil then
Clipboard.Open; AStringList.Values[LVal.Name] := ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault));
Clipboard.AsText := AStringList.Text;
Clipboard.Close;
FreeAndNil(AStringList);
end; end;
Clipboard.Open;
Clipboard.AsText := AStringList.Text;
Clipboard.Close;
FreeAndNil(AStringList);
end; end;
procedure TLocalsDlg.actCopyAllUpdate(Sender: TObject); procedure TLocalsDlg.actCopyAllUpdate(Sender: TObject);
begin begin
(Sender as TAction).Enabled := lvLocals.Items.Count > 0; (Sender as TAction).Enabled := vtLocals.ChildCount[nil] > 0;
end; end;
procedure TLocalsDlg.actCopyValueExecute(Sender: TObject); procedure TLocalsDlg.actCopyValueExecute(Sender: TObject);
var
LVal: TLocalsValue;
ResVal: TWatchResultData;
begin begin
if not DebugBoss.Evaluate(lvLocals.Selected.Caption, @CopyValueEvaluateCallback, []) then LVal := GetSelected;
if LVal = nil then
exit;
ResVal := LVal.Value;
if ResVal = nil then
exit;
if (ResVal.ValueKind <> rdkPrePrinted) then begin
Clipboard.Open;
Clipboard.AsText := ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault));
Clipboard.Close;
exit;
end;
if not DebugBoss.Evaluate(LVal.Name, @CopyValueEvaluateCallback, []) then
begin begin
Clipboard.Open; Clipboard.Open;
Clipboard.AsText := lvLocals.Selected.SubItems[0]; Clipboard.AsText := ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault));
Clipboard.Close; Clipboard.Close;
end end
end; end;
procedure TLocalsDlg.actEvaluateAllExecute(Sender: TObject);
var
I: Integer;
begin
for I := 0 to lvLocals.Items.Count-1 do
begin
EvaluateAllCallbackItem := lvLocals.Items[I];
DebugBoss.Evaluate(EvaluateAllCallbackItem.Caption, @EvaluateAllCallback, []);
end;
EvaluateAllCallbackItem := nil;
end;
procedure TLocalsDlg.LocalsChanged(Sender: TObject); procedure TLocalsDlg.LocalsChanged(Sender: TObject);
var var
n, idx: Integer; n: Integer;
List: TStringListUTF8Fast;
Item: TListItem;
Locals: TIDELocals; Locals: TIDELocals;
Snap: TSnapshot; Snap: TSnapshot;
s: String;
LVal: TLocalsValue;
VNode, VN2: PVirtualNode;
begin begin
if (ThreadsMonitor = nil) or (CallStackMonitor = nil) or (LocalsMonitor=nil) then begin if (ThreadsMonitor = nil) or (CallStackMonitor = nil) or (LocalsMonitor=nil) then begin
lvLocals.Items.Clear; ClearTree;
exit; exit;
end; end;
@ -414,7 +524,7 @@ begin
DebugLn(DBG_DATA_MONITORS, ['DebugDataMonitor: TLocalsDlg.LocalsChanged']); DebugLn(DBG_DATA_MONITORS, ['DebugDataMonitor: TLocalsDlg.LocalsChanged']);
if GetStackframe < 0 then begin // TODO need dedicated validity property if GetStackframe < 0 then begin // TODO need dedicated validity property
lvLocals.Items.Clear; ClearTree;
exit; exit;
end; end;
@ -429,54 +539,48 @@ begin
Caption:= lisLocals; Caption:= lisLocals;
end; end;
List := TStringListUTF8Fast.Create; if Locals = nil
then begin
ClearTree;
VNode:= vtLocals.AddChild(nil, nil);
vtLocals.NodeText[VNode, 1] := lisLocalsNotEvaluated;
Exit;
end;
BeginUpdate;
try try
BeginUpdate; ClearTree(True);
try for n := 0 to Locals.Count - 1 do begin
if Locals = nil LVal := Locals.Entries[n];
then begin LVal.AddFreeNotification(@DoLocalValueFreed);
lvLocals.Items.Clear;
Item := lvLocals.Items.Add;
Item.Caption := '';
Item.SubItems.add(lisLocalsNotEvaluated);
Exit;
end;
//Get existing items VNode := vtLocals.FindNodeForText(LVal.Name, 0);
for n := 0 to lvLocals.Items.Count - 1 do if (VNode = nil) then begin
begin VNode := vtLocals.AddChild(nil, LVal);
Item := lvLocals.Items[n]; LVal.AddFreeNotification(@DoLocalValueFreed);
List.AddObject(Item.Caption, Item); end
end; else
vtLocals.NodeItem[VNode] := LVal;
// add/update entries s := ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault));
for n := 0 to Locals.Count - 1 do vtLocals.NodeText[VNode, 0] := LVal.Name;
begin vtLocals.NodeText[VNode, 1] := s;
idx := List.IndexOf(Locals.Names[n]);
if idx = -1
then begin
// New entry
Item := lvLocals.Items.Add;
Item.Caption := Locals.Names[n];
Item.SubItems.Add(ExtractValue(Locals.Values[n]));
end
else begin
// Existing entry
Item := TListItem(List.Objects[idx]);
Item.SubItems[0] := ExtractValue(Locals.Values[n]);
List.Delete(idx);
end;
end;
// remove obsolete entries
for n := 0 to List.Count - 1 do
lvLocals.Items.Delete(TListItem(List.Objects[n]).Index);
finally
EndUpdate;
end; end;
VNode := vtLocals.GetFirstNoInit;
while VNode <> nil do begin
if (vtLocals.NodeItem[VNode] = nil) and (vtLocals.NodeControl[VNode] = nil) then begin
VN2 := VNode;
VNode := vtLocals.GetNextVisibleSiblingNoInit(VNode);
vtLocals.DeleteNode(VN2);
end
else
VNode := vtLocals.GetNextVisibleNoInit(VNode);
end;
finally finally
List.Free; EndUpdate;
end; end;
end; end;
@ -537,31 +641,23 @@ end;
procedure TLocalsDlg.DoBeginUpdate; procedure TLocalsDlg.DoBeginUpdate;
begin begin
lvLocals.BeginUpdate; inherited DoBeginUpdate;
vtLocals.BeginUpdate;
end; end;
procedure TLocalsDlg.DoEndUpdate; procedure TLocalsDlg.DoEndUpdate;
begin begin
inherited DoEndUpdate;
if ufNeedUpdating in FUpdateFlags then LocalsChanged(nil); if ufNeedUpdating in FUpdateFlags then LocalsChanged(nil);
lvLocals.EndUpdate; vtLocals.EndUpdate;
end; vtLocalsChange(nil, nil);
procedure TLocalsDlg.EvaluateAllCallback(Sender: TObject; ASuccess: Boolean;
ResultText: String; ResultDBGType: TDBGType);
begin
if ASuccess then
begin
if Assigned(EvaluateAllCallbackItem) then
EvaluateAllCallbackItem.SubItems[0] := ExtractValue(ResultText, ResultDBGType.TypeName);
end;
FreeAndNil(ResultDBGType);
end; end;
function TLocalsDlg.ColSizeGetter(AColId: Integer; var ASize: Integer): Boolean; function TLocalsDlg.ColSizeGetter(AColId: Integer; var ASize: Integer): Boolean;
begin begin
if (AColId - 1 >= 0) and (AColId - 1 < lvLocals.ColumnCount) then begin if (AColId - 1 >= 0) and (AColId - 1 < vtLocals.Header.Columns.Count) then begin
ASize := lvLocals.Column[AColId - 1].Width; ASize := vtLocals.Header.Columns[AColId - 1].Width;
Result := (ASize <> COL_WIDTHS[AColId - 1]) and (not lvLocals.Column[AColId - 1].AutoSize); Result := (ASize <> COL_WIDTHS[AColId - 1]);
end end
else else
Result := False; Result := False;
@ -570,35 +666,81 @@ end;
procedure TLocalsDlg.ColSizeSetter(AColId: Integer; ASize: Integer); procedure TLocalsDlg.ColSizeSetter(AColId: Integer; ASize: Integer);
begin begin
case AColId of case AColId of
COL_LOCALS_NAME: lvLocals.Column[0].Width := ASize; COL_LOCALS_NAME: vtLocals.Header.Columns[0].Width := ASize;
COL_LOCALS_VALUE: lvLocals.Column[1].Width := ASize; COL_LOCALS_VALUE: vtLocals.Header.Columns[1].Width := ASize;
end; end;
end; end;
procedure TLocalsDlg.CopyRAWValueEvaluateCallback(Sender: TObject; procedure TLocalsDlg.CopyRAWValueEvaluateCallback(Sender: TObject;
ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType); ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType);
var
LVal: TLocalsValue;
begin begin
Clipboard.Open; Clipboard.Open;
if ASuccess then if ASuccess then
Clipboard.AsText := ValueToRAW(ExtractValue(ResultText, ResultDBGType.TypeName)) Clipboard.AsText := ValueToRAW(ExtractValue(ResultText, ResultDBGType.TypeName))
else else
Clipboard.AsText := ValueToRAW(lvLocals.Selected.SubItems[0]); begin
LVal := GetSelected;
if (LVal <> nil) and (LVal.Value <> nil) then begin
Clipboard.Open;
Clipboard.AsText := ValueToRAW(ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault)));
Clipboard.Close;
end;
end;
Clipboard.Close; Clipboard.Close;
FreeAndNil(ResultDBGType); FreeAndNil(ResultDBGType);
end; end;
procedure TLocalsDlg.CopyValueEvaluateCallback(Sender: TObject; procedure TLocalsDlg.CopyValueEvaluateCallback(Sender: TObject;
ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType); ASuccess: Boolean; ResultText: String; ResultDBGType: TDBGType);
var
LVal: TLocalsValue;
begin begin
Clipboard.Open; Clipboard.Open;
if ASuccess then if ASuccess then
Clipboard.AsText := ExtractValue(ResultText, ResultDBGType.TypeName) Clipboard.AsText := ExtractValue(ResultText, ResultDBGType.TypeName)
else else begin
Clipboard.AsText := lvLocals.Selected.SubItems[0]; LVal := GetSelected;
if (LVal <> nil) and (LVal.Value <> nil) then begin
Clipboard.Open;
Clipboard.AsText := ClearMultiline(FWatchPrinter.PrintWatchValue(LVal.Value, wdfDefault));
Clipboard.Close;
end;
end;
Clipboard.Close; Clipboard.Close;
FreeAndNil(ResultDBGType); FreeAndNil(ResultDBGType);
end; end;
procedure TLocalsDlg.DoLocalValueFreed(Sender: TObject);
var
nd: PVirtualNode;
begin
nd := vtLocals.FindNodeForItem(Sender);
if nd = nil then
exit;
//vtLocals.OnItemRemoved := nil;
vtLocals.NodeItem[nd] := nil;
//vtLocals.OnItemRemoved := @DoItemRemovedFromView;
end;
procedure TLocalsDlg.ClearTree(OnlyClearNodeData: boolean);
var
LVal: TLocalsValue;
VNode: PVirtualNode;
begin
for VNode in vtLocals.NoInitNodes do begin
LVal := TLocalsValue(vtLocals.NodeItem[VNode]);
if LVal <> nil then
LVal.RemoveFreeNotification(@DoLocalValueFreed);
vtLocals.NodeItem[VNode] := nil;
end;
if not OnlyClearNodeData then
vtLocals.Clear;
end;
initialization initialization
LocalsDlgWindowCreator := IDEWindowCreators.Add(DebugDialogNames[ddtLocals]); LocalsDlgWindowCreator := IDEWindowCreators.Add(DebugDialogNames[ddtLocals]);