diff --git a/components/fpdebug/fpdbgclasses.pp b/components/fpdebug/fpdbgclasses.pp index bf78ad0f67..5259597d32 100644 --- a/components/fpdebug/fpdbgclasses.pp +++ b/components/fpdebug/fpdbgclasses.pp @@ -146,6 +146,7 @@ type function ReadMemory(AnAddress: TDbgPtr; ASize: Cardinal; ADest: Pointer): Boolean; override; overload; function ReadMemory(AnAddress: TDbgPtr; ASize: Cardinal; ADest: Pointer; out ABytesRead: Cardinal): Boolean; override; overload; function ReadMemoryEx(AnAddress, AnAddressSpace: TDbgPtr; ASize: Cardinal; ADest: Pointer): Boolean; override; + function WriteMemory(AnAddress: TDbgPtr; ASize: Cardinal; ASource: Pointer): Boolean; override; overload; function ReadRegister(ARegNum: Cardinal; out AValue: TDbgPtr; AContext: TFpDbgLocationContext): Boolean; override; function RegisterSize(ARegNum: Cardinal): Integer; override; @@ -1303,6 +1304,12 @@ begin result := GetDbgProcess.ReadData(AnAddress, ASize, ADest^); end; +function TDbgMemReader.WriteMemory(AnAddress: TDbgPtr; ASize: Cardinal; + ASource: Pointer): Boolean; +begin + result := GetDbgProcess.WriteData(AnAddress, ASize, ASource^); +end; + function TDbgMemReader.ReadRegister(ARegNum: Cardinal; out AValue: TDbgPtr; AContext: TFpDbgLocationContext): Boolean; var ARegister: TDbgRegisterValue; diff --git a/components/fpdebug/fpdbgdwarf.pas b/components/fpdebug/fpdbgdwarf.pas index 8ad912dea6..36e6734c5a 100644 --- a/components/fpdebug/fpdbgdwarf.pas +++ b/components/fpdebug/fpdbgdwarf.pas @@ -306,13 +306,14 @@ type function GetFieldFlags: TFpValueFieldFlags; override; function GetAsString: AnsiString; override; function GetAsWideString: WideString; override; + procedure SetAsString(AValue: AnsiString); override; end; { TFpValueDwarfPointer } TFpValueDwarfPointer = class(TFpValueDwarfNumeric) private - FPointetToAddr: TFpDbgMemLocation; + FPointedToAddr: TFpDbgMemLocation; function GetDerefAddress: TFpDbgMemLocation; protected function GetAsCardinal: QWord; override; @@ -384,6 +385,7 @@ type function GetMember(AIndex: Int64): TFpValue; override; function GetAsCardinal: QWord; override; // only up to qmord function IsValidTypeCast: Boolean; override; + procedure SetAsString(AValue: AnsiString); override; public destructor Destroy; override; end; @@ -2133,13 +2135,18 @@ procedure TFpValueDwarfInteger.SetAsInteger(AValue: Int64); var Size: TFpDbgValueSize; begin - if (not GetSize(Size)) or (Size <= 0) or (Size > SizeOf(AValue)) then - inherited SetAsCardinal(AValue) + if (not GetSize(Size)) or (Size <= 0) or (Size > SizeOf(AValue)) then begin + inherited SetAsCardinal(AValue); + end else if not Context.WriteSignedInt(OrdOrDataAddr, Size, AValue) then begin SetLastError(Context.LastMemError); + Exclude(FEvaluated, doneInt); + end + else begin + FIntValue := AValue; + Include(FEvaluated, doneInt); end; - Exclude(FEvaluated, doneUInt); end; procedure TFpValueDwarfInteger.SetAsCardinal(AValue: QWord); @@ -2185,13 +2192,18 @@ procedure TFpValueDwarfCardinal.SetAsCardinal(AValue: QWord); var Size: TFpDbgValueSize; begin - if (not GetSize(Size)) or (Size <= 0) or (Size > SizeOf(AValue)) then - inherited SetAsCardinal(AValue) + if (not GetSize(Size)) or (Size <= 0) or (Size > SizeOf(AValue)) then begin + inherited SetAsCardinal(AValue); + end else if not Context.WriteUnsignedInt(OrdOrDataAddr, Size, AValue) then begin SetLastError(Context.LastMemError); + Exclude(FEvaluated, doneUInt); + end + else begin + FValue := AValue; + Include(FEvaluated, doneUInt); end; - Exclude(FEvaluated, doneUInt); end; { TFpValueDwarfFloat } @@ -2289,6 +2301,32 @@ begin Result := WideChar(Word(GetAsCardinal)); end; +procedure TFpValueDwarfChar.SetAsString(AValue: AnsiString); +var + Size: TFpDbgValueSize; + u: UnicodeString; +begin + if not GetSize(Size) then + Size := ZeroSize; + if Size.Size > 2 then begin + inherited SetAsString(AValue); + end + else + if Size.Size = 2 then begin + u := UTF8Decode(AValue); + if Length(u) <> 1 then + inherited SetAsString(AValue) // error + else + SetAsCardinal(Word(u[1])); + end + else begin + if Length(AValue) <> 1 then + inherited SetAsString(AValue) // error + else + SetAsCardinal(Byte(AValue[1])); + end; +end; + { TFpValueDwarfPointer } function TFpValueDwarfPointer.GetDerefAddress: TFpDbgMemLocation; @@ -2297,7 +2335,7 @@ var Addr: TFpDbgMemLocation; begin if doneAddr in FEvaluated then begin - Result := FPointetToAddr; + Result := FPointedToAddr; exit; end; Include(FEvaluated, doneAddr); @@ -2312,7 +2350,7 @@ begin SetLastError(Context.LastMemError); end; end; - FPointetToAddr := Result; + FPointedToAddr := Result; end; function TFpValueDwarfPointer.GetAsCardinal: QWord; @@ -2493,8 +2531,14 @@ end; procedure TFpValueDwarfPointer.SetAsCardinal(AValue: QWord); begin - if not Context.WriteSignedInt(OrdOrDataAddr, SizeVal(Context.SizeOfAddress), AValue) then + if not Context.WriteSignedInt(OrdOrDataAddr, SizeVal(Context.SizeOfAddress), AValue) then begin SetLastError(Context.LastMemError); + Exclude(FEvaluated, doneAddr); + end + else begin + FPointedToAddr := TargetLoc(TDBGPtr(AValue)); + Include(FEvaluated, doneAddr); + end; end; { TFpValueDwarfEnum } @@ -2554,13 +2598,18 @@ procedure TFpValueDwarfEnum.SetAsCardinal(AValue: QWord); var Size: TFpDbgValueSize; begin - if (not GetSize(Size)) or (Size <= 0) or (Size > SizeOf(AValue)) then - inherited SetAsCardinal(AValue) + if (not GetSize(Size)) or (Size <= 0) or (Size > SizeOf(AValue)) then begin + inherited SetAsCardinal(AValue); + end else if not Context.WriteEnum(OrdOrDataAddr, Size, AValue) then begin SetLastError(Context.LastMemError); + Exclude(FEvaluated, doneUInt); + end + else begin + FValue := AValue; + Include(FEvaluated, doneUInt); end; - Exclude(FEvaluated, doneUInt); end; function TFpValueDwarfEnum.GetAsString: AnsiString; @@ -2811,8 +2860,9 @@ var Size: TFpDbgValueSize; begin Result := 0; - if not GetSize(Size) then + if (not GetSize(Size)) or (Size < 0) or (Size > SizeOf(QWord)) then exit; + InitMap; if (Size <= SizeOf(Result)) and (length(FMem) > 0) then move(FMem[0], Result, Min(SizeOf(Result), SizeToFullBytes(Size))); end; @@ -2849,6 +2899,159 @@ begin Result := False; end; +procedure TFpValueDwarfSet.SetAsString(AValue: AnsiString); +type + TCharSet = set of char; + function CheckInChar(var p: PChar; c: TCharSet): Boolean; + begin + Result := p^ in c; + if Result then + inc(p) + else + SetLastError(CreateError(fpErrFailedWriteMem)); + end; + procedure SkipSpaces(var p: Pchar); + begin + while p^ in [' ', #9] do inc(p); + end; + function CopySubString(PEnd, PStart: PChar): String; + begin + SetLength(Result, PEnd - PStart); + move(PStart^, Result[1], PEnd - PStart); + end; +var + Size: TFpDbgValueSize; + WriteMem: array of Byte; + p, p2: PChar; + s: String; + idx: Integer; + t: TFpSymbolDwarfType; + nest: TFpSymbol; + v, lb, hb, MemIdx, Bit: Int64; + DAddr: TFpDbgMemLocation; +begin + if not GetSize(Size) then + Size := ZeroSize; + if (Size <= 0) then begin + SetLastError(CreateError(fpErrFailedWriteMem)); + exit; + end; + InitMap; + t := TypeInfo; + if t = nil then exit; + t := t.TypeInfo; + if t = nil then exit; + assert(t is TFpSymbolDwarfType, 'TDbgDwarfSetSymbolValue.GetMember t'); + + SetLength(WriteMem, SizeToFullBytes(Size)); + + p := Pchar(AValue); + SkipSpaces(p); + if not CheckInChar(p, ['[']) then + exit; + + SkipSpaces(p); + if p^ <> ']' then begin // not an empty set + + if t.Kind = skEnum then begin + while p^ in ['a'..'z', 'A'..'Z', '_'] do begin + p2 := p; + inc(p); + while p^ in ['a'..'z', 'A'..'Z', '_', '0'..'9'] do + inc(p); + s := LowerCase(CopySubString(p, p2)); + + idx := t.GetNestedSymbolCount - 1; + while idx >= 0 do begin + nest := t.GetNestedSymbol(idx); + if (nest <> nil) and (LowerCase(nest.Name) = s) then + break; + dec(idx); + end; + if (idx >= 0) then begin + v := nest.OrdinalValue; + if (v >= 0) and (v < Length(WriteMem) * 8) then begin + MemIdx := v shr 3; + Bit := 1 shl (v and 7); + WriteMem[MemIdx] := WriteMem[MemIdx] or Bit; + end + else + idx := -1; + end; + if idx < 0 then begin + SetLastError(CreateError(fpErrFailedWriteMem)); + exit; + end; + + SkipSpaces(p); + if p^ = ']' then + break; + if not CheckInChar(p, [',']) then + exit; + SkipSpaces(p); + end; + SkipSpaces(p); + end + else begin // set of 1..9 + if not t.GetValueBounds(nil, lb, hb) then begin + SetLastError(CreateError(fpErrFailedWriteMem)); + exit; + end; + + while p^ in ['0'..'9', '$', '%', '&'] do begin + p2 := p; + inc(p); + case p[-1] of + '$': while p^ in ['a'..'f', 'A'..'F', '0'..'9'] do inc(p); + '&': while p^ in ['0'..'7'] do inc(p); + '%': while p^ in ['0'..'1'] do inc(p); + else while p^ in ['0'..'9'] do inc(p); + end; + if not TryStrToInt(CopySubString(p, p2), idx) then begin + SetLastError(CreateError(fpErrFailedWriteMem)); + exit; + end; + idx := idx - lb; + + if (idx >= 0) and (idx < Length(WriteMem) * 8) then begin + MemIdx := idx shr 3; + Bit := 1 shl (idx and 7); + WriteMem[MemIdx] := WriteMem[MemIdx] or Bit; + end + else begin + SetLastError(CreateError(fpErrFailedWriteMem)); + exit; + end; + + SkipSpaces(p); + if p^ = ']' then + break; + if not CheckInChar(p, [',']) then + exit; + SkipSpaces(p); + end; + SkipSpaces(p); + end; + + end; + if not CheckInChar(p, [']']) then + exit; + SkipSpaces(p); + if not CheckInChar(p, [#0]) then + exit; + + // we got the value + FMem := nil; + + // todo writeset + GetDwarfDataAddress(DAddr); + if not Context.WriteSet(DAddr, Size, WriteMem) then begin + SetLastError(Context.LastMemError); + exit; // TODO: error + end; + +end; + destructor TFpValueDwarfSet.Destroy; begin FTypedNumValue.ReleaseReference; @@ -3649,12 +3852,16 @@ begin if Form in [DW_FORM_data1, DW_FORM_data2, DW_FORM_data4, DW_FORM_data8, DW_FORM_sdata, DW_FORM_udata] then begin - if AReadState <> nil then - AReadState^ := rfConst; - Result := InformationEntry.ReadValue(AnAttribData, AValue); - if not Result then + if Result then begin + if AReadState <> nil then + AReadState^ := rfConst; + end + else begin + if AReadState <> nil then + AReadState^ := rfError; SetLastError(AValueObj, CreateError(fpErrAnyError)); + end; end else @@ -3682,6 +3889,8 @@ begin AValue := ValObj.AsInteger; if IsError(ValObj.LastError) then begin Result := False; + if AReadState <> nil then + AReadState^ := rfError; SetLastError(AValueObj, ValObj.LastError); end; ValObj.ReleaseReference; @@ -3695,8 +3904,11 @@ begin RefSymbol.ReleaseReference; end; end; - if (not Result) and (not HasError(AValueObj)) then + if (not Result) and (not HasError(AValueObj)) then begin + if AReadState <> nil then + AReadState^ := rfError; SetLastError(AValueObj, CreateError(fpErrAnyError)); + end; end else @@ -3720,13 +3932,19 @@ begin InitLocParserData.ObjectDataAddress := AValueObj.OrdOrAddress; InitLocParserData.ObjectDataAddrPush := False; Result := LocationFromAttrData(AnAttribData, AValueObj, t, @InitLocParserData); - if Result then - AValue := Int64(t.Address) - else + if Result then begin + AValue := Int64(t.Address); + end + else begin + if AReadState <> nil then + AReadState^ := rfError; SetLastError(AValueObj, CreateError(fpErrLocationParser)); + end; end else begin + if AReadState <> nil then + AReadState^ := rfError; SetLastError(AValueObj, CreateError(fpErrAnyError)); end; diff --git a/components/fpdebug/fpdmemorytools.pas b/components/fpdebug/fpdmemorytools.pas index d11809df34..812c3f8ba0 100644 --- a/components/fpdebug/fpdmemorytools.pas +++ b/components/fpdebug/fpdmemorytools.pas @@ -146,6 +146,8 @@ type // AnOpts: TFpDbgMemReadOptions): Boolean; inline; function ReadSet (const ALocation: TFpDbgMemLocation; ASize: TFpDbgValueSize; out AValue: TBytes): Boolean; inline; + function WriteSet (const ALocation: TFpDbgMemLocation; ASize: TFpDbgValueSize; + const AValue: TBytes): Boolean; inline; //function ReadSet (const ALocation: TFpDbgMemLocation; ASize: TFpDbgValueSize; // out AValue: TBytes; // AnOpts: TFpDbgMemReadOptions): Boolean; inline; @@ -168,6 +170,7 @@ type // inherited Memreaders should implement partial size ReadMemory, and forward it to the TDbgProcess class function ReadMemory(AnAddress: TDbgPtr; ASize: Cardinal; ADest: Pointer; out ABytesRead: Cardinal): Boolean; virtual; overload; function ReadMemoryEx(AnAddress, AnAddressSpace: TDbgPtr; ASize: Cardinal; ADest: Pointer): Boolean; virtual; abstract; + function WriteMemory(AnAddress: TDbgPtr; ASize: Cardinal; ASource: Pointer): Boolean; virtual; abstract; overload; // ReadRegister may need TargetMemConvertor // Register with reduced size are treated as unsigned // TODO: ReadRegister should only take THREAD-ID, not context @@ -975,6 +978,12 @@ begin Result := MemManager.ReadMemory(rdtSet, ALocation, ASize, @AValue[0], Length(AValue), Self); end; +function TFpDbgLocationContext.WriteSet(const ALocation: TFpDbgMemLocation; + ASize: TFpDbgValueSize; const AValue: TBytes): Boolean; +begin + Result := MemManager.WriteMemory(rdtSet, ALocation, ASize, @AValue[0], Length(AValue), Self); +end; + function TFpDbgLocationContext.ReadFloat(const ALocation: TFpDbgMemLocation; ASize: TFpDbgValueSize; out AValue: Extended): Boolean; begin @@ -1689,6 +1698,12 @@ begin Result := True; end; + mlfTargetMem: + begin + if (BitOffset = 0) and (ADestSize.BitSize = 0) and (ADestSize.Size > 0) then begin + FMemReader.WriteMemory(LocToAddr(ADestLocation), SizeToFullBytes(ADestSize), ASource); + end; + end; end; if (not Result) and (not IsError(FLastError)) then diff --git a/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas b/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas index 3b25fe0be7..d3bbd9ade4 100644 --- a/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas +++ b/components/lazdebuggers/lazdebuggerfp/fpdebugdebugger.pas @@ -121,6 +121,13 @@ type constructor Create(ADebugger: TFpDebugDebuggerBase; ALocals: TLocals); end; + { TFpThreadWorkerModifyUpdate } + + TFpThreadWorkerModifyUpdate = class(TFpThreadWorkerModify) + protected + procedure DoCallback_DecRef(Data: PtrInt = 0); override; + end; + { TFpThreadWorkerWatchValueEvalUpdate } TFpThreadWorkerWatchValueEvalUpdate = class(TFpThreadWorkerWatchValueEval) @@ -555,6 +562,7 @@ type function ReadMemoryEx(AnAddress, AnAddressSpace: TDbgPtr; ASize: Cardinal; ADest: Pointer): Boolean; override; function ReadRegister(ARegNum: Cardinal; out AValue: TDbgPtr; AContext: TFpDbgLocationContext): Boolean; override; + //WriteMemory is not overwritten. It must ONLY be called in the debug-thread end; { TFpWaitForConsoleOutputThread } @@ -576,6 +584,18 @@ begin RegisterDebugger(TFpDebugDebugger); end; +{ TFpThreadWorkerModifyUpdate } + +procedure TFpThreadWorkerModifyUpdate.DoCallback_DecRef(Data: PtrInt); +begin + // + FDebugger.Locals.CurrentLocalsList.Clear; + FDebugger.Watches.CurrentWatches.ClearValues; + FDebugger.CallStack.CurrentCallStackList.Clear; + + UnQueue_DecRef; +end; + { TFpDbgDebggerThreadWorkerItemHelper } function TFpDbgDebggerThreadWorkerItemHelper.FpDebugger: TFpDebugDebugger; @@ -3083,6 +3103,7 @@ var WorkItem: TFpThreadWorkerControllerRun; AThreadId, AStackFrame: Integer; EvalWorkItem: TFpThreadWorkerCmdEval; + WorkItemModify: TFpThreadWorkerModifyUpdate; begin result := False; if assigned(FDbgController) then @@ -3254,6 +3275,15 @@ begin FWorkQueue.PushItem(FEvalWorkItem); Result := True; end; + dcModify: + begin + GetCurrentThreadAndStackFrame(AThreadId, AStackFrame); + WorkItemModify := TFpThreadWorkerModifyUpdate.Create(Self, AnsiString(AParams[0].VAnsiString), AnsiString(AParams[1].VAnsiString), + AStackFrame, AThreadId); + FWorkQueue.PushItem(WorkItemModify); + WorkItemModify.DecRef; + Result := True; + end; dcSendConsoleInput: begin FDbgController.CurrentProcess.SendConsoleInput(String(AParams[0].VAnsiString)); @@ -3795,7 +3825,8 @@ end; class function TFpDebugDebugger.GetSupportedCommands: TDBGCommands; begin Result:=[dcRun, dcStop, dcStepIntoInstr, dcStepOverInstr, dcStepOver, - dcStepTo, dcRunTo, dcPause, dcStepOut, dcStepInto, dcEvaluate, dcSendConsoleInput + dcStepTo, dcRunTo, dcPause, dcStepOut, dcStepInto, dcEvaluate, dcModify, + dcSendConsoleInput {$IFDEF windows} , dcAttach, dcDetach {$ENDIF} {$IFDEF linux} , dcAttach, dcDetach {$ENDIF} ]; diff --git a/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerutils.pas b/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerutils.pas index 20276788b0..8ee2780d2c 100644 --- a/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerutils.pas +++ b/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerutils.pas @@ -123,6 +123,7 @@ type type TFpThreadWorkerPriority = ( + twpModify, // this is a user actions twpUser, twpThread, twpStack, twpLocal, twpWatch, twpContinue diff --git a/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerworkthreads.pas b/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerworkthreads.pas index ba7e8a5e58..3c52f2450b 100644 --- a/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerworkthreads.pas +++ b/components/lazdebuggers/lazdebuggerfp/fpdebugdebuggerworkthreads.pas @@ -210,6 +210,26 @@ type destructor Destroy; override; end; + { TFpThreadWorkerModify } + + TFpThreadWorkerModify = class(TFpDbgDebggerThreadWorkerLinkedItem) + private + FExpression, FNewVal: String; + FStackFrame, FThreadId: Integer; + FSuccess: Boolean; + protected + procedure DoCallback_DecRef(Data: PtrInt = 0); virtual; abstract; + procedure DoExecute; override; + property Success: Boolean read FSuccess; + public + constructor Create(ADebugger: TFpDebugDebuggerBase; + //APriority: TFpThreadWorkerPriority; + const AnExpression, ANewValue: String; + AStackFrame, AThreadId: Integer + ); + function DebugText: String; override; + end; + { TFpThreadWorkerEvaluate } TFpThreadWorkerEvaluate = class(TFpDbgDebggerThreadWorkerLinkedItem) @@ -692,6 +712,70 @@ begin inherited Destroy; end; +{ TFpThreadWorkerModify } + +procedure TFpThreadWorkerModify.DoExecute; +var + APasExpr: TFpPascalExpression; + ResValue: TFpValue; + ExpressionScope: TFpDbgSymbolScope; + i64: int64; + c64: QWord; +begin + FSuccess := False; + ExpressionScope := FDebugger.FDbgController.CurrentProcess.FindSymbolScope(FThreadId, FStackFrame); + if ExpressionScope = nil then + exit; + + APasExpr := TFpPascalExpression.Create(FExpression, ExpressionScope); + try + APasExpr.ResultValue; // trigger full validation + if not APasExpr.Valid then + exit; + + ResValue := APasExpr.ResultValue; + if ResValue = nil then + exit; + + case ResValue.Kind of + skInteger: if TryStrToInt64(FNewVal, i64) then ResValue.AsInteger := i64; + skCardinal: if TryStrToQWord(FNewVal, c64) then ResValue.AsCardinal := c64; + skBoolean: case LowerCase(trim(FNewVal)) of + 'true': ResValue.AsBool := True; + 'false': ResValue.AsBool := False; + end; + skChar: ResValue.AsString := FNewVal; + skEnum: ResValue.AsString := FNewVal; + skSet: ResValue.AsString := FNewVal; + skPointer: if TryStrToQWord(FNewVal, c64) then ResValue.AsCardinal := c64; + skFloat: ; + skCurrency: ; + skVariant: ; + end; + + + finally + APasExpr.Free; + ExpressionScope.ReleaseReference; + Queue(@DoCallback_DecRef); + end; +end; + +constructor TFpThreadWorkerModify.Create(ADebugger: TFpDebugDebuggerBase; + const AnExpression, ANewValue: String; AStackFrame, AThreadId: Integer); +begin + inherited Create(ADebugger, twpModify); + FExpression := AnExpression; + FNewVal := ANewValue; + FStackFrame := AStackFrame; + FThreadId := AThreadId; +end; + +function TFpThreadWorkerModify.DebugText: String; +begin + Result := inherited DebugText; +end; + { TFpThreadWorkerEvaluate } function TFpThreadWorkerEvaluate.DoWatchFunctionCall( diff --git a/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas b/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas index fa17c664e4..237b43bc7d 100644 --- a/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas +++ b/components/lazdebuggers/lazdebuggerfp/test/testwatches.pas @@ -7,7 +7,7 @@ interface uses Classes, SysUtils, fpcunit, testregistry, TestBase, TestDbgControl, TestDbgTestSuites, TestOutputLogger, TTestWatchUtilities, TestCommonSources, - TestDbgConfig, DbgIntfDebuggerBase, DbgIntfBaseTypes; + TestDbgConfig, DbgIntfDebuggerBase, DbgIntfBaseTypes, Forms; type @@ -15,6 +15,9 @@ type TTestWatches = class(TDBGTestCase) private + FEvalDone: Boolean; + procedure DoEvalDone(Sender: TObject; ASuccess: Boolean; + ResultText: String; ResultDBGType: TDBGType); procedure RunToPause(var ABrk: TDBGBreakPoint); published procedure TestWatchesScope; @@ -23,6 +26,7 @@ type procedure TestWatchesAddressOf; procedure TestWatchesTypeCast; procedure TestWatchesExpression; + procedure TestWatchesModify; procedure TestWatchesErrors; end; @@ -30,7 +34,7 @@ implementation var ControlTestWatch, ControlTestWatchScope, ControlTestWatchValue, ControlTestWatchFunct, - ControlTestWatchAddressOf, ControlTestWatchTypeCast, + ControlTestWatchAddressOf, ControlTestWatchTypeCast, ControlTestModify, ControlTestExpression, ControlTestErrors: Pointer; procedure TTestWatches.RunToPause(var ABrk: TDBGBreakPoint); @@ -40,6 +44,14 @@ begin ABrk.Enabled := False; end; +procedure TTestWatches.DoEvalDone(Sender: TObject; ASuccess: Boolean; + ResultText: String; ResultDBGType: TDBGType); +begin + if ResultDBGType <> nil then + ResultDBGType.Free; + FEvalDone := True; +end; + procedure TTestWatches.TestWatchesScope; procedure AddWatchesForClassMethods(t: TWatchExpectationList; AName: String; AStackOffs: Integer); @@ -606,7 +618,7 @@ procedure TTestWatches.TestWatchesValue; t.Add(AName, p+'Byte'+e, weCardinal(1+n, 'Byte', 1)); t.Add(AName, p+'Word'+e, weCardinal(100+n, 'Word', 2)); t.Add(AName, p+'Longword'+e, weCardinal(1000+n, 'Longword', 4)); - t.Add(AName, p+'QWord'+e, weCardinal(10000+n, 'QWord', 5)); + t.Add(AName, p+'QWord'+e, weCardinal(10000+n, 'QWord', 8)); t.Add(AName, p+'Shortint'+e, weInteger (50+n, 'Shortint', 1)); t.Add(AName, p+'Smallint'+e, weInteger (500+n, 'Smallint', 2)); t.Add(AName, p+'Longint'+e, weInteger (5000+n, 'Longint', 4)); @@ -628,8 +640,23 @@ procedure TTestWatches.TestWatchesValue; t.Add(AName, p+'Longint_3'+e, weInteger(-20123456+n, 'Longint', 4)); t.Add(AName, p+'Int64_3'+e, weInteger(-9123372036854775801+n, 'Int64', 8)); - t.Add(AName, p+'Bool1'+e, weBool(False )); - t.Add(AName, p+'Bool2'+e, weBool(True )); + t.Add(AName, p+'Bool1'+e, weBool(False )); + t.Add(AName, p+'Bool2'+e, weBool(True )); + t.Add(AName, p+'WBool1'+e, weBool(False, 'Boolean16')); + t.Add(AName, p+'WBool2'+e, weBool(True , 'Boolean16')); + t.Add(AName, p+'LBool1'+e, weBool(False, 'Boolean32')); + t.Add(AName, p+'LBool2'+e, weBool(True , 'Boolean32')); + t.Add(AName, p+'QBool1'+e, weBool(False, 'Boolean64')); + t.Add(AName, p+'QBool2'+e, weBool(True , 'Boolean64')); + + t.Add(AName, p+'ByteBool1'+e, weSizedBool(False, 'ByteBool')); + t.Add(AName, p+'ByteBool2'+e, weSizedBool(True , 'ByteBool')); + t.Add(AName, p+'WordBool1'+e, weSizedBool(False, 'WordBool')); + t.Add(AName, p+'WordBool2'+e, weSizedBool(True , 'WordBool')); + t.Add(AName, p+'LongBool1'+e, weSizedBool(False, 'LongBool')); + t.Add(AName, p+'LongBool2'+e, weSizedBool(True , 'LongBool')); + t.Add(AName, p+'QWordBool1'+e, weSizedBool(False, 'QWordBool')); + t.Add(AName, p+'QWordBool2'+e, weSizedBool(True , 'QWordBool')); t.Add(AName, p+'Real'+e, weFloat(50.25+n, 'Real' )); t.Add(AName, p+'Single'+e, weSingle(100.125+n, 'Single' )); @@ -947,10 +974,19 @@ StartIdxClassConst := t.Count; // t.Add(AName, 'EnVal2', weMatch('xxx', skEnumValue)); + t.Add(AName, p+'Enum16'+e, weEnum('ExVal23', 'TEnum16')); + t.Add(AName, p+'Enum16A'+e, weEnum('ExValX5', 'TEnum16')); + t.Add(AName, p+'Set'+e, weSet(['EnVal2', 'EnVal4'], 'TSet')).Skip([stDwarf]); t.Add(AName, p+'Set2'+e, weSet(['EnVal1', 'EnVal4'], '{set}')).Skip([stDwarf]) .SkipIf(ALoc = tlParam).SkipIf(ALoc = tlPointer); + t.Add(AName, p+'Set4'+e, weSet(['E4Val02', 'E4Val0A'], 'TSet4')).Skip([stDwarf]); + t.Add(AName, p+'Set5'+e, weSet(['E5Val02', 'E5Val12'], 'TSet5')).Skip([stDwarf]); + t.Add(AName, p+'Set6'+e, weSet(['E6Val02', 'E6Val1A'], 'TSet6')).Skip([stDwarf]); + t.Add(AName, p+'Set7'+e, weSet(['E7Val02', 'E7Val3A'], 'TSet7')).Skip([stDwarf]); + t.Add(AName, p+'Set8'+e, weSet(['E8Val02', 'E8Val5B'], 'TSet8')).Skip([stDwarf]); + t.Add(AName, p+'SmallSet'+e, weSet(['22', '24', '25'], 'TSmallRangeSet')).Skip([stDwarf]) .SkipIf(ALoc = tlParam).SkipIf(ALoc = tlPointer); t.Add(AName, p+'SmallSet2'+e, weSet(['21', '24', '25'], '{set}')).Skip([stDwarf]) @@ -2110,7 +2146,7 @@ procedure TTestWatches.TestWatchesExpression; ); var p, e, p2, e2: String; - n, StartIdx, i, n2: Integer; + n, i, n2: Integer; begin p := APrefix; e := APostFix; @@ -2202,7 +2238,7 @@ var ExeName: String; t: TWatchExpectationList; Src: TCommonSource; - BrkPrg, BrkFoo, BrkFooVar, BrkFooConstRef: TDBGBreakPoint; + BrkPrg: TDBGBreakPoint; begin if SkipTest then exit; if not TestControlCanTest(ControlTestExpression) then exit; @@ -2414,12 +2450,427 @@ begin end; end; +procedure TTestWatches.TestWatchesModify; + procedure WaitForModify; + var + i: Integer; + begin + // Modify does not have a callback (yet). So wait for an eval + FEvalDone := False; + Debugger.LazDebugger.Evaluate('p', @DoEvalDone); + i := 0; + while (not FEvalDone) and (i < 5*400) do begin // timeout after 5 sec + Application.Idle(False); + Debugger.WaitForFinishRun(25); + inc(i); + end; + end; +var + ExeName: String; + t: TWatchExpectationList; + Src: TCommonSource; + BrkPrg: TDBGBreakPoint; +begin + if SkipTest then exit; + if not TestControlCanTest(ControlTestModify) then exit; + t := nil; + + Src := GetCommonSourceFor('WatchesValuePrg.Pas'); + TestCompile(Src, ExeName); + + AssertTrue('Start debugger', Debugger.StartDebugger(AppDir, ExeName)); + + try + t := TWatchExpectationList.Create(Self); + t.AcceptSkSimple := [skInteger, skCardinal, skBoolean, skChar, skFloat, + skString, skAnsiString, skCurrency, skVariant, skWideString, + skInterface]; + t.AddTypeNameAlias('integer', 'integer|longint'); + t.AddTypeNameAlias('cardinal', 'cardinal|longword'); + t.AddTypeNameAlias('BYTEBOOL', 'boolean|BYTEBOOL'); + t.AddTypeNameAlias('TEnumSub', 'TEnum|TEnumSub'); + + BrkPrg := Debugger.SetBreakPoint(Src, 'Prg'); + AssertDebuggerNotInErrorState; + + (* ************ Nested Functions ************* *) + + RunToPause(BrkPrg); + + t.Clear; + + //t.Add(AName, p+'QWord'+e, weCardinal(10000+n, 'QWord', 8)); + //t.Add(AName, p+'Shortint'+e, weInteger (50+n, 'Shortint', 1)); + + t.Add('(before)', 'ModifyTestByte.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestByte.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestByte.val', weCardinal($01, 'Byte', 1)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestByte.val', '131'); + WaitForModify; + t.Add('(after)', 'ModifyTestByte.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestByte.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestByte.val', weCardinal(131, 'Byte', 1)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestWord.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestWord.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestWord.val', weCardinal($0101, 'Word', 2)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestWord.val', '35131'); + WaitForModify; + t.Add('(after)', 'ModifyTestWord.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestWord.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestWord.val', weCardinal(35131, 'Word', 2)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestCardinal.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestCardinal.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestCardinal.val', weCardinal($81020102, 'Cardinal', 4)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestCardinal.val', '$9AA93333'); + WaitForModify; + t.Add('(after)', 'ModifyTestCardinal.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestCardinal.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestCardinal.val', weCardinal($9AA93333, 'Cardinal', 4)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestQWord.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestQWord.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestQWord.val', weCardinal(qword($8102010201020102), 'QWord', 8)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestQWord.val', '$9AA9333344224422'); + WaitForModify; + t.Add('(after)', 'ModifyTestQWord.pre', weCardinal(qword($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestQWord.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestQWord.val', weCardinal(qword($9AA9333344224422), 'QWord', 8)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestInt.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestInt.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestInt.val', weInteger(-$01030103, 'Integer', 4)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestInt.val', '$44225522'); + WaitForModify; + t.Add('(after)', 'ModifyTestInt.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestInt.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestInt.val', weInteger($44225522, 'Integer', 4)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestInt64.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestInt64.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestInt64.val', weInteger(-$0103010301030103, 'Int64', 8)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestInt64.val', '$4422552201020102'); + WaitForModify; + t.Add('(after)', 'ModifyTestInt64.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestInt64.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestInt64.val', weInteger($4422552201020102, 'Int64', 8)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestPointer.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestPointer.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestPointer.val', wePointerAddr(Pointer(30), 'Pointer')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestPointer.val', '50'); + WaitForModify; + t.Add('(after)', 'ModifyTestPointer.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestPointer.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestPointer.val', wePointerAddr(Pointer(50), 'Pointer')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestPWord.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestPWord.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestPWord.val', wePointerAddr(Pointer(40), 'PWord')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestPWord.val', '70'); + WaitForModify; + t.Add('(after)', 'ModifyTestPWord.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestPWord.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestPWord.val', wePointerAddr(Pointer(70), 'PWord')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestBool.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestBool.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestBool.val', weBool(True)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestBool.val', 'False'); + WaitForModify; + t.Add('(after)', 'ModifyTestBool.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestBool.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestBool.val', weBool(False)); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestByteBool.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestByteBool.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestByteBool.val', weSizedBool(False, 'BYTEBOOL')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestByteBool.val', 'True'); + WaitForModify; + t.Add('(after)', 'ModifyTestByteBool.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestByteBool.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestByteBool.val', weSizedBool(True, 'BYTEBOOL')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestChar.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestChar.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestChar.val', weChar('B')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestChar.val', 'X'); + WaitForModify; + t.Add('(after)', 'ModifyTestChar.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestChar.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestChar.val', weChar('X')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestWideChar.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestWideChar.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestWideChar.val', weWideChar('B')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestWideChar.val', 'Y'); + WaitForModify; + t.Add('(after)', 'ModifyTestWideChar.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestWideChar.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestWideChar.val', weWideChar('Y')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestEnum.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestEnum.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestEnum.val', weEnum('EnVal2')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestEnum.val', 'EnVal4'); + WaitForModify; + t.Add('(after)', 'ModifyTestEnum.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestEnum.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestEnum.val', weEnum('EnVal4')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestEnum16.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestEnum16.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestEnum16.val', weEnum('ExValX2')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestEnum16.val', 'ExValX7'); + WaitForModify; + t.Add('(after)', 'ModifyTestEnum16.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestEnum16.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestEnum16.val', weEnum('ExValX7')); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + if Compiler.SymbolType <> stDwarf then begin + + t.Add('(before)', 'ModifyTestSet.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestSet.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestSet.val', weSet(['EnVal2', 'EnVal4'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet.val', ' [ EnVal1, EnVal3] '); + WaitForModify; + t.Add('(after)', 'ModifyTestSet.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet.val', weSet(['EnVal1', 'EnVal3'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet.val', ' [ EnVal1] '); + WaitForModify; + t.Add('(after)', 'ModifyTestSet.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet.val', weSet(['EnVal1'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet.val', ' [ EnVal4] '); + WaitForModify; + t.Add('(after)', 'ModifyTestSet.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet.val', weSet(['EnVal4'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestSet4.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestSet4.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestSet4.val', weSet(['E4Val02', 'E4Val09'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet4.val', '[E4Val03,E4Val0A ]'); + WaitForModify; + t.Add('(after)', 'ModifyTestSet4.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet4.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet4.val', weSet(['E4Val03', 'E4Val0A'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet4.val', ' [E4Val0B ]'); + WaitForModify; + t.Add('(after)', 'ModifyTestSet4.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet4.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet4.val', weSet(['E4Val0B'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestSet6.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestSet6.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestSet6.val', weSet(['E6Val02', 'E6Val1A'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet6.val', '[E6Val03,E6Val1B ]'); + WaitForModify; + t.Add('(after)', 'ModifyTestSet6.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet6.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet6.val', weSet(['E6Val03', 'E6Val1B'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestSet7.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestSet7.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestSet7.val', weSet(['E7Val02', 'E7Val3A'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet7.val', '[E7Val03,E7Val12,E7Val3B ]'); + WaitForModify; + t.Add('(after)', 'ModifyTestSet7.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet7.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet7.val', weSet(['E7Val03', 'E7Val12', 'E7Val3B'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestSet8.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestSet8.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestSet8.val', weSet(['E8Val02', 'E8Val59'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSet8.val', '[E8Val03,E8Val12,E8Val58 ]'); + WaitForModify; + t.Add('(after)', 'ModifyTestSet8.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSet8.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSet8.val', weSet(['E8Val03', 'E8Val12', 'E8Val58'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + + t.Add('(before)', 'ModifyTestSRangeSet.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(before)', 'ModifyTestSRangeSet.post', weCardinal($69, 'Byte', 1)); + t.Add('(before)', 'ModifyTestSRangeSet.val', weSet(['20','23','28'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSRangeSet.val', '[21,$18,27 ]'); + WaitForModify; + t.Add('(after)', 'ModifyTestSRangeSet.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSRangeSet.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSRangeSet.val', weSet(['21', '24', '27'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + Debugger.LazDebugger.Modify('ModifyTestSRangeSet.val', '[30 ]'); + WaitForModify; + t.Add('(after)', 'ModifyTestSRangeSet.pre', weCardinal(QWord($9696969696969696), 'QWord', 8)); + t.Add('(after)', 'ModifyTestSRangeSet.post', weCardinal($69, 'Byte', 1)); + t.Add('(after)', 'ModifyTestSRangeSet.val', weSet(['30'])); + t.EvaluateWatches; + t.CheckResults; + t.Clear; + + end; + + finally + t.Free; + Debugger.ClearDebuggerMonitors; + Debugger.FreeDebugger; + + AssertTestErrors; + end; +end; + procedure TTestWatches.TestWatchesErrors; var ExeName: String; t: TWatchExpectationList; Src: TCommonSource; - BrkPrg, BrkFoo, BrkFooVar, BrkFooConstRef: TDBGBreakPoint; + BrkPrg: TDBGBreakPoint; begin if SkipTest then exit; if not TestControlCanTest(ControlTestErrors) then exit; @@ -2479,6 +2930,7 @@ initialization ControlTestWatchFunct := TestControlRegisterTest('Function', ControlTestWatch); ControlTestWatchAddressOf := TestControlRegisterTest('AddressOf', ControlTestWatch); ControlTestWatchTypeCast := TestControlRegisterTest('TypeCast', ControlTestWatch); + ControlTestModify := TestControlRegisterTest('Modify', ControlTestWatch); ControlTestExpression := TestControlRegisterTest('Expression', ControlTestWatch); ControlTestErrors := TestControlRegisterTest('Errors', ControlTestWatch); diff --git a/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrg.pas b/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrg.pas index 50e8a7d455..7fb60ec65b 100644 --- a/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrg.pas +++ b/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrg.pas @@ -175,12 +175,63 @@ type TEnum = (EnVal1, EnVal2, EnVal3, EnVal4); + // TEnum16 more than 256 values => wordsized + TEnum16 = ( + ExVal00, ExVal01, ExVal02, ExVal03, ExVal04, ExVal05, ExVal06, ExVal07, ExVal08, ExVal09, ExVal0A, ExVal0B, ExVal0C, ExVal0D, ExVal0E, ExVal0F, + ExVal10, ExVal11, ExVal12, ExVal13, ExVal14, ExVal15, ExVal16, ExVal17, ExVal18, ExVal19, ExVal1A, ExVal1B, ExVal1C, ExVal1D, ExVal1E, ExVal1F, + ExVal20, ExVal21, ExVal22, ExVal23, ExVal24, ExVal25, ExVal26, ExVal27, ExVal28, ExVal29, ExVal2A, ExVal2B, ExVal2C, ExVal2D, ExVal2E, ExVal2F, + ExVal30, ExVal31, ExVal32, ExVal33, ExVal34, ExVal35, ExVal36, ExVal37, ExVal38, ExVal39, ExVal3A, ExVal3B, ExVal3C, ExVal3D, ExVal3E, ExVal3F, + ExVal40, ExVal41, ExVal42, ExVal43, ExVal44, ExVal45, ExVal46, ExVal47, ExVal48, ExVal49, ExVal4A, ExVal4B, ExVal4C, ExVal4D, ExVal4E, ExVal4F, + ExVal50, ExVal51, ExVal52, ExVal53, ExVal54, ExVal55, ExVal56, ExVal57, ExVal58, ExVal59, ExVal5A, ExVal5B, ExVal5C, ExVal5D, ExVal5E, ExVal5F, + ExVal60, ExVal61, ExVal62, ExVal63, ExVal64, ExVal65, ExVal66, ExVal67, ExVal68, ExVal69, ExVal6A, ExVal6B, ExVal6C, ExVal6D, ExVal6E, ExVal6F, + ExVal70, ExVal71, ExVal72, ExVal73, ExVal74, ExVal75, ExVal76, ExVal77, ExVal78, ExVal79, ExVal7A, ExVal7B, ExVal7C, ExVal7D, ExVal7E, ExVal7F, + ExVal80, ExVal81, ExVal82, ExVal83, ExVal84, ExVal85, ExVal86, ExVal87, ExVal88, ExVal89, ExVal8A, ExVal8B, ExVal8C, ExVal8D, ExVal8E, ExVal8F, + ExVal90, ExVal91, ExVal92, ExVal93, ExVal94, ExVal95, ExVal96, ExVal97, ExVal98, ExVal99, ExVal9A, ExVal9B, ExVal9C, ExVal9D, ExVal9E, ExVal9F, + ExValA0, ExValA1, ExValA2, ExValA3, ExValA4, ExValA5, ExValA6, ExValA7, ExValA8, ExValA9, ExValAA, ExValAB, ExValAC, ExValAD, ExValAE, ExValAF, + ExValB0, ExValB1, ExValB2, ExValB3, ExValB4, ExValB5, ExValB6, ExValB7, ExValB8, ExValB9, ExValBA, ExValBB, ExValBC, ExValBD, ExValBE, ExValBF, + ExValC0, ExValC1, ExValC2, ExValC3, ExValC4, ExValC5, ExValC6, ExValC7, ExValC8, ExValC9, ExValCA, ExValCB, ExValCC, ExValCD, ExValCE, ExValCF, + ExValD0, ExValD1, ExValD2, ExValD3, ExValD4, ExValD5, ExValD6, ExValD7, ExValD8, ExValD9, ExValDA, ExValDB, ExValDC, ExValDD, ExValDE, ExValDF, + ExValE0, ExValE1, ExValE2, ExValE3, ExValE4, ExValE5, ExValE6, ExValE7, ExValE8, ExValE9, ExValEA, ExValEB, ExValEC, ExValED, ExValEE, ExValEF, + ExValF0, ExValF1, ExValF2, ExValF3, ExValF4, ExValF5, ExValF6, ExValF7, ExValF8, ExValF9, ExValFA, ExValFB, ExValFC, ExValFD, ExValFE, ExValFF, + ExValX0, ExValX1, ExValX2, ExValX3, ExValX4, ExValX5, ExValX6, ExValX7, ExValX8, ExValX9, ExValXA, ExValXB, ExValXC, ExValXD, ExValXE, ExValXF + ); TEnumSub = EnVal1..EnVal2; TEnum2 = (EnVal21= 3, EnVal22=4, EnVal23=7, EnVal24=10, EnVal25=30); TEnum3 = (EnVal31, EnVal32); + TEnum4 = ( // 12 values for 16 bit set (leave some unused) + E4Val00, E4Val01, E4Val02, E4Val03, E4Val04, E4Val05, E4Val06, E4Val07, E4Val08, E4Val09, E4Val0A, E4Val0B + ); + TEnum5 = ( // 20 values for 24 bit set (leave some unused) + E5Val00, E5Val01, E5Val02, E5Val03, E5Val04, E5Val05, E5Val06, E5Val07, E5Val08, E5Val09, E5Val0A, E5Val0B, E5Val0C, E5Val0D, E5Val0E, E5Val0F, + E5Val10, E5Val11, E5Val12, E5Val13 + ); + TEnum6 = ( // 28 values for 32 bit set (leave some unused) + E6Val00, E6Val01, E6Val02, E6Val03, E6Val04, E6Val05, E6Val06, E6Val07, E6Val08, E6Val09, E6Val0A, E6Val0B, E6Val0C, E6Val0D, E6Val0E, E6Val0F, + E6Val10, E6Val11, E6Val12, E6Val13, E6Val14, E6Val15, E6Val16, E6Val17, E6Val18, E6Val19, E6Val1A, E6Val1B + ); + TEnum7 = ( // 60 values for 8 byte set (leave some unused) + E7Val00, E7Val01, E7Val02, E7Val03, E7Val04, E7Val05, E7Val06, E7Val07, E7Val08, E7Val09, E7Val0A, E7Val0B, E7Val0C, E7Val0D, E7Val0E, E7Val0F, + E7Val10, E7Val11, E7Val12, E7Val13, E7Val14, E7Val15, E7Val16, E7Val17, E7Val18, E7Val19, E7Val1A, E7Val1B, E7Val1C, E7Val1D, E7Val1E, E7Val1F, + E7Val20, E7Val21, E7Val22, E7Val23, E7Val24, E7Val25, E7Val26, E7Val27, E7Val28, E7Val29, E7Val2A, E7Val2B, E7Val2C, E7Val2D, E7Val2E, E7Val2F, + E7Val30, E7Val31, E7Val32, E7Val33, E7Val34, E7Val35, E7Val36, E7Val37, E7Val38, E7Val39, E7Val3A, E7Val3B + ); + TEnum8 = ( // 92 values for 10 byte set (leave some unused) + E8Val00, E8Val01, E8Val02, E8Val03, E8Val04, E8Val05, E8Val06, E8Val07, E8Val08, E8Val09, E8Val0A, E8Val0B, E8Val0C, E8Val0D, E8Val0E, E8Val0F, + E8Val10, E8Val11, E8Val12, E8Val13, E8Val14, E8Val15, E8Val16, E8Val17, E8Val18, E8Val19, E8Val1A, E8Val1B, E8Val1C, E8Val1D, E8Val1E, E8Val1F, + E8Val20, E8Val21, E8Val22, E8Val23, E8Val24, E8Val25, E8Val26, E8Val27, E8Val28, E8Val29, E8Val2A, E8Val2B, E8Val2C, E8Val2D, E8Val2E, E8Val2F, + E8Val30, E8Val31, E8Val32, E8Val33, E8Val34, E8Val35, E8Val36, E8Val37, E8Val38, E8Val39, E8Val3A, E8Val3B, E8Val3C, E8Val3D, E8Val3E, E8Val3F, + E8Val40, E8Val41, E8Val42, E8Val43, E8Val44, E8Val45, E8Val46, E8Val47, E8Val48, E8Val49, E8Val4A, E8Val4B, E8Val4C, E8Val4D, E8Val4E, E8Val4F, + E8Val50, E8Val51, E8Val52, E8Val53, E8Val54, E8Val55, E8Val56, E8Val57, E8Val58, E8Val59, E8Val5A, E8Val5B + ); + TSet = set of TEnum; TSet3 = set of TEnum3; TSmallRangeSet = set of TSmallRange; + TSet4 = set of TEnum4; // 2 byte + TSet5 = set of TEnum5; // 3 byte + TSet6 = set of TEnum6; // 4 byte + TSet7 = set of TEnum7; // 8 byte + TSet8 = set of TEnum8; // 10 byte TArrayEnum = array [TEnum] of word; TArrayEnumSub = array [TEnumSub] of word; @@ -428,7 +479,6 @@ begin end; function TMyBaseClass.SomeMeth1(SomeValue: Integer): Boolean; begin result := SomeValue = 0; end; - procedure Foo( (* LOCATION: param *) TEST_PREPOCESS(WatchesValuePrgIdent.inc, pre__=arg, _OP_=:, (=;//, _O2_=:, _EQ_= , _BLOCK_=TestArg) @@ -505,6 +555,109 @@ begin FFunctInt := result; end; +var + ModifyTestByte: record + pre: qword; // padding, must not be changed + val: byte; + post: byte; // padding, must not be changed + end; + ModifyTestWord: record + pre: qword; // padding, must not be changed + val: word; + post: byte; // padding, must not be changed + end; + ModifyTestCardinal: record + pre: qword; // padding, must not be changed + val: Cardinal; + post: byte; // padding, must not be changed + end; + ModifyTestqword: record + pre: qword; // padding, must not be changed + val: qword; + post: byte; // padding, must not be changed + end; + ModifyTestint: record + pre: qword; // padding, must not be changed + val: integer; + post: byte; // padding, must not be changed + end; + ModifyTestInt64: record + pre: qword; // padding, must not be changed + val: int64; + post: byte; // padding, must not be changed + end; + ModifyTestPointer: record + pre: qword; // padding, must not be changed + val: Pointer; + post: byte; // padding, must not be changed + end; + ModifyTestPWord: record + pre: qword; // padding, must not be changed + val: PWord; + post: byte; // padding, must not be changed + end; + ModifyTestBool: record + pre: qword; // padding, must not be changed + val: Boolean; + post: byte; // padding, must not be changed + end; + ModifyTestByteBool: record + pre: qword; // padding, must not be changed + val: ByteBool; + post: byte; // padding, must not be changed + end; + ModifyTestChar: record + pre: qword; // padding, must not be changed + val: Char; + post: byte; // padding, must not be changed + end; + ModifyTestWideChar: record + pre: qword; // padding, must not be changed + val: WideChar; + post: byte; // padding, must not be changed + end; + ModifyTestEnum: record + pre: qword; // padding, must not be changed + val: TEnum; + post: byte; // padding, must not be changed + end; + ModifyTestEnum16: record + pre: qword; // padding, must not be changed + val: TEnum16; + post: byte; // padding, must not be changed + end; + ModifyTestSet: record + pre: qword; // padding, must not be changed + val: TSet; + post: byte; // padding, must not be changed + end; + ModifyTestSet4: record + pre: qword; // padding, must not be changed + val: TSet4; + post: byte; // padding, must not be changed + end; + ModifyTestSet6: record + pre: qword; // padding, must not be changed + val: TSet6; + post: byte; // padding, must not be changed + end; + ModifyTestSet7: record + pre: qword; // padding, must not be changed + val: TSet7; + post: byte; // padding, must not be changed + end; + ModifyTestSet8: record + pre: qword; // padding, must not be changed + val: TSet8; + post: byte; // padding, must not be changed + end; + ModifyTestSRangeSet: record + pre: qword; // padding, must not be changed + val: TSmallRangeSet; + post: byte; // padding, must not be changed + end; + + begin U8Data1 := #$2267; //#$E2#$89#$A7; U8Data2 := #$2267'X'; @@ -527,6 +680,27 @@ begin dummy1 := nil; {$ENDIF} + with ModifyTestByte do begin pre := qword($9696969696969696); post := $69; val := $01; end; + with ModifyTestWord do begin pre := qword($9696969696969696); post := $69; val := $0101; end; + with ModifyTestCardinal do begin pre := qword($9696969696969696); post := $69; val := $81020102; end; + with ModifyTestQword do begin pre := qword($9696969696969696); post := $69; val := qword($8102010201020102); end; + with ModifyTestInt do begin pre := qword($9696969696969696); post := $69; val := -$01030103; end; + with ModifyTestInt64 do begin pre := qword($9696969696969696); post := $69; val := -$0103010301030103; end; + with ModifyTestPointer do begin pre := qword($9696969696969696); post := $69; val := pointer(30); end; + with ModifyTestPWord do begin pre := qword($9696969696969696); post := $69; val := pointer(40); end; + with ModifyTestBool do begin pre := qword($9696969696969696); post := $69; val := True; end; + with ModifyTestByteBool do begin pre := qword($9696969696969696); post := $69; val := False; end; + with ModifyTestChar do begin pre := qword($9696969696969696); post := $69; val := 'B'; end; + with ModifyTestWideChar do begin pre := qword($9696969696969696); post := $69; val := 'B'; end; + with ModifyTestEnum do begin pre := qword($9696969696969696); post := $69; val := EnVal2; end; + with ModifyTestEnum16 do begin pre := qword($9696969696969696); post := $69; val := ExValX2; end; + with ModifyTestSet do begin pre := qword($9696969696969696); post := $69; val := [EnVal2, EnVal4]; end; + with ModifyTestSet4 do begin pre := qword($9696969696969696); post := $69; val := [E4Val02, E4Val09]; end; + with ModifyTestSet6 do begin pre := qword($9696969696969696); post := $69; val := [E6Val02, E6Val1A]; end; + with ModifyTestSet7 do begin pre := qword($9696969696969696); post := $69; val := [E7Val02, E7Val3A]; end; + with ModifyTestSet8 do begin pre := qword($9696969696969696); post := $69; val := [E8Val02, E8Val59]; end; + with ModifyTestSRangeSet do begin pre := qword($9696969696969696); post := $69; val := [20,23,28]; end; + (* use global const / value in "gv" will be overriden... *) TEST_PREPOCESS(WatchesValuePrgIdent.inc,pre__=gv, {e}={, "//@@=} :=", _pre3_=gc, _BLOCK_=TestAssignGC) diff --git a/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrgIdent.inc b/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrgIdent.inc index 3d84249a05..9bf06ed4a4 100644 --- a/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrgIdent.inc +++ b/components/lazdebuggers/lazdebugtestbase/testapps/WatchesValuePrgIdent.inc @@ -30,6 +30,20 @@ pre__Bool1{e} _OP_ Boolean (False); //@@ _pre3_Bool1{e3}; pre__Bool2{e} _OP_ Boolean (True); //@@ _pre3_Bool2{e3}; + pre__WBool1{e} _OP_ Boolean16 (False); //@@ _pre3_WBool1{e3}; + pre__WBool2{e} _OP_ Boolean16 (True); //@@ _pre3_WBool2{e3}; + pre__LBool1{e} _OP_ Boolean32 (False); //@@ _pre3_LBool1{e3}; + pre__LBool2{e} _OP_ Boolean32 (True); //@@ _pre3_LBool2{e3}; + pre__QBool1{e} _OP_ Boolean64 (False); //@@ _pre3_QBool1{e3}; + pre__QBool2{e} _OP_ Boolean64 (True); //@@ _pre3_QBool2{e3}; + pre__ByteBool1{e} _OP_ ByteBool (False); //@@ _pre3_ByteBool1{e3}; + pre__ByteBool2{e} _OP_ ByteBool (True); //@@ _pre3_ByteBool2{e3}; + pre__WordBool1{e} _OP_ WordBool (False); //@@ _pre3_WordBool1{e3}; + pre__WordBool2{e} _OP_ WordBool (True); //@@ _pre3_WordBool2{e3}; + pre__LongBool1{e} _OP_ LongBool (False); //@@ _pre3_LongBool1{e3}; + pre__LongBool2{e} _OP_ LongBool (True); //@@ _pre3_LongBool2{e3}; + pre__QWordBool1{e} _OP_ QWordBool (False); //@@ _pre3_QWordBool1{e3}; + pre__QWordBool2{e} _OP_ QWordBool (True); //@@ _pre3_QWordBool2{e3}; pre__Real{e} _OP_ Real (50.25 + ADD); //@@ _pre3_Real{e3}; pre__Single{e} _OP_ Single (100.125 + ADD); //@@ _pre3_Single{e3}; @@ -373,9 +387,17 @@ pre__Enum2{e} _OP_ TEnum2(EnVal21); //@@ _pre3_Enum2{e3}; pre__Enum3{e} _OP_ TEnum2(EnVal25); //@@ _pre3_Enum3{e3}; + pre__Enum16{e} _OP_ TEnum16(ExVal23); //@@ _pre3_Enum16{e3}; + pre__Enum16A{e} _OP_ TEnum16(ExValX5); //@@ _pre3_Enum16A{e3}; + pre__Set{e} _OP_ TSet([EnVal2, EnVal4]); //@@ _pre3_Set{e3}; pre__SmallSet{e} _OP_ TSmallRangeSet([22, 24,25]); //@@ _pre3_SmallSet{e3}; + pre__Set4{e} _OP_ TSet4([E4Val02, E4Val0A]); //@@ _pre3_Set4{e3}; + pre__Set5{e} _OP_ TSet5([E5Val02, E5Val12]); //@@ _pre3_Set5{e3}; + pre__Set6{e} _OP_ TSet6([E6Val02, E6Val1A]); //@@ _pre3_Set6{e3}; + pre__Set7{e} _OP_ TSet7([E7Val02, E7Val3A]); //@@ _pre3_Set7{e3}; + pre__Set8{e} _OP_ TSet8([E8Val02, E8Val5B]); //@@ _pre3_Set8{e3}; {$IFnDEF TestAssign} pre__BitPackBoolArray{e} _O2_ TBitPackBoolArray _EQ_ (True, False, True, True); //@@ _pre3_BitPackBoolArray{e3}; // }} diff --git a/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas b/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas index f3f2f8cc50..2db4e85b68 100644 --- a/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas +++ b/components/lazdebuggers/lazdebugtestbase/ttestwatchutilities.pas @@ -13,7 +13,7 @@ uses type TWatchExpectationResultKind = ( - rkMatch, rkInteger, rkCardinal, rkFloat, rkBool, rkEnum, rkSet, + rkMatch, rkInteger, rkCardinal, rkFloat, rkBool, rkSizedBool, rkEnum, rkSet, rkChar, rkAnsiString, rkShortString, rkWideString, rkPointer, rkPointerAddr, rkClass, rkObject, rkRecord, rkInterface, rkField, rkStatArray, rkDynArray @@ -106,7 +106,7 @@ type rkFloat: ( ExpFloatValue: Extended; ); - rkBool: ( + rkBool, rkSizedBool: ( ExpBoolValue: Boolean; ); rkPointerAddr: ( @@ -301,6 +301,7 @@ function weDouble(AExpVal: Extended; ATypeName: String=#1): TWatchExpectationRes function weFloat(AExpVal: Extended; ATypeName: String=''): TWatchExpectationResult; function weBool(AExpVal: Boolean; ATypeName: String=#1): TWatchExpectationResult; +function weSizedBool(AExpVal: Boolean; ATypeName: String=''): TWatchExpectationResult; // Display as True(255) etc function weEnum(AExpVal: string; ATypeName: String=#1): TWatchExpectationResult; function weSet(const AExpVal: Array of string; ATypeName: String=#1): TWatchExpectationResult; @@ -478,6 +479,16 @@ begin Result.ExpBoolValue := AExpVal; end; +function weSizedBool(AExpVal: Boolean; ATypeName: String + ): TWatchExpectationResult; +begin + Result := Default(TWatchExpectationResult); + Result.ExpResultKind := rkSizedBool; + Result.ExpSymKind := skBoolean; + Result.ExpTypeName := ATypeName; + Result.ExpBoolValue := AExpVal; +end; + function weEnum(AExpVal: string; ATypeName: String): TWatchExpectationResult; begin Result := Default(TWatchExpectationResult); @@ -1353,7 +1364,7 @@ begin rkMatch: Result := CheckResultMatch(AContext, AnIgnoreRsn); rkInteger: Result := CheckResultNum(AContext, False, AnIgnoreRsn); rkCardinal: Result := CheckResultNum(AContext, True, AnIgnoreRsn); - rkBool: Result := CheckResultBool(AContext, AnIgnoreRsn); + rkBool, rkSizedBool: Result := CheckResultBool(AContext, AnIgnoreRsn); rkFloat: Result := CheckResultFloat(AContext, AnIgnoreRsn); rkEnum: Result := CheckResultEnum(AContext, AnIgnoreRsn); rkSet: Result := CheckResultSet(AContext, AnIgnoreRsn); @@ -1526,14 +1537,21 @@ function TWatchExpectationList.CheckResultBool( AContext: TWatchExpTestCurrentData; AnIgnoreRsn: String): Boolean; var Expect: TWatchExpectationResult; - s: String; + s, v: String; + i: SizeInt; begin with AContext.WatchExp do begin Result := True; Expect := AContext.Expectation; WriteStr(s, Expect.ExpBoolValue); - Result := TestEquals('Data', s, AContext.WatchVal.Value, False, AContext, AnIgnoreRsn); + v := AContext.WatchVal.Value; + if AContext.Expectation.ExpResultKind = rkSizedBool then begin + i := pos('(', v); + if i > 1 then + delete(v, i, 99); // remove the int value in brackets + end; + Result := TestEquals('Data', s, v, False, AContext, AnIgnoreRsn); end; end;