diff --git a/components/fpdebug/fppascalbuilder.pas b/components/fpdebug/fppascalbuilder.pas index 0fe44b6387..4a4329b074 100644 --- a/components/fpdebug/fppascalbuilder.pas +++ b/components/fpdebug/fppascalbuilder.pas @@ -6,7 +6,7 @@ interface uses Classes, SysUtils, DbgIntfBaseTypes, DbgIntfDebuggerBase, FpDbgInfo, FpdMemoryTools, - FpErrorMessages, LazLoggerBase; + FpErrorMessages, LazLoggerBase, LazUTF8; type TTypeNameFlag = ( @@ -86,6 +86,8 @@ function GetTypeName(out ATypeName: String; ADbgSymbol: TFpDbgSymbol; AFlags: TT function GetTypeAsDeclaration(out ATypeDeclaration: String; ADbgSymbol: TFpDbgSymbol; AFlags: TTypeDeclarationFlags = []; AnIndent: Integer = 0): Boolean; +function QuoteText(AText: Utf8String): UTf8String; + implementation function GetTypeName(out ATypeName: String; ADbgSymbol: TFpDbgSymbol; @@ -445,6 +447,113 @@ begin ATypeDeclaration := GetIndent + ATypeDeclaration; end; +function QuoteText(AText: Utf8String): UTf8String; +// TODO: process large text in chunks to avoid allocating huge memory +const + HEXCHR: array [0..15] of char = ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'); +var + Len: Integer; + c: Char; + RPos, SPos, SEnd, QPos: PChar; +begin + if AText = '' then + exit(''''''); + + Len := Length(AText); + + SetLength(Result, Len * 4); // This is the maximal length result can get + RPos := @Result[1]; + SPos := @AText[1]; + SEnd := @AText[Len] + 1; + + repeat + RPos^ := ''''; inc(RPos); + QPos := RPos; + + + repeat + c := SPos^; + case c of + '''': begin + RPos^ := c; inc(RPos); + RPos^ := c; inc(RPos); + inc(SPos); + end; + #32..Pred(''''), Succ('''')..#126: begin + RPos^ := c; inc(RPos); + inc(SPos); + end; + #192..#223: begin + if ((Byte(SPos[1]) and $C0) <> $80) + then + break; // invalid utf8 -> escape + RPos^ := c; inc(RPos); + RPos^ := SPos[1]; inc(RPos); + inc(SPos, 2); + end; + #224..#239: begin + if ((Byte(SPos[1]) and $C0) <> $80) or ((Byte(SPos[2]) and $C0) <> $80) + then + break; // invalid utf8 -> escape + RPos^ := c; inc(RPos); + RPos^ := SPos[1]; inc(RPos); + RPos^ := SPos[2]; inc(RPos); + inc(SPos, 2); + end; + #240..#247: begin + if ((Byte(SPos[1]) and $C0) <> $80) or ((Byte(SPos[2]) and $C0) <> $80) or + ((Byte(SPos[3]) and $C0) <> $80) + then + break; // invalid utf8 -> escape + RPos^ := c; inc(RPos); + RPos^ := SPos[1]; inc(RPos); + RPos^ := SPos[2]; inc(RPos); + RPos^ := SPos[3]; inc(RPos); + inc(SPos, 3); + end; + #0: begin + if (SPos < SEnd) then + break; // need escaping + + // END OF TEXT + RPos^ := ''''; inc(RPos); + Assert(RPos-1 <= @Result[Length(Result)], 'RPos-1 <= @Result[Length(Result)]'); + SetLength(Result, RPos - @Result[1]); + exit; + end; + else + break; // need escaping + end; + + c := SPos^; + until False;; + + if RPos = QPos then + dec(RPos) + else begin + RPos^ := ''''; inc(RPos); + end; + + repeat + c := SPos^; + if (c = #0) and (SPos >= SEnd) then begin + // END OF TEXT + Assert(RPos-1 <= @Result[Length(Result)], 'RPos-1 <= @Result[Length(Result)]'); + SetLength(Result, RPos - @Result[1]); + exit; + end; + + RPos^ := '#'; inc(RPos); + RPos^ := '$'; inc(RPos); + RPos^ := HEXCHR[Byte(c) >> 4]; inc(RPos); + RPos^ := HEXCHR[Byte(c) and 15]; inc(RPos); + inc(SPos); + c := SPos^; + until not(c in [#0..#31, #127, #$80..#$BF]); + + until False; +end; + { TFpPascalPrettyPrinter } function TFpPascalPrettyPrinter.InternalPrintValue(out APrintedValue: String; @@ -505,7 +614,7 @@ function TFpPascalPrettyPrinter.InternalPrintValue(out APrintedValue: String; if ADisplayFormat = wdfPointer then exit; // no data if svfString in AValue.FieldFlags then - APrintedValue := APrintedValue + ' ' + AValue.AsString; + APrintedValue := APrintedValue + ' ' + QuoteText(AValue.AsString); Result := True; end; @@ -586,13 +695,22 @@ function TFpPascalPrettyPrinter.InternalPrintValue(out APrintedValue: String; procedure DoChar; begin - APrintedValue := '''' + AValue.AsString + ''''; // Todo escape + APrintedValue := QuoteText(AValue.AsString); if (ppvCreateDbgType in AFlags) then begin ADBGTypeInfo^ := TDBGType.Create(skSimple, ResTypeName); end; Result := True; end; + procedure DoString; + begin + APrintedValue := QuoteText(AValue.AsString); + if (ppvCreateDbgType in AFlags) then begin + ADBGTypeInfo^ := TDBGType.Create(skString, ResTypeName); + end; + Result := True; + end; + procedure DoFloat; begin APrintedValue := FloatToStr(AValue.AsFloat); @@ -889,7 +1007,7 @@ begin skBoolean: DoBool; skChar: DoChar; skFloat: DoFloat; - skString: ; + skString: DoString; skAnsiString: ; skCurrency: ; skVariant: ;