mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-01 18:23:43 +02:00
919 lines
27 KiB
ObjectPascal
919 lines
27 KiB
ObjectPascal
unit FpPascalBuilder;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, DbgIntfBaseTypes, DbgIntfDebuggerBase, FpDbgInfo, FpdMemoryTools,
|
|
FpErrorMessages, LazLoggerBase;
|
|
|
|
type
|
|
TTypeNameFlag = (
|
|
tnfOnlyDeclared, // do not return a substitute with ^ symbol
|
|
tnfIncludeOneRef // If it is a pointer, and the pointed-to name is known, return ^TXxx
|
|
// without tnfOnlyDeclared, may return ^^^TXxx if needed
|
|
|
|
);
|
|
TTypeNameFlags = set of TTypeNameFlag;
|
|
|
|
TTypeDeclarationFlag = (
|
|
tdfNoFirstLineIndent,
|
|
tdfIncludeVarName, // like i: Integer
|
|
tdfSkipClassBody, // shorten class
|
|
tdfSkipRecordBody, // shorten class
|
|
|
|
tdfDynArrayWithPointer, // TODO, temp, act like gdb
|
|
tdfStopAfterPointer
|
|
);
|
|
TTypeDeclarationFlags = set of TTypeDeclarationFlag;
|
|
|
|
TFpPrettyPrintValueFlag = (
|
|
ppvCreateDbgType,
|
|
ppvSkipClassBody, ppvSkipRecordBody
|
|
);
|
|
TFpPrettyPrintValueFlags = set of TFpPrettyPrintValueFlag;
|
|
|
|
const
|
|
PV_FORWARD_FLAGS = [ppvSkipClassBody, ppvSkipRecordBody];
|
|
|
|
type
|
|
|
|
PDBGType = ^TDBGType;
|
|
|
|
{ TFpPascalPrettyPrinter }
|
|
|
|
TFpPascalPrettyPrinter = class
|
|
private
|
|
FAddressSize: Integer;
|
|
FMemManager: TFpDbgMemManager;
|
|
function InternalPrintValue(out APrintedValue: String;
|
|
AValue: TFpDbgValue;
|
|
AnAddressSize: Integer;
|
|
AFlags: TFpPrettyPrintValueFlags;
|
|
ANestLevel: Integer; AnIndent: String;
|
|
ADisplayFormat: TWatchDisplayFormat;
|
|
ARepeatCount: Integer = -1;
|
|
ADBGTypeInfo: PDBGType = nil
|
|
): Boolean;
|
|
public
|
|
constructor Create(AnAddressSize: Integer);
|
|
function PrintValue(out APrintedValue: String;
|
|
AValue: TFpDbgValue;
|
|
ADisplayFormat: TWatchDisplayFormat = wdfDefault;
|
|
ARepeatCount: Integer = -1
|
|
): Boolean;
|
|
function PrintValue(out APrintedValue: String;
|
|
out ADBGTypeInfo: TDBGType;
|
|
AValue: TFpDbgValue;
|
|
ADisplayFormat: TWatchDisplayFormat = wdfDefault;
|
|
ARepeatCount: Integer = -1
|
|
): Boolean;
|
|
property AddressSize: Integer read FAddressSize write FAddressSize;
|
|
property MemManager: TFpDbgMemManager read FMemManager write FMemManager;
|
|
end;
|
|
|
|
|
|
|
|
function GetTypeName(out ATypeName: String; ADbgSymbol: TFpDbgSymbol; AFlags: TTypeNameFlags = []): Boolean;
|
|
function GetTypeAsDeclaration(out ATypeDeclaration: String; ADbgSymbol: TFpDbgSymbol;
|
|
AFlags: TTypeDeclarationFlags = []; AnIndent: Integer = 0): Boolean;
|
|
|
|
implementation
|
|
|
|
function GetTypeName(out ATypeName: String; ADbgSymbol: TFpDbgSymbol;
|
|
AFlags: TTypeNameFlags): Boolean;
|
|
var
|
|
s: String;
|
|
begin
|
|
ATypeName := '';
|
|
Result := ADbgSymbol <> nil;
|
|
if not Result then
|
|
exit;
|
|
if ADbgSymbol.SymbolType = stValue then begin
|
|
ADbgSymbol := ADbgSymbol.TypeInfo;
|
|
Result := ADbgSymbol <> nil;
|
|
if not Result then
|
|
exit;
|
|
end;
|
|
|
|
ATypeName := ADbgSymbol.Name;
|
|
Result := ATypeName <> '';
|
|
|
|
if (tnfIncludeOneRef in AFlags) or
|
|
((not Result) and (not (tnfOnlyDeclared in AFlags)))
|
|
then begin
|
|
ATypeName := '^';
|
|
while ADbgSymbol.Kind = skPointer do begin
|
|
ADbgSymbol := ADbgSymbol.TypeInfo;
|
|
s := ADbgSymbol.Name;
|
|
if s <> '' then begin
|
|
ATypeName := ATypeName + s;
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
|
|
if (tnfOnlyDeclared in AFlags) then // only one level
|
|
exit;
|
|
ATypeName := ATypeName + '^';
|
|
end;
|
|
|
|
ATypeName := '';
|
|
Result := False;
|
|
end;
|
|
|
|
end;
|
|
|
|
function GetTypeAsDeclaration(out ATypeDeclaration: String; ADbgSymbol: TFpDbgSymbol;
|
|
AFlags: TTypeDeclarationFlags; AnIndent: Integer): Boolean;
|
|
var
|
|
IndentString: String;
|
|
|
|
function GetIndent: String;
|
|
begin
|
|
if (IndentString = '') and (AnIndent > 0) then
|
|
IndentString := StringOfChar(' ', AnIndent);
|
|
Result := IndentString;
|
|
end;
|
|
|
|
function NeedBracket(S: String): Boolean;
|
|
var
|
|
i, l: Integer;
|
|
begin
|
|
l := 0;
|
|
i := length(s);
|
|
while (i > 0) do begin
|
|
case s[i] of
|
|
'a'..'z', 'A'..'Z', '0'..'9', '_', '$', '^': ;
|
|
'(': dec(l);
|
|
')': inc(l);
|
|
else
|
|
if l = 0 then break;
|
|
end;
|
|
dec(i);
|
|
end;
|
|
Result := i > 0;
|
|
end;
|
|
|
|
Function MembersAsGdbText(out AText: String; WithVisibilty: Boolean; AFlags: TTypeDeclarationFlags = []): Boolean;
|
|
var
|
|
CurVis: TDbgSymbolMemberVisibility;
|
|
|
|
procedure AddVisibility(AVis: TDbgSymbolMemberVisibility; AFirst: Boolean);
|
|
begin
|
|
if not (WithVisibilty and ((CurVis <> AVis) or AFirst)) then
|
|
exit;
|
|
CurVis := AVis;
|
|
case AVis of
|
|
svPrivate: AText := AText + GetIndent + ' private' + LineEnding;
|
|
svProtected: AText := AText + GetIndent + ' protected' + LineEnding;
|
|
svPublic: AText := AText + GetIndent + ' public' + LineEnding;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
c, i: Integer;
|
|
m: TFpDbgSymbol;
|
|
s: String;
|
|
begin
|
|
Result := True;
|
|
AText := '';
|
|
c := ADbgSymbol.MemberCount;
|
|
i := 0;
|
|
while (i < c) and Result do begin
|
|
m := ADbgSymbol.Member[i];
|
|
AddVisibility(m.MemberVisibility, i= 0);
|
|
if tdfStopAfterPointer in AFlags then
|
|
Result := GetTypeName(s, m)
|
|
else
|
|
Result := GetTypeAsDeclaration(s, m, [tdfIncludeVarName, tdfStopAfterPointer] + AFlags, AnIndent + 4);
|
|
if Result then
|
|
AText := AText + GetIndent + s + ';' + LineEnding;
|
|
inc(i);
|
|
end;
|
|
end;
|
|
|
|
function GetPointerType(out ADeclaration: String): Boolean;
|
|
var
|
|
s: String;
|
|
begin
|
|
s := '';
|
|
while (ADbgSymbol.Kind = skPointer) and (ADbgSymbol.TypeInfo <> nil) do begin
|
|
ADbgSymbol := ADbgSymbol.TypeInfo;
|
|
s := s + '^';
|
|
end;
|
|
if (tdfStopAfterPointer in AFlags) then begin
|
|
Result := GetTypeName(ADeclaration, ADbgSymbol, []);
|
|
end
|
|
else begin
|
|
Result := GetTypeAsDeclaration(ADeclaration, ADbgSymbol, AFlags + [tdfStopAfterPointer]);
|
|
if not Result then
|
|
Result := GetTypeName(ADeclaration, ADbgSymbol, []);
|
|
end;
|
|
if NeedBracket(ADeclaration)
|
|
then ADeclaration := s + '(' + ADeclaration + ')'
|
|
else ADeclaration := s + ADeclaration;
|
|
end;
|
|
|
|
function GetBaseType(out ADeclaration: String): Boolean;
|
|
var
|
|
s1, s2: String;
|
|
begin
|
|
if sfSubRange in ADbgSymbol.Flags then begin
|
|
case ADbgSymbol.Kind of
|
|
// TODO: check bound are in size
|
|
skInteger: begin
|
|
Result := ADbgSymbol.HasBounds;
|
|
if Result then ADeclaration := Format('%d..%d', [ADbgSymbol.OrdLowBound, ADbgSymbol.OrdHighBound]);
|
|
end;
|
|
skCardinal: begin
|
|
Result := ADbgSymbol.HasBounds;
|
|
if Result then ADeclaration := Format('%u..%u', [QWord(ADbgSymbol.OrdLowBound), QWord(ADbgSymbol.OrdHighBound)]);
|
|
end;
|
|
skChar: begin
|
|
Result := ADbgSymbol.HasBounds;
|
|
if (ADbgSymbol.OrdLowBound >= 32) and (ADbgSymbol.OrdLowBound <= 126)
|
|
then s1 := '''' + chr(ADbgSymbol.OrdLowBound) + ''''
|
|
else s1 := '#'+IntToStr(ADbgSymbol.OrdLowBound);
|
|
if (ADbgSymbol.OrdHighBound >= 32) and (ADbgSymbol.OrdHighBound <= 126)
|
|
then s2 := '''' + chr(ADbgSymbol.OrdHighBound) + ''''
|
|
else s2 := '#'+IntToStr(ADbgSymbol.OrdHighBound);
|
|
if Result then ADeclaration := Format('%s..%s', [s1, s2]);
|
|
end;
|
|
else
|
|
Result := False; // not sure how to show a subrange of skFloat, skBoolean, :
|
|
end;
|
|
end
|
|
else
|
|
Result := GetTypeName(ADeclaration, ADbgSymbol, []);
|
|
end;
|
|
|
|
function GetFunctionType(out ADeclaration: String): Boolean;
|
|
var
|
|
s: String;
|
|
begin
|
|
// Todo param
|
|
GetTypeAsDeclaration(s, ADbgSymbol.TypeInfo, AFlags);
|
|
ADeclaration := 'function ' + ADbgSymbol.Name + ' () : ' + s + '';
|
|
if sfVirtual in ADbgSymbol.Flags then ADeclaration := ADeclaration + '; virtual';
|
|
Result := true;
|
|
end;
|
|
|
|
function GetProcedureType(out ADeclaration: String): Boolean;
|
|
begin
|
|
// Todo param
|
|
ADeclaration := 'procedure ' + ADbgSymbol.Name + ' ()';
|
|
if sfVirtual in ADbgSymbol.Flags then ADeclaration := ADeclaration + '; virtual';
|
|
Result := true;
|
|
end;
|
|
|
|
function GetClassType(out ADeclaration: String): Boolean;
|
|
var
|
|
s, s2: String;
|
|
begin
|
|
Result := tdfSkipClassBody in AFlags;
|
|
if Result then begin
|
|
GetTypeName(s, ADbgSymbol);
|
|
ADeclaration := s + ' {=class}';
|
|
exit;
|
|
end;
|
|
Result := MembersAsGdbText(s, True, [tdfSkipClassBody]);
|
|
if not GetTypeName(s2, ADbgSymbol.TypeInfo) then
|
|
s2 := '';
|
|
if Result then
|
|
ADeclaration := Format('class(%s)%s%s%send',
|
|
[s2, LineEnding, s, GetIndent]);
|
|
end;
|
|
|
|
function GetRecordType(out ADeclaration: String): Boolean;
|
|
var
|
|
s: String;
|
|
begin
|
|
if tdfSkipRecordBody in AFlags then begin
|
|
Result := True;
|
|
if GetTypeName(s, ADbgSymbol) then
|
|
ADeclaration := s + ' {=record}'
|
|
else
|
|
ADeclaration := Format('record {...};%s%send', [LineEnding, GetIndent]);
|
|
exit;
|
|
end;
|
|
Result := MembersAsGdbText(s, False);
|
|
if Result then
|
|
ADeclaration := Format('record%s%s%send', [LineEnding, s, GetIndent]);
|
|
end;
|
|
|
|
function GetEnumType(out ADeclaration: String): Boolean;
|
|
var
|
|
i, j, val: Integer;
|
|
m: TFpDbgSymbol;
|
|
begin
|
|
// TODO assigned value (a,b:=3,...)
|
|
Result := True;
|
|
ADeclaration := '(';
|
|
j := 0;
|
|
for i := 0 to ADbgSymbol.MemberCount - 1 do begin
|
|
m := ADbgSymbol.Member[i];
|
|
if i > 0 then ADeclaration := ADeclaration + ', ';
|
|
ADeclaration := ADeclaration + m.Name;
|
|
if m.HasOrdinalValue then begin
|
|
val := m.OrdinalValue;
|
|
if j <> val then begin
|
|
ADeclaration := ADeclaration + ' := ' + IntToStr(val);
|
|
j := val;
|
|
continue;
|
|
end;
|
|
end;
|
|
inc(j);
|
|
end;
|
|
ADeclaration := ADeclaration + ')'
|
|
end;
|
|
|
|
function GetSetType(out ADeclaration: String): Boolean;
|
|
var
|
|
t: TFpDbgSymbol;
|
|
s: String;
|
|
begin
|
|
// TODO assigned value (a,b:=3,...)
|
|
t := ADbgSymbol.TypeInfo;
|
|
Result := t <> nil;
|
|
if not Result then exit;
|
|
|
|
case t.Kind of
|
|
skInteger: begin
|
|
Result := t.HasBounds;
|
|
ADeclaration := format('set of %d..%d', [t.OrdLowBound, t.OrdHighBound]);
|
|
end;
|
|
skCardinal: begin
|
|
Result := t.HasBounds;
|
|
ADeclaration := format('set of %u..%u', [QWord(t.OrdLowBound), QWord(t.OrdHighBound)]);
|
|
end;
|
|
skEnum: begin
|
|
if t.Name <> '' then begin
|
|
Result := True;
|
|
s := t.Name;
|
|
end
|
|
else
|
|
Result := GetTypeAsDeclaration(s, t, AFlags);
|
|
ADeclaration := 'set of ' + s;
|
|
end;
|
|
else
|
|
Result := False;
|
|
end;
|
|
end;
|
|
|
|
function GetArrayType(out ADeclaration: String): Boolean;
|
|
var
|
|
t: TFpDbgSymbol;
|
|
s: String;
|
|
i: Integer;
|
|
begin
|
|
// TODO assigned value (a,b:=3,...)
|
|
t := ADbgSymbol.TypeInfo;
|
|
Result := (t <> nil);
|
|
if not Result then exit;
|
|
|
|
s := t.Name;
|
|
if s = '' then begin
|
|
if tdfStopAfterPointer in AFlags then
|
|
Result := GetTypeName(s, t)
|
|
else
|
|
Result := GetTypeAsDeclaration(s, t, [tdfNoFirstLineIndent, tdfStopAfterPointer] + AFlags, AnIndent + 4); // no class ?
|
|
if not Result then exit;
|
|
end;
|
|
|
|
|
|
if sfDynArray in ADbgSymbol.Flags then begin //supprts only one level
|
|
ADeclaration := 'array of ' + s;
|
|
if tdfDynArrayWithPointer in AFlags then
|
|
ADeclaration := '^(' + ADeclaration + ')';
|
|
end
|
|
else begin
|
|
ADeclaration := 'array [';
|
|
for i := 0 to ADbgSymbol.MemberCount - 1 do begin
|
|
if i > 0 then
|
|
ADeclaration := ADeclaration + ', ';
|
|
t := ADbgSymbol.Member[i];
|
|
if t.Kind = skCardinal
|
|
then ADeclaration := ADeclaration + Format('%u..%u', [QWord(t.OrdLowBound), QWord(t.OrdHighBound)])
|
|
else ADeclaration := ADeclaration + Format('%d..%d', [t.OrdLowBound, t.OrdHighBound]);
|
|
end;
|
|
ADeclaration := ADeclaration + '] of ' + s;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
VarName: String;
|
|
begin
|
|
Result := ADbgSymbol <> nil;
|
|
if not Result then
|
|
exit;
|
|
VarName := '';
|
|
if (ADbgSymbol.SymbolType = stValue) and
|
|
not((ADbgSymbol.Kind = skProcedure) or (ADbgSymbol.Kind = skFunction))
|
|
then begin
|
|
if tdfIncludeVarName in AFlags then
|
|
VarName := ADbgSymbol.Name;
|
|
ADbgSymbol := ADbgSymbol.TypeInfo;
|
|
Result := ADbgSymbol <> nil;
|
|
if not Result then
|
|
exit;
|
|
end;
|
|
|
|
case ADbgSymbol.Kind of
|
|
skPointer: Result := GetPointerType(ATypeDeclaration);
|
|
skInteger, skCardinal, skBoolean, skChar, skFloat:
|
|
Result := GetBaseType(ATypeDeclaration);
|
|
skFunction: Result := GetFunctionType(ATypeDeclaration);
|
|
skProcedure: Result := GetProcedureType(ATypeDeclaration);
|
|
skClass: Result := GetClassType(ATypeDeclaration);
|
|
skRecord: Result := GetRecordType(ATypeDeclaration);
|
|
skEnum: Result := GetEnumType(ATypeDeclaration);
|
|
skset: Result := GetSetType(ATypeDeclaration);
|
|
skArray: Result := GetArrayType(ATypeDeclaration);
|
|
end;
|
|
|
|
if VarName <> '' then
|
|
ATypeDeclaration := VarName + ': ' + ATypeDeclaration;
|
|
if (AnIndent <> 0) and not(tdfNoFirstLineIndent in AFlags) then
|
|
ATypeDeclaration := GetIndent + ATypeDeclaration;
|
|
end;
|
|
|
|
{ TFpPascalPrettyPrinter }
|
|
|
|
function TFpPascalPrettyPrinter.InternalPrintValue(out APrintedValue: String;
|
|
AValue: TFpDbgValue; AnAddressSize: Integer; AFlags: TFpPrettyPrintValueFlags;
|
|
ANestLevel: Integer; AnIndent: String; ADisplayFormat: TWatchDisplayFormat;
|
|
ARepeatCount: Integer; ADBGTypeInfo: PDBGType): Boolean;
|
|
|
|
|
|
function ResTypeName: String;
|
|
begin
|
|
if not((AValue.TypeInfo<> nil) and
|
|
GetTypeName(Result, AValue.TypeInfo, []))
|
|
then
|
|
Result := '';
|
|
end;
|
|
function ResTypeName(AVal : TFpDbgValue): String;
|
|
begin
|
|
if not((AVal.TypeInfo<> nil) and
|
|
GetTypeName(Result, AVal.TypeInfo, []))
|
|
then
|
|
Result := '';
|
|
end;
|
|
|
|
procedure DoPointer(AnAddress: boolean);
|
|
var
|
|
s: String;
|
|
v: QWord;
|
|
begin
|
|
if ((ADisplayFormat = wdfDefault) and (ANestLevel=0)) or // default for unested: with typename
|
|
(ADisplayFormat = wdfStructure)
|
|
then
|
|
s := ResTypeName
|
|
else
|
|
s := '';
|
|
|
|
if AnAddress then
|
|
v := AValue.Address.Address
|
|
else
|
|
v := AValue.AsCardinal;
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skPointer, s);
|
|
ADBGTypeInfo^.Value.AsPointer := Pointer(v); // TODO: no cut off
|
|
end;
|
|
|
|
case ADisplayFormat of
|
|
wdfDecimal, wdfUnsigned: APrintedValue := IntToStr(v);
|
|
wdfHex: APrintedValue := '$'+IntToHex(v, AnAddressSize*2);
|
|
else begin //wdfPointer/Default ;
|
|
if v = 0 then
|
|
APrintedValue := 'nil'
|
|
else
|
|
APrintedValue := '$'+IntToHex(v, AnAddressSize*2);
|
|
end;
|
|
end;
|
|
|
|
if s <> '' then
|
|
APrintedValue := s + '(' + APrintedValue + ')';
|
|
|
|
if ADisplayFormat = wdfPointer then exit; // no data
|
|
if svfString in AValue.FieldFlags then
|
|
APrintedValue := APrintedValue + ' ' + AValue.AsString;
|
|
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoInt;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
case ADisplayFormat of
|
|
wdfUnsigned: APrintedValue := IntToStr(QWord(AValue.AsInteger));
|
|
wdfHex: begin
|
|
if svfSize in AValue.FieldFlags then
|
|
n := AValue.Size * 2
|
|
else begin
|
|
n := 16;
|
|
if QWord(AValue.AsInteger) <= high(Cardinal) then n := 8;
|
|
if QWord(AValue.AsInteger) <= high(Word) then n := 3;
|
|
if QWord(AValue.AsInteger) <= high(Byte) then n := 2;
|
|
end;
|
|
APrintedValue := '$'+IntToHex(QWord(AValue.AsInteger), n);
|
|
end;
|
|
// TODO wdfChar:
|
|
else
|
|
APrintedValue := IntToStr(AValue.AsInteger);
|
|
end;
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName);
|
|
//ADBGTypeInfo^.Value.As64Bits := QWord(AValue.AsInteger); // TODO: no cut off
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoCardinal;
|
|
var
|
|
n: Integer;
|
|
begin
|
|
case ADisplayFormat of
|
|
wdfDecimal: APrintedValue := IntToStr(Int64(AValue.AsCardinal));
|
|
wdfHex: begin
|
|
if svfSize in AValue.FieldFlags then
|
|
n := AValue.Size * 2
|
|
else begin
|
|
n := 16;
|
|
if AValue.AsCardinal <= high(Cardinal) then n := 8;
|
|
if AValue.AsCardinal <= high(Word) then n := 4;
|
|
if AValue.AsCardinal <= high(Byte) then n := 2;
|
|
end;
|
|
APrintedValue := '$'+IntToHex(AValue.AsCardinal, n);
|
|
end;
|
|
// TODO wdfChar:
|
|
else
|
|
APrintedValue := IntToStr(AValue.AsCardinal);
|
|
end;
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName);
|
|
//ADBGTypeInfo^.Value.As64Bits := QWord(AValue.AsiCardinal); // TODO: no cut off
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoBool;
|
|
begin
|
|
if AValue.AsBool then begin
|
|
APrintedValue := 'True';
|
|
if AValue.AsCardinal <> 1 then
|
|
APrintedValue := APrintedValue + '(' + IntToStr(AValue.AsCardinal) + ')';
|
|
end
|
|
else
|
|
APrintedValue := 'False';
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoChar;
|
|
begin
|
|
APrintedValue := '''' + AValue.AsString + ''''; // Todo escape
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoFloat;
|
|
begin
|
|
APrintedValue := FloatToStr(AValue.AsFloat);
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoEnum;
|
|
var
|
|
s: String;
|
|
begin
|
|
APrintedValue := AValue.AsString;
|
|
if APrintedValue = '' then begin
|
|
s := ResTypeName;
|
|
APrintedValue := s + '(' + IntToStr(AValue.AsCardinal) + ')';
|
|
end
|
|
else if (ppvCreateDbgType in AFlags) then
|
|
s := ResTypeName;
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skEnum, s);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoEnumVal;
|
|
begin
|
|
APrintedValue := AValue.AsString;
|
|
if APrintedValue <> '' then
|
|
APrintedValue := APrintedValue + ':=';
|
|
APrintedValue := APrintedValue+ IntToStr(AValue.AsCardinal);
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoSet;
|
|
var
|
|
s: String;
|
|
i: Integer;
|
|
m: TFpDbgValue;
|
|
begin
|
|
APrintedValue := '';
|
|
for i := 0 to AValue.MemberCount-1 do begin
|
|
m := AValue.Member[i];
|
|
if svfIdentifier in m.FieldFlags then
|
|
s := m.AsString
|
|
else
|
|
if svfOrdinal in m.FieldFlags then // set of byte
|
|
s := IntToStr(m.AsCardinal)
|
|
else
|
|
Continue; // Todo: missing member
|
|
if APrintedValue = ''
|
|
then APrintedValue := s
|
|
else APrintedValue := APrintedValue + ', ' + s;
|
|
end;
|
|
APrintedValue := '[' + APrintedValue + ']';
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSet, ResTypeName);
|
|
end;
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoStructure;
|
|
var
|
|
s, s2, MbName, MbVal: String;
|
|
i: Integer;
|
|
m: TFpDbgValue;
|
|
fl: TFpPrettyPrintValueFlags;
|
|
f: TDBGField;
|
|
ti: TFpDbgSymbol;
|
|
begin
|
|
if (AValue.Kind = skClass) and (AValue.AsCardinal = 0) then begin
|
|
APrintedValue := 'nil';
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName);
|
|
end;
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
s := ResTypeName;
|
|
case AValue.Kind of
|
|
skRecord: ADBGTypeInfo^ := TDBGType.Create(skRecord, s);
|
|
skObject: ADBGTypeInfo^ := TDBGType.Create(skClass, s);
|
|
skClass: ADBGTypeInfo^ := TDBGType.Create(skClass, s);
|
|
end;
|
|
end;
|
|
|
|
if ADisplayFormat = wdfPointer then begin
|
|
if not (ppvCreateDbgType in AFlags) then
|
|
s := ResTypeName;
|
|
APrintedValue := '$'+IntToHex(AValue.AsCardinal, AnAddressSize*2);
|
|
if s <> '' then
|
|
APrintedValue := s + '(' + APrintedValue + ')';
|
|
Result := True;
|
|
if not (ppvCreateDbgType in AFlags) then
|
|
exit;
|
|
end
|
|
else
|
|
if ( (AValue.Kind in [skClass, skObject]) and (ppvSkipClassBody in AFlags) ) or
|
|
( (AValue.Kind in [skRecord]) and (ppvSkipRecordBody in AFlags) )
|
|
then begin
|
|
APrintedValue := ResTypeName;
|
|
case AValue.Kind of
|
|
skRecord: APrintedValue := '{record:}' + APrintedValue;
|
|
skObject: APrintedValue := '{object:}' + APrintedValue;
|
|
skClass: APrintedValue := '{class:}' + APrintedValue + '(' + '$'+IntToHex(AValue.AsCardinal, AnAddressSize*2) + ')';
|
|
end;
|
|
Result := True;
|
|
if not (ppvCreateDbgType in AFlags) then
|
|
exit;
|
|
end;
|
|
|
|
s2 := LineEnding;
|
|
if AFlags <> [] then s2 := ' ';;
|
|
fl := [ppvSkipClassBody];
|
|
//if ppvSkipClassBody in AFlags then
|
|
// fl := [ppvSkipClassBody, ppvSkipRecordBody];
|
|
|
|
if (ppvCreateDbgType in AFlags) and (AValue.Kind in [skObject, skClass]) then begin
|
|
ti := AValue.TypeInfo;
|
|
if (ti <> nil) and (ti.TypeInfo <> nil) then
|
|
ADBGTypeInfo^.Ancestor := ti.TypeInfo.Name;
|
|
end;
|
|
|
|
if not Result then
|
|
APrintedValue := '';
|
|
for i := 0 to AValue.MemberCount-1 do begin
|
|
m := AValue.Member[i];
|
|
if (m = nil) or (m.Kind in [skProcedure, skFunction]) then
|
|
continue;
|
|
s := '';
|
|
InternalPrintValue(MbVal, m, AnAddressSize, fl, ANestLevel+1, AnIndent, ADisplayFormat);
|
|
if m.DbgSymbol <> nil then begin
|
|
MbName := m.DbgSymbol.Name;
|
|
s := MbName + ' = ' + MbVal;
|
|
end
|
|
else begin
|
|
MbName := '';
|
|
s := MbVal;
|
|
end;
|
|
|
|
if not Result then begin
|
|
if APrintedValue = ''
|
|
then APrintedValue := s
|
|
else APrintedValue := APrintedValue + '; ' + s2 + s;
|
|
end;
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
s := '';
|
|
if m.ContextTypeInfo <> nil then s := m.ContextTypeInfo.Name;
|
|
f := TDBGField.Create(MbName, TDBGType.Create(skSimple, ResTypeName(m)),
|
|
flPublic, [], s);
|
|
f.DBGType.Value.AsString := MbVal;
|
|
ADBGTypeInfo^.Fields.Add(f);
|
|
end;
|
|
end;
|
|
if not Result then
|
|
APrintedValue := '(' + APrintedValue + ')';
|
|
Result := True;
|
|
end;
|
|
|
|
procedure DoArray;
|
|
var
|
|
s: String;
|
|
i: Integer;
|
|
m: TFpDbgValue;
|
|
Cnt, FullCnt, d: Integer;
|
|
begin
|
|
APrintedValue := '';
|
|
|
|
if (ppvCreateDbgType in AFlags) then begin
|
|
ADBGTypeInfo^ := TDBGType.Create(skArray, ResTypeName);
|
|
//ATypeInfo.Len;
|
|
//ATypeInfo.BoundLow;
|
|
//ATypeInfo.BoundHigh;
|
|
end;
|
|
|
|
Cnt := AValue.MemberCount;
|
|
FullCnt := Cnt;
|
|
if (Cnt = 0) and (svfOrdinal in AValue.FieldFlags) then begin // dyn array
|
|
APrintedValue := 'nil';
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
if (ANestLevel > 2) then begin
|
|
s := ResTypeName;
|
|
APrintedValue := s+'({'+IntToStr(FullCnt)+' elements})'; // TODO len and addr (dyn array)
|
|
Result := True;
|
|
exit;
|
|
end;
|
|
If ARepeatCount > 0 then Cnt := ARepeatCount
|
|
else if (ANestLevel > 1) and (Cnt > 3) then Cnt := 3
|
|
else if (ANestLevel > 0) and (Cnt > 10) then Cnt := 10
|
|
else if (Cnt > 300) then Cnt := 300;
|
|
d := 0;
|
|
// TODO: use valueobject for bounds
|
|
if (AValue.IndexTypeCount > 0) and AValue.IndexType[0].HasBounds then
|
|
d := AValue.IndexType[0].OrdLowBound;
|
|
for i := d to d + Cnt - 1 do begin
|
|
m := AValue.Member[i];
|
|
if m <> nil then
|
|
InternalPrintValue(s, m, AnAddressSize, AFlags * PV_FORWARD_FLAGS, ANestLevel+1, AnIndent, ADisplayFormat)
|
|
else
|
|
s := '{error}';
|
|
if APrintedValue = ''
|
|
then APrintedValue := s
|
|
else APrintedValue := APrintedValue + ', ' + s;
|
|
end;
|
|
if Cnt < FullCnt then
|
|
APrintedValue := APrintedValue + ', {'+IntToStr(FullCnt-Cnt)+' more elements}';
|
|
APrintedValue := '(' + APrintedValue + ')';
|
|
Result := True;
|
|
end;
|
|
var
|
|
MemAddr: TFpDbgMemLocation;
|
|
MemSize: Integer;
|
|
MemDest: array of Byte;
|
|
i: Integer;
|
|
begin
|
|
if ADBGTypeInfo <> nil then ADBGTypeInfo^ := nil;
|
|
if ANestLevel > 0 then begin
|
|
AnIndent := AnIndent + ' ';
|
|
end;
|
|
|
|
if ADisplayFormat = wdfMemDump then begin
|
|
if FMemManager <> nil then begin
|
|
MemAddr := UnInitializedLoc;
|
|
if svfDataAddress in AValue.FieldFlags then begin
|
|
MemAddr := AValue.DataAddress;
|
|
MemSize := AValue.DataSize;
|
|
end
|
|
else
|
|
if svfAddress in AValue.FieldFlags then begin
|
|
MemAddr := AValue.Address;
|
|
MemSize := AValue.Size;
|
|
end;
|
|
if MemSize < ARepeatCount then MemSize := ARepeatCount;
|
|
if MemSize <= 0 then MemSize := 256;
|
|
|
|
if IsTargetAddr(MemAddr) then begin
|
|
SetLength(MemDest, MemSize);
|
|
if FMemManager.ReadMemory(MemAddr, MemSize, @MemDest[0]) then begin
|
|
APrintedValue := IntToHex(MemAddr.Address, AnAddressSize*2)+ ':' + LineEnding;
|
|
for i := 0 to high(MemDest) do begin
|
|
if (i > 0) and (i mod 16 = 0) then
|
|
APrintedValue := APrintedValue + LineEnding
|
|
else
|
|
if (i > 0) and (i mod 8 = 0) then
|
|
APrintedValue := APrintedValue + ' '
|
|
else
|
|
if (i > 0) then
|
|
APrintedValue := APrintedValue + ' ';
|
|
APrintedValue := APrintedValue + IntToHex(MemDest[i], 2);
|
|
end;
|
|
end
|
|
else begin
|
|
APrintedValue := 'Cannot read memory at address '+ IntToHex(MemAddr.Address, AnAddressSize*2);
|
|
end;
|
|
exit;
|
|
end;
|
|
end;
|
|
|
|
APrintedValue := 'Cannot read memory for expression';
|
|
exit
|
|
end;
|
|
|
|
Result := False;
|
|
case AValue.Kind of
|
|
skUnit: ;
|
|
skProcedure: ;
|
|
skFunction: ;
|
|
skPointer: DoPointer(False);
|
|
skInteger: DoInt;
|
|
skCardinal: DoCardinal;
|
|
skBoolean: DoBool;
|
|
skChar: DoChar;
|
|
skFloat: DoFloat;
|
|
skString: ;
|
|
skAnsiString: ;
|
|
skCurrency: ;
|
|
skVariant: ;
|
|
skWideString: ;
|
|
skEnum: DoEnum;
|
|
skEnumValue: DoEnumVal;
|
|
skSet: DoSet;
|
|
skRecord: DoStructure;
|
|
skObject: DoStructure;
|
|
skClass: DoStructure;
|
|
skInterface: ;
|
|
skArray: DoArray;
|
|
skNone: DoPointer(true);
|
|
end;
|
|
|
|
if (ADBGTypeInfo <> nil) and (ADBGTypeInfo^ <> nil) then
|
|
ADBGTypeInfo^.Value.AsString := APrintedValue;
|
|
|
|
if IsError(AValue.LastError) then
|
|
APrintedValue := ErrorHandler.ErrorAsString(AValue.LastError);
|
|
end;
|
|
|
|
constructor TFpPascalPrettyPrinter.Create(AnAddressSize: Integer);
|
|
begin
|
|
FAddressSize := AnAddressSize;
|
|
end;
|
|
|
|
function TFpPascalPrettyPrinter.PrintValue(out APrintedValue: String; AValue: TFpDbgValue;
|
|
ADisplayFormat: TWatchDisplayFormat; ARepeatCount: Integer): Boolean;
|
|
begin
|
|
Result := InternalPrintValue(APrintedValue, AValue,
|
|
AddressSize, [], 0, '', ADisplayFormat, ARepeatCount);
|
|
end;
|
|
|
|
function TFpPascalPrettyPrinter.PrintValue(out APrintedValue: String; out
|
|
ADBGTypeInfo: TDBGType; AValue: TFpDbgValue; ADisplayFormat: TWatchDisplayFormat;
|
|
ARepeatCount: Integer): Boolean;
|
|
begin
|
|
Result := InternalPrintValue(APrintedValue, AValue,
|
|
AddressSize, [ppvCreateDbgType], 0, '',
|
|
ADisplayFormat, ARepeatCount, @ADBGTypeInfo);
|
|
end;
|
|
|
|
end.
|
|
|