lazarus/ide/packages/idedebugger/idedebuggerwatchresprinter.pas

1625 lines
56 KiB
ObjectPascal

unit IdeDebuggerWatchResPrinter;
{$mode objfpc}{$H+}
{$ModeSwitch advancedrecords}
interface
uses
Classes, SysUtils, Math, IdeDebuggerWatchResult, IdeDebuggerUtils, IdeDebuggerDisplayFormats,
IdeDebuggerBase, IdeDebuggerStringConstants, IdeDebuggerValueFormatter, IdeDebuggerWatchResUtils,
LazDebuggerIntf, LazUTF8, IdeDebuggerWatchValueIntf, StrUtils, LazDebuggerUtils,
LazDebuggerIntfFloatTypes;
type
{ TResolvedDisplayFormatNum }
TResolvedDisplayFormatNum = record
UseInherited: boolean;
Visible: boolean;
BaseFormat: TValueDisplayFormatBase;
SignFormat: TValueDisplayFormatSign;
MinDigits: Integer;
SeparatorDec: boolean;
SeparatorHexBin: TValueDisplayFormatHexSeperator;
class operator = (a,b: TResolvedDisplayFormatNum): boolean;
class operator := (a: TWatchDisplayFormatNum): TResolvedDisplayFormatNum;
class operator := (a: TWatchDisplayFormatNum2): TResolvedDisplayFormatNum;
end;
TResolvedDisplayFormat = record
Num1: TResolvedDisplayFormatNum;
Num2: TResolvedDisplayFormatNum; // Enum, Addr, ... // using "visible"
Enum: TWatchDisplayFormatEnum;
Bool: TWatchDisplayFormatBool;
Char: TWatchDisplayFormatChar;
Float: TWatchDisplayFormatFloat;
Struct: TWatchDisplayFormatStruct; // Ignore Addr part
Pointer: TWatchDisplayFormatPointer; // Ignore Addr part
Address: TWatchDisplayFormatAddr;
end;
{ TDisplayFormatResolver }
TDisplayFormatResolver = class
private
FFallBackFormats: TWatchDisplayFormatList;
public
constructor Create;
destructor Destroy; override;
function ResolveDispFormat(const ADispFormat: TWatchDisplayFormat;
const AResValue: TWatchResultData
): TResolvedDisplayFormat;
function ResolveMultiLine(const ADispFormat: TWatchDisplayFormat): TWatchDisplayFormatMultiline;
function ResolveArrayNavBar(const ADispFormat: TWatchDisplayFormat): TWatchDisplayFormatArrayNav;
function ResolveArrayLen(const ADispFormat: TWatchDisplayFormat): TWatchDisplayFormatArrayLen;
// Resolving from FallBackFormats[n] to FallBackFormats[0]
// [0] must be the IDE global format, and will be used regardless of UseInherited
property FallBackFormats: TWatchDisplayFormatList read FFallBackFormats;
end;
TWatchResultPrinterFormatFlag = (
rpfIndent, // use Indent. Only when MultiLine
rpfMultiLine,
rpfClearMultiLine, // clean up pre-printed data
rpfSkipValueFormatter
);
TWatchResultPrinterFormatFlags = set of TWatchResultPrinterFormatFlag;
{ TWatchResultPrinter }
TWatchResultPrinter = class(TObject, IWatchResultPrinter)
private
FFormatFlags: TWatchResultPrinterFormatFlags;
FLineSeparator: String;
FOnlyValueFormatter: IIdeDbgValueFormatterIntf;
FDefaultValueFormatter, FCurrentValueFormatter, FNextValueFormatter: IIdeDbgValueFormatterIntf;
FWatchedVarName: String;
FTargetAddressSize: integer;
FDisplayFormatResolver: TDisplayFormatResolver;
FNextCallIsValueFormatter: Boolean;
FInValFormNestLevel: integer;
FResValueInFormatter: TWatchResultData;
FWatchedExprInFormatter: String;
FIndentString: String;
FHasLineBreak: Boolean;
FElementCount, FCurrentMultilineLvl, FDeepestMultilineLvl, FDeepestArray: integer;
FParentResValue, FCurrentResValue: TWatchResultData;
FCurrentOuterMostArrayLvl, FOuterMostArrayCount, FCurrentArrayCombineLvl: integer;
FCurrentArrayLenShown: boolean;
protected const
MAX_ALLOWED_NEST_LVL = 100;
protected type
TWatchResStoredSettings = record
FFormatFlags: TWatchResultPrinterFormatFlags;
FCurrentValueFormatter: IIdeDbgValueFormatterIntf;
FResValueInFormatter: TWatchResultData;
end;
protected
procedure StoreSetting(var AStorage: TWatchResStoredSettings); inline;
procedure RestoreSetting(const AStorage: TWatchResStoredSettings); inline;
function PrintNumber(AUnsignedValue: QWord; ASignedValue: Int64;
AByteSize: Integer;
const ANumFormat: TResolvedDisplayFormatNum;
PrintNil: Boolean = False
): String;
function PrintArray(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart;
function PrintStruct(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart;
function PrintConverted(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart;
function PrintProc(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer): TStringBuilderPart;
function PrintMemDump(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer): TStringBuilderPart;
function PrintWatchValueEx(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart;
public
constructor Create;
destructor Destroy; override;
function PrintWatchValue(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; const AWatchedExpr: String): String;
function PrintWatchValueIntf(AResValue: IWatchResultDataIntf; const ADispFormat: TWatchDisplayFormat; AFlags: TWatchResultPrinterFlags = []): String;
function IWatchResultPrinter.PrintWatchValue = PrintWatchValueIntf;
property FormatFlags: TWatchResultPrinterFormatFlags read FFormatFlags write FFormatFlags;
property OnlyValueFormatter: IIdeDbgValueFormatterIntf read FOnlyValueFormatter write FOnlyValueFormatter;
property TargetAddressSize: integer read FTargetAddressSize write FTargetAddressSize;
property DisplayFormatResolver: TDisplayFormatResolver read FDisplayFormatResolver;
end;
const
{$WRITEABLECONST OFF}
FormatBoolToEnum: array [TValueDisplayFormatBool] of TValueDisplayFormatEnum = (vdfEnumName, vdfEnumOrd, vdfEnumNameAndOrd);
FormatCharToEnum: array [TValueDisplayFormatChar] of TValueDisplayFormatEnum = (vdfEnumName, vdfEnumOrd, vdfEnumNameAndOrd);
FormatEnumToBool: array [TValueDisplayFormatEnum] of TValueDisplayFormatBool = (vdfBoolName, vdfBoolOrd, vdfBoolNameAndOrd);
FormatEnumToChar: array [TValueDisplayFormatEnum] of TValueDisplayFormatChar = (vdfCharLetter, vdfCharOrd, vdfCharLetterAndOrd);
implementation
const
{$WRITEABLECONST OFF}
(* NO vdf...default in the below *)
DefaultEnumNum: TResolvedDisplayFormatNum = (
UseInherited: False;
Visible: False;
BaseFormat: vdfBaseDecimal;
SignFormat: vdfSignSigned;
MinDigits: 0;
SeparatorDec: False;
SeparatorHexBin: vdfhsNone;
);
DefaultAddrNum: TResolvedDisplayFormatNum = (
UseInherited: False;
Visible: False;
BaseFormat: vdfBaseHex;
SignFormat: vdfSignUnsigned;
MinDigits: 0;
SeparatorDec: False;
SeparatorHexBin: vdfhsNone;
);
{ TResolvedDisplayFormatNum }
class operator TResolvedDisplayFormatNum. = (a, b: TResolvedDisplayFormatNum): boolean;
begin
Result :=
(a.UseInherited = b.UseInherited) and
(a.Visible = b.Visible) and
(a.BaseFormat = b.BaseFormat) and
(a.SignFormat = b.SignFormat) and
(a.MinDigits = b.MinDigits) and
(a.SeparatorDec = b.SeparatorDec) and
(a.SeparatorHexBin = b.SeparatorHexBin)
;
end;
class operator TResolvedDisplayFormatNum. := (a: TWatchDisplayFormatNum
): TResolvedDisplayFormatNum;
begin
Result.UseInherited := a.UseInherited;
Result.Visible := False;
Result.BaseFormat := a.BaseFormat;
Result.SignFormat := a.SignFormat;
Result.MinDigits := a.MinDigits[a.BaseFormat];
Result.SeparatorDec := a.SeparatorDec;
Result.SeparatorHexBin := a.SeparatorHexBin;
end;
class operator TResolvedDisplayFormatNum. := (a: TWatchDisplayFormatNum2
): TResolvedDisplayFormatNum;
begin
Result.UseInherited := a.UseInherited;
Result.Visible := a.Visible;
Result.BaseFormat := a.BaseFormat;
Result.SignFormat := a.SignFormat;
Result.MinDigits := a.MinDigits[a.BaseFormat];
Result.SeparatorDec := a.SeparatorDec;
Result.SeparatorHexBin := a.SeparatorHexBin;
end;
{ TDisplayFormatResolver }
constructor TDisplayFormatResolver.Create;
begin
inherited Create;
FFallBackFormats := TWatchDisplayFormatList.Create;
end;
destructor TDisplayFormatResolver.Destroy;
begin
inherited Destroy;
FFallBackFormats.Free;
end;
function TDisplayFormatResolver.ResolveDispFormat(const ADispFormat: TWatchDisplayFormat;
const AResValue: TWatchResultData): TResolvedDisplayFormat;
procedure ResolveSign(var ASign: TValueDisplayFormatSign; ABase: TValueDisplayFormatBase);
begin
if ASign = vdfSignAuto then begin
if ABase = vdfBaseDecimal then
ASign := vdfSignSigned
else
ASign := vdfSignUnsigned;
end;
end;
procedure ResolveSign(var ASign: TValueDisplayFormatSign; ADef: TValueDisplayFormatSign);
begin
if ASign = vdfSignAuto then
ASign := ADef;
end;
procedure ResolveMinDigits(var AMinDigits: Integer; ABase: TValueDisplayFormatBase);
begin
if AMinDigits = 0 then begin
if ABase = vdfBaseHex then
AMinDigits := -1;
end;
end;
procedure ResolveMinDigitsToFull(var AMinDigits: Integer);
begin
if AMinDigits = 0 then
AMinDigits := -1;
end;
var
i: Integer;
fbf: TWatchDisplayFormat;
begin
case AResValue.ValueKind of
rdkSignedNumVal, rdkUnsignedNumVal: begin
Result.Num1 := ADispFormat.Num;
Result.Num2 := ADispFormat.Num2;
for i := Max(0, FFallBackFormats.Count -1) downto 0 do begin
fbf := FFallBackFormats[i];
if Result.Num1.UseInherited then Result.Num1 := fbf.Num;
if Result.Num2.UseInherited then Result.Num2 := fbf.Num2;
if not(Result.Num1.UseInherited or Result.Num2.UseInherited) then
break;
end;
if AResValue.ValueKind = rdkUnsignedNumVal then begin
ResolveSign(Result.Num1.SignFormat, TValueDisplayFormatSign(vdfSignUnsigned));
ResolveSign(Result.Num2.SignFormat, TValueDisplayFormatSign(vdfSignUnsigned));
end
else begin
ResolveSign(Result.Num1.SignFormat, Result.Num1.BaseFormat);
ResolveSign(Result.Num2.SignFormat, Result.Num2.BaseFormat);
end;
ResolveMinDigits(Result.Num1.MinDigits, Result.Num1.BaseFormat);
ResolveMinDigits(Result.Num2.MinDigits, Result.Num2.BaseFormat);
end;
rdkEnum, rdkSet: begin
Result.Enum := ADispFormat.Enum;
if Result.Enum.UseInherited then begin
i := FFallBackFormats.Count -1;
while (i >0) and (FFallBackFormats[i].Enum.UseInherited) do
dec(i);
Result.Enum := FFallBackFormats[i].Enum;
end;
Result.Num2 := DefaultEnumNum;
Result.Num2.Visible := Result.Enum.MainFormat in [vdfEnumNameAndOrd, vdfEnumOrd];
Result.Num2.BaseFormat := Result.Enum.BaseFormat;
Result.Num2.SignFormat := Result.Enum.SignFormat;
ResolveSign(Result.Num2.SignFormat, DefaultEnumNum.SignFormat);
end;
rdkEnumVal: begin
Result.Enum := ADispFormat.Enum;
if Result.Enum.UseInherited then begin
i := FFallBackFormats.Count -1;
while (i >0) and (FFallBackFormats[i].EnumVal.UseInherited) do
dec(i);
Result.Enum := FFallBackFormats[i].EnumVal;
end;
Result.Num2 := DefaultEnumNum;
Result.Num2.Visible := Result.Enum.MainFormat in [vdfEnumNameAndOrd, vdfEnumOrd];
Result.Num2.BaseFormat := Result.Enum.BaseFormat;
Result.Num2.SignFormat := Result.Enum.SignFormat;
ResolveSign(Result.Num2.SignFormat, DefaultEnumNum.SignFormat);
end;
rdkBool: begin
Result.Bool := ADispFormat.Bool;
if Result.Bool.UseInherited then begin
i := FFallBackFormats.Count -1;
while (i >0) and (FFallBackFormats[i].Bool.UseInherited) do
dec(i);
Result.Bool := FFallBackFormats[i].Bool;
end;
Result.Num2 := DefaultEnumNum;
Result.Num2.Visible := Result.Bool.MainFormat in [vdfBoolNameAndOrd, vdfBoolOrd];
Result.Num2.BaseFormat := Result.Bool.BaseFormat;
Result.Num2.SignFormat := Result.Bool.SignFormat;
ResolveSign(Result.Num2.SignFormat, DefaultEnumNum.SignFormat);
end;
rdkChar: begin
Result.Char := ADispFormat.Char;
if Result.Char.UseInherited then begin
i := FFallBackFormats.Count -1;
while (i >0) and (FFallBackFormats[i].Char.UseInherited) do
dec(i);
Result.Char := FFallBackFormats[i].Char;
end;
Result.Num2 := DefaultEnumNum;
Result.Num2.Visible := Result.Char.MainFormat in [vdfCharLetterAndOrd, vdfCharOrd];
Result.Num2.BaseFormat := Result.Char.BaseFormat;
Result.Num2.SignFormat := Result.Char.SignFormat;
ResolveSign(Result.Num2.SignFormat, DefaultEnumNum.SignFormat);
end;
rdkFloatVal: begin
Result.Float := ADispFormat.Float;
if Result.Float.UseInherited then begin
i := FFallBackFormats.Count -1;
while (i >0) and (FFallBackFormats[i].Float.UseInherited) do
dec(i);
Result.Float := FFallBackFormats[i].Float;
end;
end;
rdkStruct: begin
Result.Struct := ADispFormat.Struct;
Result.Address := ADispFormat.Struct.Address;
for i := Max(0, FFallBackFormats.Count -1) downto 0 do begin
fbf := FFallBackFormats[i];
if Result.Struct.UseInherited then Result.Struct := fbf.Struct;
if Result.Address.UseInherited then Result.Address := fbf.Struct.Address;
if not(Result.Struct.UseInherited or Result.Struct.Address.UseInherited) then
break;
end;
Result.Num2 := DefaultAddrNum;
Result.Num2.Visible := Result.Struct.ShowPointerFormat in [vdfStructPointerOn, vdfStructPointerOnly];
Result.Num2.BaseFormat := Result.Struct.Address.BaseFormat;
if Result.Struct.Address.Signed then
Result.Num2.SignFormat := vdfSignSigned;
ResolveSign(Result.Num2.SignFormat, DefaultAddrNum.SignFormat);
if not Result.Address.NoLeadZero then
ResolveMinDigits(Result.Num2.MinDigits, Result.Num2.BaseFormat);
end;
rdkFunction, rdkProcedure,
rdkFunctionRef, rdkProcedureRef,
rdkPointerVal: begin
Result.Pointer := ADispFormat.Pointer;
Result.Address := ADispFormat.Pointer.Address;
for i := Max(0, FFallBackFormats.Count -1) downto 0 do begin
fbf := FFallBackFormats[i];
if Result.Pointer.UseInherited then Result.Pointer := fbf.Pointer;
if Result.Address.UseInherited then Result.Address := fbf.Pointer.Address;
if not(Result.Pointer.UseInherited or Result.Pointer.Address.UseInherited) then
break;
end;
Result.Num2 := DefaultAddrNum;
Result.Num2.Visible := Result.Pointer.DerefFormat <> vdfPointerDerefOnly;
Result.Num2.BaseFormat := Result.Pointer.Address.BaseFormat;
if Result.Pointer.Address.Signed then
Result.Num2.SignFormat := vdfSignSigned;
ResolveSign(Result.Num2.SignFormat, DefaultAddrNum.SignFormat);
if not Result.Address.NoLeadZero then
ResolveMinDigits(Result.Num2.MinDigits, Result.Num2.BaseFormat);
end;
//rdkString, rdkWideString,
//rdkVariant,
//rdkPCharOrString,
//rdkArray,
//rdkConvertRes,
end;
end;
function TDisplayFormatResolver.ResolveMultiLine(const ADispFormat: TWatchDisplayFormat
): TWatchDisplayFormatMultiline;
var
i: Integer;
begin
Result := ADispFormat.MultiLine;
i := FFallBackFormats.Count-1;
while (Result.UseInherited) and (i > 0) do begin
Result := FFallBackFormats[i].MultiLine;
dec(i);
end;
if (Result.UseInherited) then
Result := DefaultWatchDisplayFormat.MultiLine;
end;
function TDisplayFormatResolver.ResolveArrayNavBar(const ADispFormat: TWatchDisplayFormat
): TWatchDisplayFormatArrayNav;
var
i: Integer;
begin
Result := ADispFormat.ArrayNavBar;
i := FFallBackFormats.Count-1;
while (Result.UseInherited) and (i > 0) do begin
Result := FFallBackFormats[i].ArrayNavBar;
dec(i);
end;
if (Result.UseInherited) then
Result := DefaultWatchDisplayFormat.ArrayNavBar;
end;
function TDisplayFormatResolver.ResolveArrayLen(const ADispFormat: TWatchDisplayFormat
): TWatchDisplayFormatArrayLen;
var
i: Integer;
begin
Result := ADispFormat.ArrayLen;
i := FFallBackFormats.Count-1;
while (Result.UseInherited) and (i > 0) do begin
Result := FFallBackFormats[i].ArrayLen;
dec(i);
end;
if (Result.UseInherited) then
Result := DefaultWatchDisplayFormat.ArrayLen;
end;
{ TWatchResultPrinter }
procedure TWatchResultPrinter.StoreSetting(var AStorage: TWatchResStoredSettings);
begin
AStorage.FFormatFlags := FFormatFlags;
AStorage.FCurrentValueFormatter := FCurrentValueFormatter;
AStorage.FResValueInFormatter := FResValueInFormatter;
end;
procedure TWatchResultPrinter.RestoreSetting(const AStorage: TWatchResStoredSettings);
begin
FFormatFlags := AStorage.FFormatFlags;
FCurrentValueFormatter := AStorage.FCurrentValueFormatter;
FResValueInFormatter := AStorage.FResValueInFormatter;
end;
function TWatchResultPrinter.PrintNumber(AUnsignedValue: QWord; ASignedValue: Int64;
AByteSize: Integer; const ANumFormat: TResolvedDisplayFormatNum; PrintNil: Boolean): String;
function PadNumber(ANum: String; ANewLen, ASepPos: Integer; ASep: Char): String;
var
i, j: Integer;
begin
Result := ANum;
i := Length(Result);
ANewLen := ANewLen + (ANewLen-1) div ASepPos;
SetLength(Result, ANewLen);
inc(ASepPos);
j := 0;
while (ANewLen > i) and (ANewLen > 0) do begin
inc(j);
if j = ASepPos then begin
Result[ANewLen] := ASep;
dec(ANewLen);
j := 1;
end;
if i <= 0 then
Result[ANewLen] := '0'
else
Result[ANewLen] := Result[i];
dec(ANewLen);
dec(i);
end;
end;
var
s: String;
i, d: Integer;
begin
if PrintNil and (AUnsignedValue = 0) then
exit('nil');
Result := '';
s := '';
if (ANumFormat.SignFormat = vdfSignSigned) and (ASignedValue < 0) then
s := '-';
case ANumFormat.BaseFormat of
vdfBaseDecimal: begin
{$PUSH}{$Q-}// abs may overflow
if ANumFormat.SignFormat = vdfSignSigned then Result := IntToStr(qword(abs(ASignedValue)))
{$POP}
else Result := IntToStr(AUnsignedValue);
d := ANumFormat.MinDigits;
if d < 0 then case AByteSize of
1: d := 3;
2: d := 5;
3..4: d := 10;
else d := 20;
end;
i := Length(Result);
if d < i then
d := i;
if ANumFormat.SeparatorDec then
Result := PadNumber(Result, d, 3, FormatSettings.ThousandSeparator)
else
if d > i then
Result := StringOfChar('0', d - i) + Result;
Result := s + Result;
end;
vdfBaseHex: begin
s := s + '$';
if ANumFormat.SignFormat = vdfSignSigned then Result := IntToHex(qword(abs(ASignedValue)), 1)
else Result := IntToHex(AUnsignedValue, 1);
d := ANumFormat.MinDigits;
if d < 0 then case AByteSize of
1: d := 2;
2: d := 4;
3..4: d := 8;
else d := 16;
end;
i := Length(Result);
if d < i then
d := i;
case ANumFormat.SeparatorHexBin of
vdfhsNone: if d > i then Result := StringOfChar('0', d - i) + Result;
vdfhsByte: Result := PadNumber(Result, d, 2, ' ');
vdfhsWord: Result := PadNumber(Result, d, 4, ' ');
vdfhsLong: Result := PadNumber(Result, d, 8, ' ');
end;
Result := s + Result;
end;
vdfBaseOct: begin
s := s + '&';
if ANumFormat.SignFormat = vdfSignSigned then Result := Dec64ToNumb(qword(abs(ASignedValue)), 0 , 8)
else Result := Dec64ToNumb(AUnsignedValue, 0 , 8);
d := ANumFormat.MinDigits;
if d < 0 then case AByteSize of
1: d := 3;
2: d := 6;
3..4: d := 11;
else d := 22;
end;
i := Length(Result);
if d < i then
d := i;
if d > i then Result := StringOfChar('0', d - i) + Result;
Result := s + Result;
end;
vdfBaseBin: begin
s := s + '%';
if ANumFormat.SignFormat = vdfSignSigned then Result := Dec64ToNumb(qword(abs(ASignedValue)), 0 , 2)
else Result := Dec64ToNumb(AUnsignedValue, 0 , 2);
d := ANumFormat.MinDigits;
if d < 0 then case AByteSize of
1: d := 8;
2: d := 16;
3..4: d := 32;
else d := 64;
end;
i := Length(Result);
if d < i then
d := i;
case ANumFormat.SeparatorHexBin of
vdfhsNone: if d > i then Result := StringOfChar('0', d - i) + Result;
vdfhsByte: Result := PadNumber(Result, d, 8, ' ');
vdfhsWord: Result := PadNumber(Result, d, 16, ' ');
vdfhsLong: Result := PadNumber(Result, d, 32, ' ');
end;
Result := s + Result;
end;
vdfBaseChar: begin
if AUnsignedValue <= 31 then
Result := '#'+IntToStr(AUnsignedValue)
else
if AUnsignedValue <= high(Cardinal) then
Result := UnicodeToUTF8(AUnsignedValue)
else
//if ANumFormat.NumCharFormat = vdfNumCharOnlyUnicode then
Result := IntToStr(AUnsignedValue);
end;
else begin // should never happen
Result := IntToStr(AUnsignedValue);
end;
end;
end;
function TWatchResultPrinter.PrintArray(AResValue: TWatchResultData;
const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String
): TStringBuilderPart;
type
TIntegerArray = array of integer;
function CheckArrayIndexes(AnArrayResValue: TWatchResultData;
var AKnownLengthList: TIntegerArray; var AKnownLengthDepth, AMaxAllEqualDepth: Integer;
ACurrentDepth: integer; AnArrayTypes: TLzDbgArrayTypes;
out AnHasNonStatNested: boolean): boolean;
var
r: TWatchResultData;
Len, i, Cnt: Integer;
NestedNonStatNested: Boolean;
begin
Result := False;
AnHasNonStatNested := AnArrayResValue.ArrayType <> datStatArray;
Len := AnArrayResValue.ArrayLength;
if ACurrentDepth > AKnownLengthDepth then begin
AKnownLengthDepth := ACurrentDepth;
AKnownLengthList[ACurrentDepth] := Len;
end
else
if AKnownLengthList[ACurrentDepth] <> Len then
exit;
Result := True;
Cnt := AnArrayResValue.Count;
if (Cnt <> Len) then begin
if (AnArrayResValue.ArrayType <> datStatArray) then begin
if ACurrentDepth < AMaxAllEqualDepth then
AMaxAllEqualDepth := ACurrentDepth;
exit;
end;
AnArrayTypes := [datStatArray]; // all nested must be stat, since we can't test to the end
end;
if ACurrentDepth = AMaxAllEqualDepth then
exit;
if (Len = 0) or (Cnt = 0)
//( (Cnt = 0) and (ACurrentDepth = AKnownLengthDepth))
then begin
if ACurrentDepth < AMaxAllEqualDepth then
AMaxAllEqualDepth := ACurrentDepth;
exit;
end;
for i := 0 to Cnt - 1 do begin
AnArrayResValue.SetSelectedIndex(i);
r := AnArrayResValue.SelectedEntry;
NestedNonStatNested := False;
if (r.ValueKind <> rdkArray) or
( not(r.ArrayType in AnArrayTypes)) or
not CheckArrayIndexes(AnArrayResValue.SelectedEntry, AKnownLengthList, AKnownLengthDepth,
AMaxAllEqualDepth, ACurrentDepth + 1, AnArrayTypes, NestedNonStatNested)
then begin
AnHasNonStatNested := AnHasNonStatNested or NestedNonStatNested;
if ACurrentDepth < AMaxAllEqualDepth then
AMaxAllEqualDepth := ACurrentDepth;
exit;
end;
AnHasNonStatNested := AnHasNonStatNested or NestedNonStatNested;
if not AnHasNonStatNested then // includes current is stat-array
break; // they must all be the same
end;
end;
procedure CheckArrayIndexes(var AnArrayResValue: TWatchResultData;
out AKnownLengthList: TIntegerArray; out AnAllEqualDepth: Integer;
AnArrayTypes: TLzDbgArrayTypes);
var
KnownLenDepth: Integer;
dummy: boolean;
begin
SetLength(AKnownLengthList, 100);
KnownLenDepth := -1;
AnAllEqualDepth := 99;
CheckArrayIndexes(AnArrayResValue, AKnownLengthList, KnownLenDepth, AnAllEqualDepth, 0, AnArrayTypes, dummy);
inc(AnAllEqualDepth);
end;
var
i, Cnt, Len, MaxLen: Integer;
PrefixIdxCnt, OldCurrentOuterMostArrayLvl, OldOuterMostArrayCount,
OldCurrentArrayCombineLvl, ElemCnt, HideLenEach, ForceSingleLineEach, SepLen: Integer;
CurIndentString: String;
tn, sep, sep2, LenStr: String;
OldHasLineBreak, CutOff: Boolean;
ShowLen, ShowCombined, ShowCombinedStatOnly, ShowCombinedDynOnly, OuterMostArray,
CouldHide, OldCurrentArrayLenShown, LoopCurrentArrayLenShown, CouldSingleLine,
ShowMultiLine: Boolean;
MultiLine: TWatchDisplayFormatMultiline;
PrefixIdxList: TIntegerArray;
LenPrefix: TWatchDisplayFormatArrayLen;
ArrayTypes: TLzDbgArrayTypes;
EntryVal: TWatchResultData;
R2: PString;
begin
inc(FCurrentMultilineLvl);
if (ANestLvl > FDeepestArray) then
FDeepestArray := ANestLvl;
if (AResValue.ArrayType = datDynArray) then begin
tn := AResValue.TypeName;
if (AResValue.Count = 0) and (AResValue.DataAddress = 0) then begin
if (ADispFormat.Struct.DataFormat = vdfStructFull) then
Result.RawAsStringPtr^ := AResValue.TypeName + '(nil)'
else
Result.RawAsString := 'nil';
exit;
end;
if (ADispFormat.Struct.ShowPointerFormat = vdfStructPointerOnly) then begin
R2 := Result.RawAsStringPtr;
R2^ := '$'+IntToHex(AResValue.DataAddress, HexDigicCount(AResValue.DataAddress, 4, True));
if tn <> '' then
R2^ := tn + '(' + R2^ + ')';
exit;
end;
end;
CurIndentString := FIndentString;
SepLen := 2; // ', '
MultiLine := FDisplayFormatResolver.ResolveMultiLine(ADispFormat);
ShowMultiLine := (rpfMultiLine in FFormatFlags) and (FCurrentMultilineLvl <= MultiLine.MaxMultiLineDepth);
if ShowMultiLine then begin
SepLen := 1 {,} + Length(FLineSeparator);
if (rpfIndent in FFormatFlags) then begin
SepLen := SepLen + Length(FIndentString);
FIndentString := FIndentString + ' ';
end;
CouldSingleLine := MultiLine.ForceSingleLine;
end
else
CouldSingleLine := False;
ForceSingleLineEach := Max(1, MultiLine.ForceSingleLineThresholdEach);
OldHasLineBreak := FHasLineBreak;
FHasLineBreak := False;
OldCurrentOuterMostArrayLvl := FCurrentOuterMostArrayLvl;
OldOuterMostArrayCount := FOuterMostArrayCount;
OldCurrentArrayCombineLvl := FCurrentArrayCombineLvl;
OldCurrentArrayLenShown := FCurrentArrayLenShown;
try
if (FCurrentMultilineLvl > FDeepestMultilineLvl) and (AResValue.ArrayLength > 0) then
FDeepestMultilineLvl := FCurrentMultilineLvl;
OuterMostArray := (FParentResValue = nil) or (FParentResValue.ValueKind <> rdkArray);
if OuterMostArray then begin
FCurrentOuterMostArrayLvl := ANestLvl;
inc(FOuterMostArrayCount);
FCurrentArrayCombineLvl := 0;
FCurrentArrayLenShown := False;
end;
LenPrefix := FDisplayFormatResolver.ResolveArrayLen(ADispFormat);
ShowLen := LenPrefix.ShowLenPrefix and
( (FOuterMostArrayCount = 1) or LenPrefix.ShowLenPrefixEmbedded ) and
(AResValue.ArrayLength > 0) and
(ANestLvl <= FCurrentOuterMostArrayLvl + LenPrefix.LenPrefixMaxNest) and
(ANestLvl >= FCurrentOuterMostArrayLvl + FCurrentArrayCombineLvl);
if ShowLen then begin
ShowCombinedDynOnly := (LenPrefix.LenPrefixCombine = vdfatDyn) and (AResValue.ArrayType = datDynArray);
ShowCombinedStatOnly := (LenPrefix.LenPrefixCombine = vdfatStat) and (AResValue.ArrayType = datStatArray);
ShowCombined := OuterMostArray and (
(LenPrefix.LenPrefixCombine = vdfatAll) or
ShowCombinedStatOnly or ShowCombinedDynOnly
);
if ShowCombinedDynOnly then
ArrayTypes := [datDynArray]
else
if ShowCombinedStatOnly then
ArrayTypes := [datStatArray]
else
ArrayTypes := [Low(TLzDbgArrayType)..High(TLzDbgArrayType)];
end;
if ShowLen then begin
if ShowCombined then begin
CheckArrayIndexes(AResValue, PrefixIdxList, PrefixIdxCnt, ArrayTypes);
FCurrentArrayCombineLvl := PrefixIdxCnt;
end
else begin
SetLength(PrefixIdxList, 1);
PrefixIdxList[0] := AResValue.ArrayLength;
PrefixIdxCnt := 1;
end;
ShowLen := PrefixIdxCnt > 0;
if ShowLen then begin
LenStr := '';
for i := 0 to PrefixIdxCnt - 2 do
LenStr := LenStr + IntToStr(PrefixIdxList[i]) + ', ';
LenStr := LenStr + IntToStr(PrefixIdxList[PrefixIdxCnt-1]);
if PrefixIdxCnt > 1 then
LenStr := '('+LenStr+')';
LenStr := Format(drsLen2, [LenStr]);
end;
end;
CouldHide := LenPrefix.HideLen and (AResValue.ArrayLength <= LenPrefix.HideLenThresholdCnt);
if AResValue.ArrayLength = 0 then begin
if ShowLen and not CouldHide then
Result.RawAsStringPtr^ := LenStr + '()'
else
Result.RawAsString := '()';
exit;
end;
Cnt := AResValue.Count;
CutOff := (Cnt < AResValue.ArrayLength);
if CutOff then begin
Result.RawPartCount := Cnt + 1;
end
else
Result.RawPartCount := Cnt;
LoopCurrentArrayLenShown := FCurrentArrayLenShown;
FCurrentArrayLenShown := False;
HideLenEach := Max(1, LenPrefix.HideLenThresholdEach);
MaxLen := 1000*1000 div Max(1, ANestLvl*4);
Len := 0;
if Cnt > 0 then
dec(FElementCount);
for i := 0 to Cnt - 1 do begin
AResValue.SetSelectedIndex(i);
ElemCnt := FElementCount;
EntryVal := AResValue.SelectedEntry;
Result.RawParts[i] := PrintWatchValueEx(EntryVal, ADispFormat, ANestLvl, AWatchedExpr);
Len := Len + Result.PartsTotalLen[i] + SepLen;
if CouldHide and (
( (LenPrefix.HideLenThresholdLen > 0) and
(Result.PartsTotalLen[i] > LenPrefix.HideLenThresholdLen)
) or
(FElementCount - ElemCnt > HideLenEach) or
( (LenPrefix.HideLenThresholdEach = 0) and (EntryVal.ValueKind in [rdkArray, rdkStruct]) )
)
then
CouldHide := False;
if CouldSingleLine and (
( (MultiLine.ForceSingleLineThresholdLen > 0) and
(Result.PartsTotalLen[i] > MultiLine.ForceSingleLineThresholdLen)
) or
(FElementCount - ElemCnt > ForceSingleLineEach) or
( (MultiLine.ForceSingleLineThresholdEach = 0) and (EntryVal.ValueKind in [rdkArray, rdkStruct]) )
)
then
CouldSingleLine := False;
if Len > MaxLen then begin
CutOff := True;
Cnt := i+1;
Result.RawChangePartCount(Cnt+1);
end;
end;
if FCurrentArrayLenShown then
CouldHide := False;
FCurrentArrayLenShown := FCurrentArrayLenShown or LoopCurrentArrayLenShown;
if FHasLineBreak or
( ( (ANestLvl < FDeepestArray) or (MultiLine.ForceSingleLineReverseDepth <= 1) ) and
(AResValue.FieldCount > MultiLine.ForceSingleLineThresholdStructFld) ) or
(Min(FDeepestMultilineLvl, MultiLine.MaxMultiLineDepth) - FCurrentMultilineLvl >= MultiLine.ForceSingleLineReverseDepth)
then
CouldSingleLine := False;
if CutOff then begin
Result.RawPartsAsString[Cnt] := '...';
inc(Cnt);
end;
if ShowMultiLine and not CouldSingleLine then begin
if (rpfIndent in FFormatFlags) then begin
sep := ',' + FLineSeparator + FIndentString;
sep2 := FLineSeparator + FIndentString;
end
else begin
sep := ',' + FLineSeparator;
sep2 := FLineSeparator;
end;
if Result.RawPartCount > 1 then
FHasLineBreak := True;
end
else begin
sep := ', ';
sep2 := '';
end;
Result.RawSeparator := sep;
//if (Cnt > 1) and (not CouldSingleLine) then
if FHasLineBreak and (not CouldSingleLine) then
Result.RawPostfix := sep2 +')'
else
Result.RawPostfix := ')';
if CouldHide and
(ANestLvl - FCurrentOuterMostArrayLvl >= LenPrefix.HideLenKeepDepth)
then
ShowLen := False;
if ShowLen then begin
Result.RawPrefix := LenStr + sep2 + '(';
FCurrentArrayLenShown := True;
end
else
Result.RawPrefix := '(';
if OldHasLineBreak then
FHasLineBreak := True;
finally
if FCurrentOuterMostArrayLvl = ANestLvl then
FCurrentArrayLenShown := OldCurrentArrayLenShown;
FCurrentOuterMostArrayLvl := OldCurrentOuterMostArrayLvl;
FOuterMostArrayCount := OldOuterMostArrayCount;
FCurrentArrayCombineLvl := OldCurrentArrayCombineLvl;
FIndentString := CurIndentString;
end;
end;
function TWatchResultPrinter.PrintStruct(AResValue: TWatchResultData;
const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String
): TStringBuilderPart;
const
VisibilityNames: array [TLzDbgFieldVisibility] of string = (
'', 'private', 'protected', 'public', 'published'
);
var
Resolved: TResolvedDisplayFormat;
FldInfo: TWatchResultDataFieldInfo;
FldOwner: TWatchResultData;
vis, sep, tn, Header, we, CurIndentString: String;
InclVisSect, OldHasLineBreak, CouldSingleLine, ShowMultiLine: Boolean;
MultiLine: TWatchDisplayFormatMultiline;
FldIdx, Len, ForceSingleLineEach, ElemCnt, SepLen, Cnt: Integer;
RR: TStringBuilderPart;
begin
inc(FCurrentMultilineLvl);
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
tn := AResValue.TypeName;
Header := '';
if (AResValue.StructType in [dstClass, dstInterface])
then begin
if (AResValue.DataAddress = 0) then begin
//Header := PrintNumber(0, 0, FTargetAddressSize, Resolved.Num2);
if (Resolved.Address.TypeFormat = vdfAddressTyped) and (tn <> '') then
Result.RawAsStringPtr^ := tn + '(nil)'
else
Result.RawAsString := 'nil';
exit;
end;
if (Resolved.Struct.ShowPointerFormat <> vdfStructPointerOff) or (AResValue.FieldCount = 0)
then begin
// TODO: for 32 bit target, sign extend the 2nd argument
Header := PrintNumber(AResValue.DataAddress, Int64(AResValue.DataAddress), FTargetAddressSize, Resolved.Num2, True);
if (Resolved.Address.TypeFormat = vdfAddressTyped) and (tn <> '') then begin
Header := tn + '(' + Header + ')';
tn := '';
end;
if (Resolved.Struct.ShowPointerFormat = vdfStructPointerOnly) or (AResValue.FieldCount = 0) then begin
Result.RawAsString := Header;
exit;
end;
if Header <> '' then
Header := Header + ': ';
end;
end
else
if (Resolved.Struct.DataFormat <> vdfStructFull)
//and not(AResValue.StructType in [dstClass, dstInterface])
then
tn := '';
if AResValue.FieldCount = 0 then begin
Result.RawAsStringPtr^ := Header + tn + '()';
exit;
end;
if (FCurrentMultilineLvl > FDeepestMultilineLvl) and (AResValue.FieldCount > 0) then
FDeepestMultilineLvl := FCurrentMultilineLvl;
CurIndentString := FIndentString;
SepLen := 1; // ' ' space
MultiLine := FDisplayFormatResolver.ResolveMultiLine(ADispFormat);
ShowMultiLine := (rpfMultiLine in FFormatFlags) and (FCurrentMultilineLvl <= MultiLine.MaxMultiLineDepth);
if ShowMultiLine then begin
SepLen := Length(FLineSeparator);
if (rpfIndent in FFormatFlags) then begin
SepLen := SepLen + Length(FIndentString);
FIndentString := FIndentString + ' ';
end;
CouldSingleLine := MultiLine.ForceSingleLine and (AResValue.FieldCount <= MultiLine.ForceSingleLineThresholdStructFld);
end
else
CouldSingleLine := False;
ForceSingleLineEach := Max(1, MultiLine.ForceSingleLineThresholdEach);
OldHasLineBreak := FHasLineBreak;
FHasLineBreak := False;
we := AWatchedExpr + '.';
InclVisSect := (Resolved.Struct.DataFormat = vdfStructFull) and (AResValue.StructType in [dstClass, dstObject]);
FldOwner := nil;
vis := '';
dec(FElementCount);
FldIdx := 1;
if Resolved.Struct.DataFormat = vdfStructFull then
inc(FldIdx);
if InclVisSect then
inc(FldIdx);
Cnt := AResValue.FieldCount * FldIdx;
Result.RawPartCount := Cnt;
FldIdx := 0;
Len := 0;
for FldInfo in AResValue do begin
if Len > 1 + 1000*1000 div Max(1, ANestLvl*4) then begin
Result.RawPartsAsString[FldIdx] := '...';
inc(FldIdx);
break;
end;
if FldOwner <> FldInfo.Owner then begin
FldOwner := FldInfo.Owner;
vis := '';
if (Resolved.Struct.DataFormat = vdfStructFull) and (FldOwner <> nil) and (FldOwner.DirectFieldCount > 0) and
(AResValue.StructType in [dstClass, dstInterface, dstObject]) // record has no inheritance
then begin
if FldIdx = Cnt then begin
inc(Cnt, 100);
Result.RawChangePartCount(Cnt);
end;
Result.RawPartsAsString[FldIdx] := '{' + FldOwner.TypeName + '}';
Len := Len + Result.PartsTotalLen[FldIdx] + SepLen;
inc(FldIdx);
inc(FElementCount);
end;
end;
if InclVisSect and (vis <> VisibilityNames[FldInfo.FieldVisibility]) then begin
if FldIdx = Cnt then begin
inc(Cnt, 100);
Result.RawChangePartCount(Cnt);
end;
vis := VisibilityNames[FldInfo.FieldVisibility];
Result.RawPartsAsString[FldIdx] := vis;
Len := Len + Result.PartsTotalLen[FldIdx] + SepLen;
inc(FldIdx);
inc(FElementCount);
end;
ElemCnt := FElementCount;
if Resolved.Struct.DataFormat <> vdfStructValOnly then begin
RR.RawPartCount := 2;
RR.RawPartsAsString[0] := FldInfo.FieldName;
RR.RawSeparator := ': ';
RR.RawParts[1] := PrintWatchValueEx(FldInfo.Field, ADispFormat, ANestLvl, we + UpperCase(FldInfo.FieldName));
end
else begin
RR.RawPartCount := 1;
RR.RawParts[0] := PrintWatchValueEx(FldInfo.Field, ADispFormat, ANestLvl, we + UpperCase(FldInfo.FieldName));
end;
RR.RawPostfix := '; ';
if FldIdx = Cnt then begin
inc(Cnt, 100);
Result.RawChangePartCount(Cnt);
end;
Result.RawParts[FldIdx] := RR;
Len := Len + RR.TotalLen + SepLen;
if CouldSingleLine and (
( (MultiLine.ForceSingleLineThresholdLen > 0) and
(Result.PartsTotalLen[FldIdx] > MultiLine.ForceSingleLineThresholdLen)
) or
(FElementCount - ElemCnt > ForceSingleLineEach) or
( (MultiLine.ForceSingleLineThresholdEach = 0) and (FldInfo.Field.ValueKind in [rdkArray, rdkStruct]) )
)
then
CouldSingleLine := False;
inc(FldIdx);
end;
Result.RawChangePartCount(FldIdx);
if FHasLineBreak or
(Min(FDeepestMultilineLvl, MultiLine.MaxMultiLineDepth) - FCurrentMultilineLvl > MultiLine.ForceSingleLineReverseDepth)
then
CouldSingleLine := False;
if ShowMultiLine and not CouldSingleLine then begin
if (rpfIndent in FFormatFlags) then
sep := FLineSeparator + FIndentString
else
sep := FLineSeparator;
end
else
sep := ' ';
Result.RawSeparator := sep;
if FHasLineBreak then
Result.RawPostfix := sep + ')'
else
Result.RawPostfix := ')';
if (Header <> '') or (tn <> '') then begin
Result.RawPrefix := Header + tn + '(' + Sep;
FHasLineBreak := sep <> ' ';
end
else
Result.RawPrefix := '(';
if ((Result.RawPartCount > 1) and (Sep <> ' ')) or OldHasLineBreak then
FHasLineBreak := True;
FIndentString := CurIndentString;
end;
function TWatchResultPrinter.PrintConverted(AResValue: TWatchResultData;
const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String
): TStringBuilderPart;
begin
if AResValue.FieldCount = 0 then begin
Result.RawAsString := 'Error: No result';
exit;
end;
if (AResValue.FieldCount = 1) or
( (AResValue.Fields[0].Field <> nil) and
((AResValue.Fields[0].Field.ValueKind <> rdkError))
)
then begin
Result := PrintWatchValueEx(AResValue.Fields[0].Field, ADispFormat, ANestLvl, AWatchedExpr);
exit;
end;
if (AResValue.FieldCount > 1) then begin
if (AResValue.Fields[0].Field = nil) or
(AResValue.Fields[0].Field.ValueKind <> rdkError) or
(AResValue.Fields[0].Field.AsString <> '')
then begin
Result.RawPartCount := 4;
Result.RawParts[0] := PrintWatchValueEx(AResValue.Fields[1].Field, ADispFormat, ANestLvl, AWatchedExpr);
Result.RawPartsAsString[1] := ' { ';
Result.RawParts[2] := PrintWatchValueEx(AResValue.Fields[0].Field, ADispFormat, ANestLvl, AWatchedExpr);
Result.RawPartsAsString[3] := ' }';
end
else
Result := PrintWatchValueEx(AResValue.Fields[1].Field, ADispFormat, ANestLvl, AWatchedExpr);
exit;
end;
Result.RawAsString := 'Error: No result';
end;
function TWatchResultPrinter.PrintProc(AResValue: TWatchResultData;
const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer): TStringBuilderPart;
var
Resolved: TResolvedDisplayFormat;
s, R: String;
begin
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
R := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, TargetAddressSize, Resolved.Num2, True);
if AResValue.AsString <> '' then
R := R + ' = ' + AResValue.AsString;
if ANestLvl > 0 then begin
s := AResValue.TypeName;
end
else begin
s := AResValue.AsDesc;
if s = '' then
s := AResValue.TypeName;
end;
if s <> '' then
if AResValue.ValueKind in [rdkFunctionRef, rdkProcedureRef] then
R := R + ': '+s
else
R := s + ' AT ' +R;
Result.RawAsString := R;
end;
function TWatchResultPrinter.PrintMemDump(AResValue: TWatchResultData;
const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer
): TStringBuilderPart;
const
HDIG = '0123456789ABCDEF';
var
r: PString;
m: RawByteString;
s: String;
i, l, j, b: Integer;
begin
m := AResValue.AsString;
r := Result.RawAsStringPtr;
SetLength(r^, Length(m)*4 + Max(20, FTargetAddressSize*2+4));
i := 1;
l := 0;
if AResValue.DataAddress <> 0 then begin
s := IntToHex(AResValue.DataAddress, FTargetAddressSize*2)+':';
move(s[1], r^[i], Length(s));
inc(i, Length(s));
if (rpfMultiLine in FFormatFlags) and (Length(FLineSeparator) > 0) then begin
move(FLineSeparator[1], r^[i], Length(FLineSeparator));
inc(i, Length(FLineSeparator));
end
else begin
r^[i] := ' '; inc(i);
end;
l := i;
end;
for j := 1 to Length(m) do begin
b := ord(m[j]);
r^[i] := HDIG[(b div 16)+1]; inc(i);
r^[i] := HDIG[(b and 15)+1]; inc(i);
l := i;
if ((j and 15) = 0) then begin
if (rpfMultiLine in FFormatFlags) and (Length(FLineSeparator) > 0) then begin
move(FLineSeparator[1], r^[i], Length(FLineSeparator));
inc(i, Length(FLineSeparator));
l := i;
end
else begin
r^[i] := ' '; inc(i);
r^[i] := ' '; inc(i);
r^[i] := ' '; inc(i);
end;
end
else
if ((j and 7) = 0) then begin
r^[i] := ' '; inc(i);
r^[i] := ' '; inc(i);
end
else begin
r^[i] := ' '; inc(i);
end;
end;
SetLength(r^, l+1);
end;
function TWatchResultPrinter.PrintWatchValueEx(AResValue: TWatchResultData;
const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String
): TStringBuilderPart;
function PrintChar: String;
var
Resolved: TResolvedDisplayFormat;
s: String;
begin
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
result := '';
s := '';
if Resolved.Char.MainFormat <> vdfCharOrd then begin
s := ' = ';
case AResValue.ByteSize of
//1: Result := QuoteText(SysToUTF8(char(Byte(AResValue.AsQWord))));
1: Result := QuoteText(char(Byte(AResValue.AsQWord)));
2: Result := QuoteWideText(WideChar(Word(AResValue.AsQWord)));
else
if Resolved.Char.MainFormat <> vdfCharLetter then
Result := '#' + IntToStr(AResValue.AsQWord)
else
Result := '#' + PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num2);
end;
end;
if Resolved.Char.MainFormat <> vdfCharLetter then begin
Result := Result + s + PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num2);
end;
end;
function PrintBool: String;
var
Resolved: TResolvedDisplayFormat;
c: QWord;
s: String;
begin
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
c := AResValue.AsQWord;
result := '';
if Resolved.Bool.MainFormat <> vdfBoolOrd then begin
if c = 0 then
Result := 'False'
else
Result := 'True';
end;
if Resolved.Bool.MainFormat <> vdfBoolName then begin
s := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num2);
if Result <> '' then
Result := Result + '(' + s + ')'
else
Result := s;
end;
end;
function PrintEnum: String;
var
Resolved: TResolvedDisplayFormat;
s: String;
begin
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
result := '';
s := '';
if Resolved.Enum.MainFormat <> vdfEnumOrd then begin
Result := AResValue.AsString;
if Result = '' then begin
Result := AResValue.TypeName;
if Result <> ''
then Result := Result + '(' + IntToStr(AResValue.AsInt64) + ')'
else Result := IntToStr(AResValue.AsInt64);
end;
s := ' = ';
end;
if Resolved.Enum.MainFormat <> vdfEnumName then begin
Result := Result + s + PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num2);
end;
end;
function PrintSet: String;
var
i: Integer;
begin
Result := '';
for i := 0 to AResValue.Count - 1 do
Result := Result + ',' + AResValue.ElementName[i];
if Result = '' then
Result := '[]'
else begin
Result[1] := '[';
Result := Result + ']'
end;
end;
var
PointerValue: TWatchResultDataPointer absolute AResValue;
ResTypeName, R: String;
PtrDeref, PtrDeref2, OldCurrentResValue, OldParentResValue: TWatchResultData;
Resolved: TResolvedDisplayFormat;
n, OldCurrentMultilineLvl: Integer;
StoredSettings: TWatchResStoredSettings;
R2: PString;
begin
inc(ANestLvl);
OldCurrentResValue := FCurrentResValue;
OldParentResValue := FParentResValue;
OldCurrentMultilineLvl := FCurrentMultilineLvl;
FParentResValue := FCurrentResValue;
FCurrentResValue := AResValue;
inc(FElementCount);
try
if ANestLvl > MAX_ALLOWED_NEST_LVL then begin
Result.RawAsString := '...';
exit;
end;
if AResValue = nil then begin
Result.RawAsString := '???';
exit;
end;
if FCurrentValueFormatter <> nil then begin
StoreSetting(StoredSettings);
// for the next IWatchResultPrinter.FormatValue call
FNextCallIsValueFormatter := True;
FInValFormNestLevel := ANestLvl;
FResValueInFormatter := AResValue;
FWatchedExprInFormatter := AWatchedExpr;
//
try
R2 := Result.RawAsStringPtr;
if FCurrentValueFormatter.FormatValue(AResValue, ADispFormat, ANestLvl, Self, R2^, FWatchedVarName, AWatchedExpr) then begin
exit;
end;
finally
FNextCallIsValueFormatter := False;
RestoreSetting(StoredSettings);
end;
end
else
FCurrentValueFormatter := FNextValueFormatter;
Result.Init;
case AResValue.ValueKind of
rdkError:
begin
if rpfClearMultiLine in FFormatFlags then
Result.RawAsStringPtr^ := 'Error: ' + ClearMultiline(AResValue.AsString)
else
Result.RawAsStringPtr^ := 'Error: ' + AResValue.AsString;
end;
rdkUnknown:
Result.RawAsStringPtr^ := 'Error: Unknown';
rdkPrePrinted: begin
if rpfClearMultiLine in FFormatFlags then
Result.RawAsStringPtr^ := ClearMultiline(AResValue.AsString)
else
Result.RawAsStringPtr^ := AResValue.AsString;
end;
rdkSignedNumVal,
rdkUnsignedNumVal: begin
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
R2 := Result.RawAsStringPtr;
R2^ := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num1);
if Resolved.Num2.Visible then begin
R2^ := R2^ +' = ' +
PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num2);
end;
end;
rdkPointerVal: begin
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
R := '';
PtrDeref := PointerValue.DerefData;
if (Resolved.Pointer.DerefFormat = vdfPointerDerefOnly) then
dec(FElementCount);
if (Resolved.Pointer.DerefFormat <> vdfPointerDerefOnly) or (PtrDeref = nil) then begin
n := AResValue.ByteSize;
if n = 0 then n := FTargetAddressSize;
R := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, n, Resolved.Num2, True);
if Resolved.Pointer.Address.TypeFormat = vdfAddressTyped then begin
ResTypeName := AResValue.TypeName;
if (ResTypeName = '') and (PtrDeref <> nil) then begin
ResTypeName := PtrDeref.TypeName;
if ResTypeName <> '' then
ResTypeName := '^'+ResTypeName;
end;
if ResTypeName <> '' then
R := ResTypeName + '(' + R + ')';
end;
end;
if (Resolved.Pointer.DerefFormat <> vdfPointerDerefOff) and (PtrDeref <> nil) then begin
while (PtrDeref.ValueKind = rdkPointerVal) and (PtrDeref.DerefData <> nil) do begin
PtrDeref2 := PtrDeref;
R := R + '^';
PtrDeref := PtrDeref.DerefData;
end;
Result.RawPartCount := 2;
if PtrDeref <> nil then begin
R := R + '^: ';
Result.RawParts[1] := PrintWatchValueEx(PtrDeref, ADispFormat, ANestLvl, AWatchedExpr+'^');
end
else begin
R := R + ': ';
Result.RawParts[1] := PrintWatchValueEx(PtrDeref2, ADispFormat, ANestLvl, AWatchedExpr+'^');
end;
Result.RawPartsAsString[0] := R;
end
else
Result.RawAsString := R;
end;
rdkFloatVal: begin
Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue);
if Resolved.Float.NumFormat = vdfFloatScientific then
case AResValue.FloatPrecission of
dfpSingle: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffExponent, 9, 0);
dfpDouble: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffExponent, 17, 0);
dfpExtended: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffExponent, 21, 0);
end
else
case AResValue.FloatPrecission of
dfpSingle: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffGeneral, 9, 0);
dfpDouble: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffGeneral, 17, 0);
dfpExtended: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffGeneral, 21, 0);
end;
end;
rdkChar: Result.RawAsStringPtr^ := PrintChar;
rdkString: Result.RawAsStringPtr^ := QuoteText(AResValue.AsString);
rdkWideString: Result.RawAsStringPtr^ := QuoteWideText(AResValue.AsWideString);
rdkBool: Result.RawAsStringPtr^ := PrintBool;
rdkEnum, rdkEnumVal:
Result.RawAsStringPtr^ := PrintEnum;
rdkSet: Result.RawAsStringPtr^ := PrintSet;
rdkPCharOrString: begin
Result.RawPartCount := 4;
AResValue.SetSelectedIndex(0); // pchar res
Result.RawPartsAsString[0] := 'PChar: ';
Result.RawParts[1] := PrintWatchValueEx(AResValue.SelectedEntry, ADispFormat, ANestLvl, AWatchedExpr);
AResValue.SetSelectedIndex(1); // string res
if rpfClearMultiLine in FFormatFlags then
Result.RawPartsAsString[2] := ' - String: '
else
Result.RawPartsAsString[2] := FLineSeparator + 'String: ';
Result.RawParts[3] := PrintWatchValueEx(AResValue.SelectedEntry, ADispFormat, ANestLvl, AWatchedExpr);
end;
rdkArray: Result := PrintArray(AResValue, ADispFormat, ANestLvl, AWatchedExpr);
rdkStruct: Result := PrintStruct(AResValue, ADispFormat, ANestLvl, AWatchedExpr);
rdkConvertRes: Result := PrintConverted(AResValue, ADispFormat, ANestLvl, AWatchedExpr);
rdkFunction,
rdkProcedure,
rdkFunctionRef,
rdkProcedureRef: Result := PrintProc(AResValue, ADispFormat, ANestLvl);
rdkVariant: Result := PrintWatchValueEx(AResValue.DerefData, ADispFormat, ANestLvl, AWatchedExpr);
rdkMemDump: Result := PrintMemDump(AResValue, ADispFormat, ANestLvl);
end;
finally
FCurrentResValue := OldCurrentResValue;
FParentResValue := OldParentResValue;
FCurrentMultilineLvl := OldCurrentMultilineLvl;
end;
end;
constructor TWatchResultPrinter.Create;
begin
FFormatFlags := [rpfMultiLine, rpfIndent];
FCurrentMultilineLvl := 0;
FIndentString := '';
FTargetAddressSize := SizeOf(Pointer); // TODO: ask debugger
FDisplayFormatResolver := TDisplayFormatResolver.Create;
end;
destructor TWatchResultPrinter.Destroy;
begin
inherited Destroy;
FDisplayFormatResolver.Free;
end;
function TWatchResultPrinter.PrintWatchValue(AResValue: TWatchResultData;
const ADispFormat: TWatchDisplayFormat; const AWatchedExpr: String): String;
var
Res: TStringBuilderPart;
begin
DisableFloatExceptions;
try
FNextValueFormatter := nil;
if FOnlyValueFormatter <> nil then
FDefaultValueFormatter := FOnlyValueFormatter
else
FDefaultValueFormatter := GlobalValueFormatterSelectorList;
if rpfSkipValueFormatter in FormatFlags then
FCurrentValueFormatter := nil
else
FCurrentValueFormatter := FDefaultValueFormatter;
if rpfMultiLine in FFormatFlags then
FLineSeparator := LineEnding
else
FLineSeparator := ' ';
FWatchedVarName := UpperCase(AWatchedExpr);
FParentResValue := nil;
FCurrentResValue := nil;
FElementCount := 0;
FCurrentMultilineLvl := 0;
FDeepestMultilineLvl := 0;
FDeepestArray := 0;
Res := PrintWatchValueEx(AResValue, ADispFormat, -1, FWatchedVarName);
Result := Res.GetFullString;
Res.FreeAll;
finally
EnableFloatExceptions;
end;
end;
function TWatchResultPrinter.PrintWatchValueIntf(AResValue: IWatchResultDataIntf;
const ADispFormat: TWatchDisplayFormat; AFlags: TWatchResultPrinterFlags): String;
var
AResValObj: TWatchResultData;
IncLvl: Integer;
Res: TStringBuilderPart;
begin
AResValObj := TWatchResultData(AResValue.GetInternalObject);
FNextValueFormatter := nil;
if wpfUseDefaultValueFormatterList in AFlags then
FNextValueFormatter := FCurrentValueFormatter;
if wpfUseCurrentValueFormatterList in AFlags then
FNextValueFormatter := FDefaultValueFormatter;
FCurrentValueFormatter := nil;
IncLvl := 0;
if wpfResValueIsNestedValue in AFlags then begin
if AResValObj <> FResValueInFormatter then
FCurrentValueFormatter := FNextValueFormatter;
IncLvl := 1; // Incrementing the level protects against endless loop
end;
if FNextCallIsValueFormatter then begin
FNextCallIsValueFormatter := False;
Res := PrintWatchValueEx(AResValObj, ADispFormat, FInValFormNestLevel - 1 + IncLvl, FWatchedExprInFormatter); // This will increase it by one, compared to the value given to the formatter
Result := Res.GetFullString;
Res.FreeAll;
end
else begin
// TOOD: full init? Or Call PrintWatchValueEx ?
// TODO: inc level/count of iterations
FParentResValue := nil;
FCurrentResValue := nil;
FElementCount := 0;
FCurrentMultilineLvl := 0;
FDeepestMultilineLvl := 0;
FDeepestArray := 0;
Res := PrintWatchValueEx(AResValObj, ADispFormat, -1, FWatchedExprInFormatter);
Result := Res.GetFullString;
Res.FreeAll;
end;
end;
end.