mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-11 09:16:16 +02:00
1625 lines
56 KiB
ObjectPascal
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.
|
|
|