From 08fac03972e3847220f313bf7574da9eb7eabbd1 Mon Sep 17 00:00:00 2001 From: Martin Date: Sat, 30 Dec 2023 20:03:06 +0100 Subject: [PATCH] FpDebug: Add comparing enum-values in watches "enum1 >= enum2" (cherry picked from commit 5c6662f3e7c703b9f44b2a699cc0a4f56224d151) --- components/fpdebug/fpdbgdwarf.pas | 16 ++++ components/fpdebug/fpdmemorytools.pas | 22 +++++ components/fpdebug/fppascalparser.pas | 88 +++++++++++++++++++ .../lazdebuggerfp/test/testwatches.pas | 50 +++++++++++ 4 files changed, 176 insertions(+) diff --git a/components/fpdebug/fpdbgdwarf.pas b/components/fpdebug/fpdbgdwarf.pas index 0c83118b6c..2634c86b24 100644 --- a/components/fpdebug/fpdbgdwarf.pas +++ b/components/fpdebug/fpdbgdwarf.pas @@ -350,6 +350,7 @@ type //function IsValidTypeCast: Boolean; override; function GetFieldFlags: TFpValueFieldFlags; override; function GetAsCardinal: QWord; override; + function GetAsInteger: Int64; override; procedure SetAsCardinal(AValue: QWord); override; function GetAsString: AnsiString; override; procedure SetAsString(AValue: AnsiString); override; @@ -368,6 +369,7 @@ type protected function GetFieldFlags: TFpValueFieldFlags; override; function GetAsCardinal: QWord; override; + function GetAsInteger: Int64; override; function GetAsString: AnsiString; override; function IsValidTypeCast: Boolean; override; function GetKind: TDbgSymbolKind; override; @@ -2906,6 +2908,15 @@ begin FValue := Result; end; +function TFpValueDwarfEnum.GetAsInteger: Int64; +var + Size: TFpDbgValueSize; +begin + Result := GetAsCardinal; + if GetSize(Size) then + Result := SignExtend(QWord(Result), Size); +end; + procedure TFpValueDwarfEnum.SetAsCardinal(AValue: QWord); var Size: TFpDbgValueSize; @@ -2979,6 +2990,11 @@ begin Result := FOwnerVal.OrdinalValue; end; +function TFpValueDwarfEnumMember.GetAsInteger: Int64; +begin + Result := FOwnerVal.OrdinalValue; +end; + function TFpValueDwarfEnumMember.GetAsString: AnsiString; begin Result := FOwnerVal.Name; diff --git a/components/fpdebug/fpdmemorytools.pas b/components/fpdebug/fpdmemorytools.pas index f4523698c5..ffb8f9ddcd 100644 --- a/components/fpdebug/fpdmemorytools.pas +++ b/components/fpdebug/fpdmemorytools.pas @@ -512,6 +512,8 @@ operator - (const AnAddr: TFpDbgMemLocation; AVal: QWord): TFpDbgMemLocation; i function LocToAddr(const ALocation: TFpDbgMemLocation): TDbgPtr; inline; // does not check valid function LocToAddrOrNil(const ALocation: TFpDbgMemLocation): TDbgPtr; inline; // save version +function SignExtend(ASrcVal: QWord; ASrcSize: TFpDbgValueSize): Int64; + //function EmptyMemReadOpts:TFpDbgMemReadOptions; function dbgs(const ALocation: TFpDbgMemLocation): String; overload; @@ -900,6 +902,26 @@ begin Result := 0; end; +function SignExtend(ASrcVal: QWord; ASrcSize: TFpDbgValueSize): Int64; +var + SourceFullSize, SBit: Int64; + b: TBitSize; +begin + Result := Int64(ASrcVal); + + SourceFullSize := SizeToFullBytes(ASrcSize); + if SourceFullSize = 0 then + exit; + + b := ASrcSize.BitSize; + SBit := 8 * SourceFullSize; + if b > 0 then + SBit := SBit + b - 8; + + if (ASrcVal and (1 shl (SBit-1)) ) <> 0 then + Result := Result or (int64(-1) shl SBit); +end; + //function {%H-}EmptyMemReadOpts: TFpDbgMemReadOptions; //begin // // diff --git a/components/fpdebug/fppascalparser.pas b/components/fpdebug/fppascalparser.pas index 7a09727509..0ff8c42080 100644 --- a/components/fpdebug/fppascalparser.pas +++ b/components/fpdebug/fppascalparser.pas @@ -4829,6 +4829,42 @@ function TFpPascalExpressionPartOperatorCompare.DoGetResultValue: TFpValue; else SetError('= not supported'); end; + function CheckEnumCompatible(AName: String; AExpVal: Integer; AnEnum: TFpValue): boolean; + var + m, t: TFpSymbol; + begin + Result := AName = ''; // un-named / maybe invalid ordinal value + if Result then + exit; + + t := AnEnum.TypeInfo; + Result := t = nil; + if Result then // TODO: TFpValueDwarfEnumMember currently does not have type info + exit; + + m := t.NestedSymbolByName[AName]; + Result := m <> nil; + if not Result then + exit; + Result := m.HasOrdinalValue and (m.OrdinalValue = AExpVal); + end; + function EnumEqual(AnEnumVal, AOtherVal: TFpValue; AReverse: Boolean = False): TFpValue; + begin + Result := nil; + if (AOtherVal.Kind in [skEnum, skEnumValue]) and + (AnEnumVal.FieldFlags * [svfInteger, svfCardinal, svfOrdinal] <> []) and + (AOtherVal.FieldFlags * [svfInteger, svfCardinal, svfOrdinal] <> []) + then begin + if CheckEnumCompatible(AnEnumVal.AsString, AnEnumVal.AsInteger, AOtherVal) and + CheckEnumCompatible(AOtherVal.AsString, AOtherVal.AsInteger, AnEnumVal) + then + Result := TFpValueConstBool.Create((AnEnumVal.AsInteger = AOtherVal.AsInteger) xor AReverse) + else + SetError('type mismatch between enum'); + end + else + SetError('= not supported'); + end; function IntGreaterThanValue(AIntVal, AOtherVal: TFpValue; AReverse: Boolean = False): TFpValue; begin @@ -4868,6 +4904,23 @@ function TFpPascalExpressionPartOperatorCompare.DoGetResultValue: TFpValue; else SetError('= not supported'); end; + function EnumGreaterThanValue(AnEnumVal, AOtherVal: TFpValue; AReverse: Boolean = False): TFpValue; + begin + Result := nil; + if (AOtherVal.Kind in [skEnum, skEnumValue]) and + (AnEnumVal.FieldFlags * [svfInteger, svfCardinal, svfOrdinal] <> []) and + (AOtherVal.FieldFlags * [svfInteger, svfCardinal, svfOrdinal] <> []) + then begin + if CheckEnumCompatible(AnEnumVal.AsString, AnEnumVal.AsInteger, AOtherVal) and + CheckEnumCompatible(AOtherVal.AsString, AOtherVal.AsInteger, AnEnumVal) + then + Result := TFpValueConstBool.Create((AnEnumVal.AsInteger > AOtherVal.AsInteger) xor AReverse) + else + SetError('type mismatch between enum'); + end + else + SetError(GetText+' not supported'); + end; function IntSmallerThanValue(AIntVal, AOtherVal: TFpValue; AReverse: Boolean = False): TFpValue; begin @@ -4907,6 +4960,23 @@ function TFpPascalExpressionPartOperatorCompare.DoGetResultValue: TFpValue; else SetError('= not supported'); end; + function EnumSmallerThanValue(AnEnumVal, AOtherVal: TFpValue; AReverse: Boolean = False): TFpValue; + begin + Result := nil; + if (AOtherVal.Kind in [skEnum, skEnumValue]) and + (AnEnumVal.FieldFlags * [svfInteger, svfCardinal, svfOrdinal] <> []) and + (AOtherVal.FieldFlags * [svfInteger, svfCardinal, svfOrdinal] <> []) + then begin + if CheckEnumCompatible(AnEnumVal.AsString, AnEnumVal.AsInteger, AOtherVal) and + CheckEnumCompatible(AOtherVal.AsString, AOtherVal.AsInteger, AnEnumVal) + then + Result := TFpValueConstBool.Create((AnEnumVal.AsInteger < AOtherVal.AsInteger) xor AReverse) + else + SetError('type mismatch between enum'); + end + else + SetError(GetText+' not supported'); + end; function SymDiffSets(ASetVal, AOtherVal: TFpValue): TFpValue; var @@ -4980,6 +5050,8 @@ begin Result := CharDataEqualToValue(tmp1, tmp2, (s = '<>')); skSet: Result := SetEqual(tmp1, tmp2, (s = '<>')); skBoolean: Result := BoolEqual(tmp1, tmp2, (s = '<>')); + skEnum, skEnumValue: + Result := EnumEqual(tmp1, tmp2, (s = '<>')); end; end else @@ -4994,6 +5066,8 @@ begin Result := CharDataGreaterThanValue(tmp1, tmp2, (s = '<=')); skString, skAnsiString, skWideString, skChar{, skWideChar}: Result := CharDataGreaterThanValue(tmp1, tmp2, (s = '<=')); + skEnum, skEnumValue: + Result := EnumGreaterThanValue(tmp1, tmp2, (s = '<=')); end; end else @@ -5008,6 +5082,8 @@ begin Result := CharDataSmallerThanValue(tmp1, tmp2, (s = '>=')); skString, skAnsiString, skWideString, skChar{, skWideChar}: Result := CharDataSmallerThanValue(tmp1, tmp2, (s = '>=')); + skEnum, skEnumValue: + Result := EnumSmallerThanValue(tmp1, tmp2, (s = '>=')); end; end else @@ -5108,6 +5184,18 @@ begin exit end; + if (tmp.Kind in [skType]) and + (tmp.DbgSymbol <> nil) and (tmp.DbgSymbol.Kind in [skEnum]) + then begin + Result := tmp.MemberByName[MemberName]; + if Result <> nil then begin + {$IFDEF WITH_REFCOUNT_DEBUG}Result.DbgRenameReference(nil, 'DoGetResultValue'){$ENDIF}; + exit; + end; + SetError(fpErrNoMemberWithName, [MemberName]); + exit + end; + if (tmp.Kind = skUnit) or ( (tmp.DbgSymbol <> nil) and (tmp.DbgSymbol.Kind = skUnit) ) then begin diff --git a/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas b/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas index 3c5ee6f9cd..b2339c9439 100644 --- a/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas +++ b/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas @@ -3589,6 +3589,52 @@ procedure TTestWatches.TestWatchesExpression; t.Add(AName, p+'ShortInt'+e +' and '+ p+'Word'+e, weCardinal((50+n) and (1000+n)) ); t.Add(AName, p+'ShortInt'+e +' and '+ IntToStr(1002+n), weCardinal((50+n) and (1002+n)) ); + + t.Add('ENUM-EQ: ', p+'Enum = eNVaL2', weBool(False)); + t.Add('ENUM-EQ: ', p+'Enum = eNVaL3', weBool(True)); + t.Add('ENUM-EQ: ', p+'Enum = TEnum.eNVaL2', weBool(False)); + t.Add('ENUM-EQ: ', p+'Enum = TEnum.eNVaL3', weBool(True)); + t.Add('ENUM-EQ: ', 'eNVaL2 = '+p+'Enum', weBool(False)); + t.Add('ENUM-EQ: ', 'eNVaL3 = '+p+'Enum', weBool(True)); + t.Add('ENUM-EQ: ', p+'Enum = '+p2+'Enum', weBool(True)); + t.Add('ENUM-EQ: ', p+'Enum = '+p2+'EnumA', weBool(False)); + t.Add('ENUM-EQ: ', 'eNVaL3 = eNVaL2', weBool(False)); + t.Add('ENUM-EQ: ', 'eNVaL3 = eNVaL3', weBool(True)); + + t.Add('ENUM-EQ: ', 'TEnum('+p+'Enum) = eNVaL2', weBool(False)); + t.Add('ENUM-EQ: ', 'TEnum('+p+'Enum) = eNVaL3', weBool(True)); + t.Add('ENUM-EQ: ', p+'Enum = TEnum(1)', weBool(False)); + t.Add('ENUM-EQ: ', p+'Enum = TEnum(2)', weBool(True)); + t.Add('ENUM-EQ: ', 'TEnum(1) = '+p+'Enum', weBool(False)); + t.Add('ENUM-EQ: ', 'TEnum(2) = '+p+'Enum', weBool(True)); + t.Add('ENUM-EQ: ', 'TEnum(2) = TEnum(1)', weBool(False)); + t.Add('ENUM-EQ: ', 'TEnum(2) = TEnum(2)', weBool(True)); + t.Add('ENUM-EQ: ', 'TEnum(2) = eNVaL2', weBool(False)); + t.Add('ENUM-EQ: ', 'TEnum(2) = eNVaL3', weBool(True)); + + t.Add('ENUM-EQ: ', p+'EnumA = '+p2+'Enum1', weBool(False)); + t.Add('ENUM-EQ: ','ENVal2 = '+p2+'Enum1', weBool(True)); + t.Add('ENUM-EQ: ','ENVal1 = '+p2+'Enum1', weBool(False)); + + t.Add('ENUM-Cmp: ', p+'Enum > eNVaL2', weBool(True)); + t.Add('ENUM-Cmp: ', p+'Enum >= eNVaL2', weBool(True)); + t.Add('ENUM-Cmp: ', p+'Enum > eNVaL3', weBool(False)); + t.Add('ENUM-Cmp: ', p+'Enum >= eNVaL3', weBool(True)); + t.Add('ENUM-Cmp: ', p+'Enum < eNVaL2', weBool(False)); + t.Add('ENUM-Cmp: ', p+'Enum < eNVaL3', weBool(False)); + t.Add('ENUM-Cmp: ', p+'Enum < eNVaL4', weBool(True)); + t.Add('ENUM-Cmp: ', 'eNVaL2 < '+p+'Enum', weBool(True)); + t.Add('ENUM-Cmp: ', 'eNVaL3 < '+p+'Enum', weBool(False)); + t.Add('ENUM-Cmp: ', 'eNVaL3 < eNVaL2', weBool(False)); + t.Add('ENUM-Cmp: ', 'eNVaL3 > eNVaL2', weBool(True)); + + t.Add('ENUM-Cmp: ', 'TEnum('+p+'Enum) > eNVaL2', weBool(True)); + t.Add('ENUM-Cmp: ', 'TEnum('+p+'Enum) > eNVaL3', weBool(False)); + t.Add('ENUM-Cmp: ', p+'Enum > TEnum(2)', weBool(False)); + t.Add('ENUM-Cmp: ', p+'Enum > TEnum(1)', weBool(True)); + t.Add('ENUM-Cmp: ', p+'Enum > TEnum(-1)', weBool(True)); + + for i := 0 to t.Count-1 do t.Tests[i].IgnTypeName(); end; @@ -4333,6 +4379,10 @@ begin t.Add('', op1+'True'+op2+'^boolean(1)^', weMatchErr('read.*mem|data.*location')); end; + t.Add('Diff ENUM types: ','gvEnum2 = gvEnum', weMatchErr('type')); + t.Add('Diff ENUM types: ','gvEnum2 = gvEnum1', weMatchErr('type')); + t.Add('Diff ENUM types: ','gvEnum2 = EnVal2', weMatchErr('type')); + t.EvaluateWatches; t.CheckResults;