From 77857b84ce996c52b28d999d3135a05c682556d5 Mon Sep 17 00:00:00 2001 From: Martin Date: Sun, 19 Jun 2022 11:41:50 +0200 Subject: [PATCH] FpDebug: improve dyn array detection --- components/fpdebug/fpdbgdwarf.pas | 11 +++-- components/fpdebug/fpwatchresultdata.pas | 2 +- .../lazdebugtestbase/ttestwatchutilities.pas | 44 ++++++++++++++++--- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/components/fpdebug/fpdbgdwarf.pas b/components/fpdebug/fpdbgdwarf.pas index cc526245e5..778b5b8cef 100644 --- a/components/fpdebug/fpdbgdwarf.pas +++ b/components/fpdebug/fpdbgdwarf.pas @@ -5551,7 +5551,7 @@ begin end; function TFpSymbolDwarfTypeArray.GetFlags: TDbgSymbolFlags; - function IsDynSubRange(m: TFpSymbolDwarf): Boolean; + function IsDynSubRange(m: TFpSymbolDwarf; lb: Int64): Boolean; begin Result := sfSubRange in m.Flags; if not Result then exit; @@ -5559,8 +5559,11 @@ function TFpSymbolDwarfTypeArray.GetFlags: TDbgSymbolFlags; m := m.NestedTypeInfo; Result := m <> nil; if not Result then exit; // TODO: should not happen, handle error - Result := (TFpSymbolDwarfTypeSubRange(m).FHighBoundState = rfValue) // dynamic high bound // TODO:? Could be rfConst for locationExpr - or (TFpSymbolDwarfTypeSubRange(m).FHighBoundState = rfNotRead); // dynamic high bound (yet to be read) + // dynamic high bound (or yet to be read) + Result := (TFpSymbolDwarfTypeSubRange(m).FHighBoundState in [rfNotRead, rfValue, rfExpression]) and + ( (TFpSymbolDwarfTypeSubRange(m).FLowBoundState = rfNotRead) or + (lb = 0) + ); end; var m: TFpSymbol; @@ -5571,7 +5574,7 @@ begin m := NestedSymbol[0]; if (not m.GetValueBounds(nil, lb, hb)) or // e.g. Subrange with missing upper bound (hb < lb) or - (IsDynSubRange(TFpSymbolDwarf(m))) + (IsDynSubRange(TFpSymbolDwarf(m), lb)) then Result := Result + [sfDynArray] else diff --git a/components/fpdebug/fpwatchresultdata.pas b/components/fpdebug/fpwatchresultdata.pas index d91b037ac2..9f768a1ff1 100644 --- a/components/fpdebug/fpwatchresultdata.pas +++ b/components/fpdebug/fpwatchresultdata.pas @@ -341,7 +341,7 @@ begin EntryRes := AnResData.CreateArrayValue(datUnknown, Cnt, LowBnd); end else - if (sfDynArray in ti.Flags) or (LowBnd = 0) then begin // LowBnd = 0 => there is some bug, reporting some dyn arrays as stat. + if (sfDynArray in ti.Flags) and (LowBnd = 0) then begin // LowBnd = 0 => there is some bug, reporting some dyn arrays as stat. EntryRes := AnResData.CreateArrayValue(datDynArray, Cnt, 0); if AnFpValue.FieldFlags * [svfInteger, svfCardinal] <> [] then Addr := AnFpValue.AsCardinal diff --git a/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas b/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas index 4254d24421..4d3a52e235 100644 --- a/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas +++ b/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas @@ -36,6 +36,7 @@ type ehIgnPointerDerefData, // Ignore if a pointer has deref data or not ehIgnKind, // Ignore skSimple, .... ehIgnKindPtr, // Ignore skSimple, ONLY if got kind=skPointer + ehIgnKindArrayType, // Ignore dyn vs stat array ehIgnTypeName, // Ignore the typename ehIgnTypeNameInData, // Ignore any appearance of typename in data ehMatchTypeName, // The typename is a regex @@ -88,6 +89,7 @@ type function IgnData(ASymTypes: TSymbolTypes = []): TWatchExpectationResult; function IgnKind(ASymTypes: TSymbolTypes = []): TWatchExpectationResult; function IgnKindPtr(ASymTypes: TSymbolTypes = []): TWatchExpectationResult; + function IgnKindArrayType(ASymTypes: TSymbolTypes = []): TWatchExpectationResult; function IgnTypeName(ASymTypes: TSymbolTypes = []): TWatchExpectationResult; function MatchTypeName(ASymTypes: TSymbolTypes = []): TWatchExpectationResult; @@ -204,6 +206,7 @@ type WatchTpInf: TDBGType; //deprecated; Expectation: TWatchExpectationResult; HasTypeInfo: Boolean; + IsNested: Boolean; end; { TWatchExpectationList } @@ -242,7 +245,7 @@ type function TestFalse(Name: string; Got: Boolean; AContext: TWatchExpTestCurrentData; AIgnoreReason: String): Boolean; function CheckResult(AnWatchExp: TWatchExpectation): Boolean; - function CheckData(AContext: TWatchExpTestCurrentData; AnIgnoreRsn: String): Boolean; virtual; + function CheckData(AContext: TWatchExpTestCurrentData; AnIgnoreRsn: String; AnIsNested: Boolean = True): Boolean; virtual; function VerifyDebuggerState: Boolean; virtual; function VerifySymType(AContext: TWatchExpTestCurrentData; AnIgnoreRsn: String): Boolean; virtual; function VerifyTypeName(AContext: TWatchExpTestCurrentData; AnIgnoreRsn: String): Boolean; virtual; @@ -892,6 +895,12 @@ begin Result := Self.AddFlag(ehIgnKindPtr, ASymTypes); end; +function TWatchExpectationResult.IgnKindArrayType(ASymTypes: TSymbolTypes + ): TWatchExpectationResult; +begin + Result := Self.AddFlag(ehIgnKindArrayType, ASymTypes); +end; + function TWatchExpectationResult.IgnTypeName(ASymTypes: TSymbolTypes ): TWatchExpectationResult; begin @@ -1421,7 +1430,7 @@ begin if ehNotImplementedData in ehf then AnIgnoreRsn := AnIgnoreRsn + 'Not implemented (Data)'; - Result := CheckData(Context, AnIgnoreRsn); + Result := CheckData(Context, AnIgnoreRsn, False); finally FTest.TestBaseName := CurBaseName; end; @@ -1429,8 +1438,9 @@ begin end; function TWatchExpectationList.CheckData(AContext: TWatchExpTestCurrentData; - AnIgnoreRsn: String): Boolean; + AnIgnoreRsn: String; AnIsNested: Boolean): Boolean; begin + AContext.IsNested := AnIsNested; case AContext.Expectation.ExpResultKind of rkMatch: Result := CheckResultMatch(AContext, AnIgnoreRsn); rkInteger: Result := CheckResultNum(AContext, False, AnIgnoreRsn); @@ -2008,10 +2018,25 @@ var v, n: String; parsed: array of String; i, e: Integer; + ehf: TWatchExpErrorHandlingFlags; + PrePrint: Boolean; begin with AContext.WatchExp do begin Result := True; Expect := AContext.Expectation; + ehf := AContext.Expectation.ExpErrorHandlingFlags[Compiler.SymbolType]; + + PrePrint := AContext.WatchRes.ValueKind = rdkPrePrinted; + + if (not (ehIgnKindArrayType in ehf)) and + (not (PrePrint and AContext.IsNested)) + then begin + TestTrue('ValueKind is array', AContext.WatchRes.ValueKind = rdkArray, AContext, AnIgnoreRsn); + case AContext.Expectation.ExpResultKind of + rkStatArray: TestTrue('Is a static array', AContext.WatchRes.ArrayType = datStatArray, AContext, AnIgnoreRsn); + rkDynArray: TestTrue('Is a dynamic array', AContext.WatchRes.ArrayType = datDynArray, AContext, AnIgnoreRsn); + end; + end; v := FWatchResultPrinter.PrintWatchValue(AContext.WatchRes, wdfDefault); debugln([' expect ',Expect.ExpFullArrayLen,' got "',v,'"' ]); @@ -2039,12 +2064,19 @@ debugln([' expect ',Expect.ExpFullArrayLen,' got "',v,'"' ]); SubContext := AContext; for i := 0 to min(e, length(Expect.ExpSubResults)) - 1 do begin FTest.TestBaseName := n + ' Idx='+IntToStr(i); - SubContext.WatchRes := TWatchResultDataPrePrinted.Create(parsed[i]); + AContext.WatchRes.SetSelectedIndex(i); + + if PrePrint then + SubContext.WatchRes := TWatchResultDataPrePrinted.Create(parsed[i]) + else + SubContext.WatchRes := AContext.WatchRes.SelectedEntry; SubContext.Expectation := Expect.ExpSubResults[i]; - Result := CheckData(SubContext, AnIgnoreRsn); + // if "not preprint" do treat this as non-nested + Result := CheckData(SubContext, AnIgnoreRsn, PrePrint); - FreeAndNil(SubContext.WatchRes); + if PrePrint then + FreeAndNil(SubContext.WatchRes); end; FTest.TestBaseName := n; end;