unit FpPascalBuilder; {$mode objfpc}{$H+} interface uses Classes, SysUtils, DbgIntfBaseTypes, DbgIntfDebuggerBase, FpDbgInfo, FpdMemoryTools, FpErrorMessages, LazLoggerBase, LCLIntf; 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; 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 (ppvCreateDbgType in AFlags) then begin ADBGTypeInfo^ := TDBGType.Create(skPointer, s); ADBGTypeInfo^.Value.AsPointer := Pointer(AValue.AsCardinal); // TODO: no cut off end; v := AValue.AsCardinal; case ADisplayFormat of wdfDecimal, wdfUnsigned: APrintedValue := IntToStr(AValue.AsCardinal); wdfHex: APrintedValue := '$'+IntToHex(AValue.AsCardinal, AnAddressSize*2); else begin //wdfPointer/Default ; if v = 0 then APrintedValue := 'nil' else APrintedValue := '$'+IntToHex(AValue.AsCardinal, 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; 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 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; 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; 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.