IdeDebugger: fix watch-data storage. Some nested array or pointer values would hit an assertion if some (but not all) data produced errors.

(cherry picked from commit a1e7cba842)
This commit is contained in:
Martin 2025-06-01 15:35:52 +02:00
parent 9dd0cbd4b1
commit 06d4458a85
2 changed files with 98 additions and 9 deletions

View File

@ -3321,8 +3321,10 @@ begin
AnErrSource.FDataArray[i] := Default(_ERROR_DATA); AnErrSource.FDataArray[i] := Default(_ERROR_DATA);
end; end;
assert(AnOverrideTemplate = nil, 'TGenericWatchResultData.TGenericWatchResultStorage.ImportOverrides: AnOverrideTemplate = nil'); // AnOverrideTemplate is used for any depth of nest level below the array
//if AnOverrideTemplate = nil then (* TODO: each NestedStorage needs its own *)
//assert(AnOverrideTemplate = nil, 'TGenericWatchResultData.TGenericWatchResultStorage.ImportOverrides: AnOverrideTemplate = nil');
if AnOverrideTemplate = nil then
AnOverrideTemplate := _ERROR_CLASS.CreateEmpty; AnOverrideTemplate := _ERROR_CLASS.CreateEmpty;
end; end;

View File

@ -311,6 +311,7 @@ type
procedure TestWatchResPCharOrStringWithArray; procedure TestWatchResPCharOrStringWithArray;
procedure TestWatchArray; procedure TestWatchArray;
procedure TestWatchArrayNested; procedure TestWatchArrayNested;
procedure TestWatchArrayPtrErr; // either ptr or data can be error
procedure TestWatchStuct; procedure TestWatchStuct;
procedure TestWatchStuctNested; procedure TestWatchStuctNested;
procedure TestWatchArrayStuct; procedure TestWatchArrayStuct;
@ -2185,6 +2186,82 @@ begin
if x > 0 then
Res.Free
else
t.Done;
end;
end;
procedure TTestIdeDebuggerWatchResult.TestWatchArrayPtrErr;
var
t: TTestWatchResWrapper;
ProtoIntf: IDbgWatchDataIntf;
Res: TWatchResultData;
i, x, aProtoErr: Integer;
ADataErr, aDataErr1, aDataErr2, aDataErr3: integer;
aSetProto: Boolean;
begin
for x := 0 to 2 do
for aSetProto := low(Boolean) to high(Boolean) do
for aProtoErr := 0 to 4 do
for aDataErr1 := 0 to 4 do
for aDataErr2 := 0 to 4 do
for aDataErr3 := 0 to 4 do
begin
if (not aSetProto) and (aProtoErr > 0) then continue;
t.Init;
ProtoIntf := t.ResIntf.CreateArrayValue(datStatArray, 3, 0);
t.ResIntf.SetTypeName('TMyArray');
if aSetProto then begin
if aProtoErr in [0..3] then
CreateData(ProtoIntf, cdPtr_ErrNum, (aProtoErr and 1) <> 0, 'TMyProto', 987, 87, 'ZZ'); // value part should be ignored
if aProtoErr in [2..4] then
ProtoIntf.CreateError('protoerr');
end;
for i := 0 to 2 do begin
case i of
0: ADataErr := aDataErr1;
1: ADataErr := aDataErr2;
2: ADataErr := aDataErr3;
end;
ProtoIntf := t.ResIntf.SetNextArrayData;
if ADataErr in [0..3] then
CreateData(ProtoIntf, cdPtr_ErrNum, (aDataErr and 1) <> 0, 'TMyProto', 200+i, 990+i, 'E'+IntToStr(i));
if ADataErr in [2..4] then
ProtoIntf.CreateError('dataerr'+IntToStr(i));
end;
Res := t.GetIdeRes;
case x of
1: Res := SaveLoad(Res);
2: Res := Res.CreateCopy;
end;
if x > 0 then
t.Done;
AssertValKind('', Res, rdkArray);
AssertArrayData('', Res, datStatArray, 3, 0, 'TMyArray');
for i := 0 to 2 do begin
case i of
0: ADataErr := aDataErr1;
1: ADataErr := aDataErr2;
2: ADataErr := aDataErr3;
end;
Res.SetSelectedIndex(i);
if ADataErr in [2..4] then
AssertErrData('err-'+IntToStr(i), Res.SelectedEntry, 'dataerr'+IntToStr(i))
else
AssertData(''+IntToStr(i), Res.SelectedEntry, cdPtr_ErrNum, (aDataErr and 1) = 1, 'TMyProto', 200+i, 990+i, 'E'+IntToStr(i));
end;
if x > 0 then if x > 0 then
Res.Free Res.Free
else else
@ -2358,7 +2435,7 @@ var
StrctTyp: TLzDbgStructType; StrctTyp: TLzDbgStructType;
aSetProto, aUsePtr, aUseExtraStuct, WithFld, aErr1, aErr2, aErr1b, aErr2b, aSetProto, aUsePtr, aUseExtraStuct, WithFld, aErr1, aErr2, aErr1b, aErr2b,
AnAbortErr, AnAbortAnchErr, AnAbortErr, AnAbortAnchErr,
aNil, aNilb, aOuterErr, aOuterErr2: Boolean; aNil, aNilb, aOuterErr, aOuterErr2, aStructErr: Boolean;
aEntryType1, aEntryType2: TTestCreateDataKind; aEntryType1, aEntryType2: TTestCreateDataKind;
Res, InnerRes: TWatchResultData; Res, InnerRes: TWatchResultData;
AFlags: TCreateStructFlags; AFlags: TCreateStructFlags;
@ -2374,6 +2451,7 @@ begin
for WithFld := low(Boolean) to high(Boolean) do for WithFld := low(Boolean) to high(Boolean) do
for aOuterErr := low(Boolean) to high(Boolean) do for aOuterErr := low(Boolean) to high(Boolean) do
for aOuterErr2 := low(Boolean) to high(Boolean) do for aOuterErr2 := low(Boolean) to high(Boolean) do
for aStructErr := low(Boolean) to high(Boolean) do
for aEntryType1 := low(TTestCreateDataKind) to high(TTestCreateDataKind) do for aEntryType1 := low(TTestCreateDataKind) to high(TTestCreateDataKind) do
for aEntryType2 := low(TTestCreateDataKind) to high(TTestCreateDataKind) do for aEntryType2 := low(TTestCreateDataKind) to high(TTestCreateDataKind) do
for WithAnch := 0 to 2 do for WithAnch := 0 to 2 do
@ -2402,7 +2480,7 @@ begin
//if aNil <> False then continue; //if aNil <> False then continue;
//if aNilb <> False then continue; //if aNilb <> False then continue;
if (AnAbortAnchErr or AnAbortErr) and not (aOuterErr2) then // partial fields are not allowed, so the struct MUST be replaced by an error if (AnAbortAnchErr or AnAbortErr) and not (aOuterErr2 or aStructErr) then // partial fields are not allowed, so the struct MUST be replaced by an error
continue; continue;
if AnAbortAnchErr and ( (WithAnch = 0) or (not WithFld) ) then // No abort, if no fields in Anchestor if AnAbortAnchErr and ( (WithAnch = 0) or (not WithFld) ) then // No abort, if no fields in Anchestor
continue; continue;
@ -2417,7 +2495,7 @@ begin
then then
continue; continue;
if ( (not WithFld) or (aNil and aNilb) or // no fields (except maybe in prototype) if ( (not WithFld) or (aNil and aNilb) or // no fields (except maybe in prototype)
aOuterErr or aOuterErr2 // or if fields aren't actually stored aOuterErr or aOuterErr2 or aStructErr // or if fields aren't actually stored
) and ) and
( (aEntryType1 > low(TTestCreateDataKind)) or ( (aEntryType1 > low(TTestCreateDataKind)) or
(aEntryType2 > low(TTestCreateDataKind)) or (aEntryType2 > low(TTestCreateDataKind)) or
@ -2431,9 +2509,11 @@ begin
) )
then then
continue; continue;
if aStructErr and (aOuterErr2 or not(aUsePtr or aUseExtraStuct)) then
continue;
if abs(ord(aEntryType1) - ord(aEntryType2)) > 1 then if abs(ord(aEntryType1) - ord(aEntryType2)) > 1 then
continue; continue;
if aSetProto and (AnAbortErr or AnAbortAnchErr or aOuterErr2) then // proto should only affect first element added / first element is otherwise used as proto if aSetProto and (AnAbortErr or AnAbortAnchErr or aOuterErr2 or aStructErr) then // proto should only affect first element added / first element is otherwise used as proto
continue; continue;
t.Init; t.Init;
@ -2484,13 +2564,15 @@ begin
if AnAbortAnchErr then AFlags := AFlags + [csfAbortFieldsAfterAnchestorError]; if AnAbortAnchErr then AFlags := AFlags + [csfAbortFieldsAfterAnchestorError];
CreateStruct(ProtoIntf, StrctTyp, WithFld, WithAnch, WithFld, WithFld, CreateStruct(ProtoIntf, StrctTyp, WithFld, WithAnch, WithFld, WithFld,
aEntryType1, aEntryType2, aErr1b, aErr2b, aNilb, False, AFlags); aEntryType1, aEntryType2, aErr1b, aErr2b, aNilb, False, AFlags);
if aStructErr then
ProtoIntf.CreateError('boom2');
if aOuterErr2 then if aOuterErr2 then
OuterIntf.CreateError('boom2'); OuterIntf.CreateError('boom2');
end end
else begin else begin
// Index 2 // Index 2
CreateStruct(ProtoIntf, StrctTyp, WithFld, WithAnch, WithFld, WithFld, CreateStruct(ProtoIntf, StrctTyp, WithFld, WithAnch, WithFld, WithFld,
aEntryType1, aEntryType2, aErr1b, aErr2b, aNilb, False); aEntryType1, aEntryType2, False, False, aNilb, False, [csfSkipAnchestorErr]);
end; end;
end; end;
@ -2539,6 +2621,11 @@ begin
end end
else if i = 1 then begin else if i = 1 then begin
// Index 1 // Index 1
if aStructErr then begin
AssertErrData('outer err', InnerRes, 'boom2');
continue;
end;
AFlags := []; AFlags := [];
if AnAbortErr then AFlags := AFlags + [csfAbortFieldsAfterError]; if AnAbortErr then AFlags := AFlags + [csfAbortFieldsAfterError];
if AnAbortAnchErr then AFlags := AFlags + [csfAbortFieldsAfterAnchestorError]; if AnAbortAnchErr then AFlags := AFlags + [csfAbortFieldsAfterAnchestorError];
@ -2550,7 +2637,7 @@ begin
// Index 2 // Index 2
AssertValKind('', InnerRes, rdkStruct); AssertValKind('', InnerRes, rdkStruct);
AssertStruct('', InnerRes, StrctTyp, WithFld, WithAnch, WithFld, WithFld, AssertStruct('', InnerRes, StrctTyp, WithFld, WithAnch, WithFld, WithFld,
aEntryType1, aEntryType2, aErr1b, aErr2b, aNilb, '', False); aEntryType1, aEntryType2, False, False, aNilb, '', False, [csfSkipAnchestorErr]);
end; end;
end; end;