mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-09-02 13:20:26 +02:00
FpDebug: Refactor ReadBounds
git-svn-id: trunk@61800 -
This commit is contained in:
parent
767d2014d8
commit
14ef5eb9fc
@ -120,6 +120,8 @@ type
|
|||||||
TFpSymbolDwarfDataClass = class of TFpSymbolDwarfData;
|
TFpSymbolDwarfDataClass = class of TFpSymbolDwarfData;
|
||||||
TFpSymbolDwarfTypeClass = class of TFpSymbolDwarfType;
|
TFpSymbolDwarfTypeClass = class of TFpSymbolDwarfType;
|
||||||
|
|
||||||
|
PFpSymbolDwarfData = ^TFpSymbolDwarfData;
|
||||||
|
|
||||||
{%region Value objects }
|
{%region Value objects }
|
||||||
|
|
||||||
{ TFpValueDwarfBase }
|
{ TFpValueDwarfBase }
|
||||||
@ -442,7 +444,11 @@ type
|
|||||||
end;
|
end;
|
||||||
PInitLocParserData = ^TInitLocParserData;
|
PInitLocParserData = ^TInitLocParserData;
|
||||||
|
|
||||||
{ TDbgDwarfIdentifier }
|
(* TFpDwarfAtEntryDataReadState
|
||||||
|
Since Dwarf-3 several DW_AT_* can be const, expression or reference.
|
||||||
|
*)
|
||||||
|
TFpDwarfAtEntryDataReadState = (rfNotRead, rfNotFound, rfError, rfConst, rfValue, rfExpression);
|
||||||
|
PFpDwarfAtEntryDataReadState = ^TFpDwarfAtEntryDataReadState;
|
||||||
|
|
||||||
{ TFpSymbolDwarf }
|
{ TFpSymbolDwarf }
|
||||||
|
|
||||||
@ -479,7 +485,9 @@ type
|
|||||||
function ComputeDataMemberAddress(const AnInformationEntry: TDwarfInformationEntry;
|
function ComputeDataMemberAddress(const AnInformationEntry: TDwarfInformationEntry;
|
||||||
AValueObj: TFpValueDwarf; var AnAddress: TFpDbgMemLocation): Boolean; inline;
|
AValueObj: TFpValueDwarf; var AnAddress: TFpDbgMemLocation): Boolean; inline;
|
||||||
function ConstRefOrExprFromAttrData(const AnAttribData: TDwarfAttribData;
|
function ConstRefOrExprFromAttrData(const AnAttribData: TDwarfAttribData;
|
||||||
AValueObj: TFpValueDwarf; out AValue: TDBGPtr): Boolean;
|
AValueObj: TFpValueDwarf; out AValue: TDBGPtr;
|
||||||
|
AReadState: PFpDwarfAtEntryDataReadState = nil;
|
||||||
|
ADataSymbol: PFpSymbolDwarfData = nil): Boolean;
|
||||||
function LocationFromAttrData(const AnAttribData: TDwarfAttribData; AValueObj: TFpValueDwarf;
|
function LocationFromAttrData(const AnAttribData: TDwarfAttribData; AValueObj: TFpValueDwarf;
|
||||||
var AnAddress: TFpDbgMemLocation; // kept, if tag does not exist
|
var AnAddress: TFpDbgMemLocation; // kept, if tag does not exist
|
||||||
AnInitLocParserData: PInitLocParserData = nil;
|
AnInitLocParserData: PInitLocParserData = nil;
|
||||||
@ -662,7 +670,6 @@ DECL = DW_AT_decl_column, DW_AT_decl_file, DW_AT_decl_line
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
{ TFpSymbolDwarfTypeSubRange }
|
{ TFpSymbolDwarfTypeSubRange }
|
||||||
TFpDwarfSubRangeBoundReadState = (rfNotRead, rfNotFound, rfError, rfConst, rfValue);
|
|
||||||
|
|
||||||
TFpSymbolDwarfTypeSubRange = class(TFpSymbolDwarfTypeModifier)
|
TFpSymbolDwarfTypeSubRange = class(TFpSymbolDwarfTypeModifier)
|
||||||
// TODO not a modifier, maybe have a forwarder base class
|
// TODO not a modifier, maybe have a forwarder base class
|
||||||
@ -670,18 +677,16 @@ DECL = DW_AT_decl_column, DW_AT_decl_file, DW_AT_decl_line
|
|||||||
private
|
private
|
||||||
FLowBoundConst: Int64;
|
FLowBoundConst: Int64;
|
||||||
FLowBoundValue: TFpValue;
|
FLowBoundValue: TFpValue;
|
||||||
FLowBoundState: TFpDwarfSubRangeBoundReadState;
|
FLowBoundState: TFpDwarfAtEntryDataReadState;
|
||||||
FHighBoundConst: Int64;
|
FHighBoundConst: Int64;
|
||||||
FHighBoundValue: TFpValue;
|
FHighBoundValue: TFpValue;
|
||||||
FHighBoundState: TFpDwarfSubRangeBoundReadState;
|
FHighBoundState: TFpDwarfAtEntryDataReadState;
|
||||||
FCountConst: Int64;
|
FCountConst: Int64;
|
||||||
FCountValue: TFpValue;
|
FCountValue: TFpValue;
|
||||||
FCountState: TFpDwarfSubRangeBoundReadState;
|
FCountState: TFpDwarfAtEntryDataReadState;
|
||||||
FLowEnumIdx, FHighEnumIdx: Integer;
|
FLowEnumIdx, FHighEnumIdx: Integer;
|
||||||
FEnumIdxValid: Boolean;
|
FEnumIdxValid: Boolean;
|
||||||
procedure InitEnumIdx;
|
procedure InitEnumIdx;
|
||||||
procedure ReadBound(AValueObj: TFpValueDwarf; AnAttrib: Cardinal;
|
|
||||||
var ABoundConst: Int64; var ABoundValue: TFpValue; var ABoundState: TFpDwarfSubRangeBoundReadState);
|
|
||||||
protected
|
protected
|
||||||
function DoGetNestedTypeInfo: TFpSymbolDwarfType;override;
|
function DoGetNestedTypeInfo: TFpSymbolDwarfType;override;
|
||||||
function GetHasBounds: Boolean; override;
|
function GetHasBounds: Boolean; override;
|
||||||
@ -700,9 +705,9 @@ DECL = DW_AT_decl_column, DW_AT_decl_file, DW_AT_decl_line
|
|||||||
function GetValueBounds(AValueObj: TFpValue; out ALowBound, AHighBound: Int64): Boolean; override;
|
function GetValueBounds(AValueObj: TFpValue; out ALowBound, AHighBound: Int64): Boolean; override;
|
||||||
function GetValueLowBound(AValueObj: TFpValue; out ALowBound: Int64): Boolean; override;
|
function GetValueLowBound(AValueObj: TFpValue; out ALowBound: Int64): Boolean; override;
|
||||||
function GetValueHighBound(AValueObj: TFpValue; out AHighBound: Int64): Boolean; override;
|
function GetValueHighBound(AValueObj: TFpValue; out AHighBound: Int64): Boolean; override;
|
||||||
property LowBoundState: TFpDwarfSubRangeBoundReadState read FLowBoundState; deprecated;
|
property LowBoundState: TFpDwarfAtEntryDataReadState read FLowBoundState; deprecated;
|
||||||
property HighBoundState: TFpDwarfSubRangeBoundReadState read FHighBoundState; deprecated;
|
property HighBoundState: TFpDwarfAtEntryDataReadState read FHighBoundState; deprecated;
|
||||||
property CountState: TFpDwarfSubRangeBoundReadState read FCountState; deprecated;
|
property CountState: TFpDwarfAtEntryDataReadState read FCountState; deprecated;
|
||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -966,14 +971,14 @@ DECL = DW_AT_decl_column, DW_AT_decl_file, DW_AT_decl_line
|
|||||||
end;
|
end;
|
||||||
{%endregion Symbol objects }
|
{%endregion Symbol objects }
|
||||||
|
|
||||||
function dbgs(ASubRangeBoundReadState: TFpDwarfSubRangeBoundReadState): String; overload;
|
function dbgs(ASubRangeBoundReadState: TFpDwarfAtEntryDataReadState): String; overload;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
var
|
var
|
||||||
DBG_WARNINGS, FPDBG_DWARF_VERBOSE, FPDBG_DWARF_ERRORS, FPDBG_DWARF_WARNINGS, FPDBG_DWARF_SEARCH, FPDBG_DWARF_DATA_WARNINGS: PLazLoggerLogGroup;
|
DBG_WARNINGS, FPDBG_DWARF_VERBOSE, FPDBG_DWARF_ERRORS, FPDBG_DWARF_WARNINGS, FPDBG_DWARF_SEARCH, FPDBG_DWARF_DATA_WARNINGS: PLazLoggerLogGroup;
|
||||||
|
|
||||||
function dbgs(ASubRangeBoundReadState: TFpDwarfSubRangeBoundReadState): String;
|
function dbgs(ASubRangeBoundReadState: TFpDwarfAtEntryDataReadState): String;
|
||||||
begin
|
begin
|
||||||
WriteStr(Result, ASubRangeBoundReadState);
|
WriteStr(Result, ASubRangeBoundReadState);
|
||||||
end;
|
end;
|
||||||
@ -2977,7 +2982,8 @@ end;
|
|||||||
|
|
||||||
function TFpSymbolDwarf.ConstRefOrExprFromAttrData(
|
function TFpSymbolDwarf.ConstRefOrExprFromAttrData(
|
||||||
const AnAttribData: TDwarfAttribData; AValueObj: TFpValueDwarf; out
|
const AnAttribData: TDwarfAttribData; AValueObj: TFpValueDwarf; out
|
||||||
AValue: TDBGPtr): Boolean;
|
AValue: TDBGPtr; AReadState: PFpDwarfAtEntryDataReadState;
|
||||||
|
ADataSymbol: PFpSymbolDwarfData): Boolean;
|
||||||
var
|
var
|
||||||
Form: Cardinal;
|
Form: Cardinal;
|
||||||
FwdInfoPtr: Pointer;
|
FwdInfoPtr: Pointer;
|
||||||
@ -2993,6 +2999,9 @@ begin
|
|||||||
if Form in [DW_FORM_data1, DW_FORM_data2, DW_FORM_data4, DW_FORM_data8,
|
if Form in [DW_FORM_data1, DW_FORM_data2, DW_FORM_data4, DW_FORM_data8,
|
||||||
DW_FORM_sdata, DW_FORM_udata]
|
DW_FORM_sdata, DW_FORM_udata]
|
||||||
then begin
|
then begin
|
||||||
|
if AReadState <> nil then
|
||||||
|
AReadState^ := rfConst;
|
||||||
|
|
||||||
Result := InformationEntry.ReadValue(AnAttribData, AValue);
|
Result := InformationEntry.ReadValue(AnAttribData, AValue);
|
||||||
if not Result then
|
if not Result then
|
||||||
SetLastError(CreateError(fpErrAnyError));
|
SetLastError(CreateError(fpErrAnyError));
|
||||||
@ -3002,6 +3011,9 @@ begin
|
|||||||
if Form in [DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4, DW_FORM_ref8,
|
if Form in [DW_FORM_ref1, DW_FORM_ref2, DW_FORM_ref4, DW_FORM_ref8,
|
||||||
DW_FORM_ref_addr, DW_FORM_ref_udata]
|
DW_FORM_ref_addr, DW_FORM_ref_udata]
|
||||||
then begin
|
then begin
|
||||||
|
if AReadState <> nil then
|
||||||
|
AReadState^ := rfValue;
|
||||||
|
|
||||||
Result := InformationEntry.ReadReference(AnAttribData, FwdInfoPtr, FwdCompUint);
|
Result := InformationEntry.ReadReference(AnAttribData, FwdInfoPtr, FwdCompUint);
|
||||||
if Result then begin
|
if Result then begin
|
||||||
NewInfo := TDwarfInformationEntry.Create(FwdCompUint, FwdInfoPtr);
|
NewInfo := TDwarfInformationEntry.Create(FwdCompUint, FwdInfoPtr);
|
||||||
@ -3012,7 +3024,10 @@ begin
|
|||||||
AValue := RefSymbol.Value.AsInteger;
|
AValue := RefSymbol.Value.AsInteger;
|
||||||
Result := not IsError(RefSymbol.LastError);
|
Result := not IsError(RefSymbol.LastError);
|
||||||
// TODO: copy the error
|
// TODO: copy the error
|
||||||
RefSymbol.ReleaseReference;
|
if ADataSymbol <> nil then
|
||||||
|
ADataSymbol^ := RefSymbol
|
||||||
|
else
|
||||||
|
RefSymbol.ReleaseReference;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
if not Result then
|
if not Result then
|
||||||
@ -3022,6 +3037,16 @@ begin
|
|||||||
else
|
else
|
||||||
if Form in [DW_FORM_block, DW_FORM_block1, DW_FORM_block2, DW_FORM_block4]
|
if Form in [DW_FORM_block, DW_FORM_block1, DW_FORM_block2, DW_FORM_block4]
|
||||||
then begin
|
then begin
|
||||||
|
// TODO: until there always will be an AValueObj
|
||||||
|
if AValueObj = nil then begin
|
||||||
|
if AReadState <> nil then
|
||||||
|
AReadState^ := rfNotRead;
|
||||||
|
exit(true); // claim success
|
||||||
|
end;
|
||||||
|
|
||||||
|
if AReadState <> nil then
|
||||||
|
AReadState^ := rfExpression;
|
||||||
|
|
||||||
InitLocParserData.ObjectDataAddress := AValueObj.Address;
|
InitLocParserData.ObjectDataAddress := AValueObj.Address;
|
||||||
if not IsValidLoc(InitLocParserData.ObjectDataAddress) then
|
if not IsValidLoc(InitLocParserData.ObjectDataAddress) then
|
||||||
InitLocParserData.ObjectDataAddress := AValueObj.OrdOrAddress;
|
InitLocParserData.ObjectDataAddress := AValueObj.OrdOrAddress;
|
||||||
@ -3036,6 +3061,9 @@ begin
|
|||||||
else begin
|
else begin
|
||||||
SetLastError(CreateError(fpErrAnyError));
|
SetLastError(CreateError(fpErrAnyError));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
if (not Result) and (AReadState <> nil) then
|
||||||
|
AReadState^ := rfError;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TFpSymbolDwarf.LocationFromAttrData(
|
function TFpSymbolDwarf.LocationFromAttrData(
|
||||||
@ -3649,66 +3677,6 @@ begin
|
|||||||
FLowEnumIdx := i + 1;
|
FLowEnumIdx := i + 1;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TFpSymbolDwarfTypeSubRange.ReadBound(AValueObj: TFpValueDwarf;
|
|
||||||
AnAttrib: Cardinal; var ABoundConst: Int64; var ABoundValue: TFpValue;
|
|
||||||
var ABoundState: TFpDwarfSubRangeBoundReadState);
|
|
||||||
var
|
|
||||||
FwdInfoPtr: Pointer;
|
|
||||||
FwdCompUint: TDwarfCompilationUnit;
|
|
||||||
NewInfo: TDwarfInformationEntry;
|
|
||||||
AnAddress: TFpDbgMemLocation;
|
|
||||||
InitLocParserData: TInitLocParserData;
|
|
||||||
AttrData: TDwarfAttribData;
|
|
||||||
BoundSymbol: TFpSymbolDwarfData;
|
|
||||||
begin
|
|
||||||
// TODO: assert(AValueObj <> nil, 'TFpSymbolDwarfTypeSubRange.ReadBound: AValueObj <> nil');
|
|
||||||
if ABoundState <> rfNotRead then exit;
|
|
||||||
|
|
||||||
if InformationEntry.GetAttribData(AnAttrib, AttrData) then begin
|
|
||||||
// TODO: check the FORM, to determine what data to read.
|
|
||||||
if InformationEntry.ReadReference(AttrData, FwdInfoPtr, FwdCompUint) then begin
|
|
||||||
NewInfo := TDwarfInformationEntry.Create(FwdCompUint, FwdInfoPtr);
|
|
||||||
BoundSymbol := TFpSymbolDwarfData.CreateValueSubClass('', NewInfo);
|
|
||||||
NewInfo.ReleaseReference;
|
|
||||||
ABoundValue := nil;
|
|
||||||
if BoundSymbol <> nil then begin
|
|
||||||
ABoundValue := BoundSymbol.Value;
|
|
||||||
ABoundValue.AddReference;
|
|
||||||
end;
|
|
||||||
if (ABoundValue = nil) or not (svfInteger in ABoundValue.FieldFlags) then begin
|
|
||||||
// Not implemented in DwarfSymbolClassMap.GetDwarfSymbolClass // or not a value type (bound can not be a type)
|
|
||||||
ABoundState := rfError;
|
|
||||||
exit;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ABoundState := rfValue;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if InformationEntry.ReadValue(AttrData, ABoundConst) then begin
|
|
||||||
ABoundState := rfConst;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
begin
|
|
||||||
if assigned(AValueObj) then begin
|
|
||||||
InitLocParserData.ObjectDataAddress := AValueObj.Address;
|
|
||||||
if not IsValidLoc(InitLocParserData.ObjectDataAddress) then
|
|
||||||
InitLocParserData.ObjectDataAddress := AValueObj.OrdOrAddress;
|
|
||||||
InitLocParserData.ObjectDataAddrPush := False;
|
|
||||||
if LocationFromAttrData(AttrData, AValueObj, AnAddress, @InitLocParserData) then begin
|
|
||||||
ABoundState := rfConst;
|
|
||||||
ABoundConst := Int64(AnAddress.Address);
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ABoundState := rfError;
|
|
||||||
end
|
|
||||||
else // TODO: check AttrData=>FORM is correct for LocationFromAttrData
|
|
||||||
ABoundState := rfNotRead; // There is a bound, but it can not be read yet
|
|
||||||
end;
|
|
||||||
end
|
|
||||||
else
|
|
||||||
ABoundState := rfNotFound;
|
|
||||||
end;
|
|
||||||
|
|
||||||
function TFpSymbolDwarfTypeSubRange.DoGetNestedTypeInfo: TFpSymbolDwarfType;
|
function TFpSymbolDwarfTypeSubRange.DoGetNestedTypeInfo: TFpSymbolDwarfType;
|
||||||
begin
|
begin
|
||||||
Result := inherited DoGetNestedTypeInfo;
|
Result := inherited DoGetNestedTypeInfo;
|
||||||
@ -3837,42 +3805,57 @@ end;
|
|||||||
|
|
||||||
function TFpSymbolDwarfTypeSubRange.GetValueLowBound(AValueObj: TFpValue;
|
function TFpSymbolDwarfTypeSubRange.GetValueLowBound(AValueObj: TFpValue;
|
||||||
out ALowBound: Int64): Boolean;
|
out ALowBound: Int64): Boolean;
|
||||||
|
var
|
||||||
|
AttrData: TDwarfAttribData;
|
||||||
|
t: TDBGPtr;
|
||||||
begin
|
begin
|
||||||
assert((AValueObj = nil) or (AValueObj is TFpValueDwarf), 'TFpSymbolDwarfTypeSubRange.GetValueLowBound: AValueObj is TFpValueDwarf(');
|
assert((AValueObj = nil) or (AValueObj is TFpValueDwarf), 'TFpSymbolDwarfTypeSubRange.GetValueLowBound: AValueObj is TFpValueDwarf(');
|
||||||
if FLowBoundState = rfNotRead then
|
if FLowBoundState = rfNotRead then begin
|
||||||
ReadBound(TFpValueDwarf(AValueObj), DW_AT_lower_bound, FLowBoundConst, FLowBoundValue, FLowBoundState);
|
if InformationEntry.GetAttribData(DW_AT_lower_bound, AttrData) then
|
||||||
Result := True;
|
ConstRefOrExprFromAttrData(AttrData, TFpValueDwarf(AValueObj), t, @FLowBoundState, @FLowBoundValue)
|
||||||
case FLowBoundState of
|
else
|
||||||
rfConst: ALowBound := FLowBoundConst;
|
FLowBoundState := rfNotFound;
|
||||||
rfValue: ALowBound := FLowBoundValue.AsInteger;
|
FLowBoundConst := int64(t);
|
||||||
else Result := False;
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Result := FLowBoundState in [rfConst, rfValue, rfExpression];
|
||||||
|
ALowBound := FLowBoundConst;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TFpSymbolDwarfTypeSubRange.GetValueHighBound(AValueObj: TFpValue;
|
function TFpSymbolDwarfTypeSubRange.GetValueHighBound(AValueObj: TFpValue;
|
||||||
out AHighBound: Int64): Boolean;
|
out AHighBound: Int64): Boolean;
|
||||||
|
var
|
||||||
|
AttrData: TDwarfAttribData;
|
||||||
|
t: TDBGPtr;
|
||||||
begin
|
begin
|
||||||
assert((AValueObj = nil) or (AValueObj is TFpValueDwarf), 'TFpSymbolDwarfTypeSubRange.GetValueHighBound: AValueObj is TFpValueDwarf(');
|
assert((AValueObj = nil) or (AValueObj is TFpValueDwarf), 'TFpSymbolDwarfTypeSubRange.GetValueHighBound: AValueObj is TFpValueDwarf(');
|
||||||
if FHighBoundState = rfNotRead then
|
if FHighBoundState = rfNotRead then begin
|
||||||
ReadBound(TFpValueDwarf(AValueObj), DW_AT_upper_bound, FHighBoundConst, FHighBoundValue, FHighBoundState);
|
if InformationEntry.GetAttribData(DW_AT_upper_bound, AttrData) then
|
||||||
|
ConstRefOrExprFromAttrData(AttrData, TFpValueDwarf(AValueObj), t, @FHighBoundState, @FHighBoundValue)
|
||||||
|
else
|
||||||
|
FHighBoundState := rfNotFound;
|
||||||
|
FHighBoundConst := int64(t);
|
||||||
|
end;
|
||||||
|
|
||||||
Result := True;
|
Result := FHighBoundState in [rfConst, rfValue, rfExpression];
|
||||||
case FHighBoundState of
|
AHighBound := FHighBoundConst;
|
||||||
rfConst: AHighBound := FHighBoundConst;
|
|
||||||
rfValue: AHighBound := FHighBoundValue.AsInteger;
|
if FHighBoundState = rfNotFound then begin
|
||||||
rfNotFound: begin
|
Result := GetValueLowBound(AValueObj, AHighBound);
|
||||||
Result := GetValueLowBound(AValueObj, AHighBound);
|
if Result then begin
|
||||||
if not Result then
|
if FCountState = rfNotRead then begin
|
||||||
exit;
|
if InformationEntry.GetAttribData(DW_AT_upper_bound, AttrData) then
|
||||||
if FCountState = rfNotRead then
|
ConstRefOrExprFromAttrData(AttrData, TFpValueDwarf(AValueObj), t, @FCountState, @FCountValue)
|
||||||
ReadBound(TFpValueDwarf(AValueObj), DW_AT_count, FCountConst, FCountValue, FCountState);
|
else
|
||||||
case FCountState of
|
FCountState := rfNotFound;
|
||||||
rfConst: AHighBound := AHighBound + FCountConst;
|
FCountConst := int64(t);
|
||||||
rfValue: AHighBound := AHighBound + FCountValue.AsInteger;
|
|
||||||
else Result := False;
|
|
||||||
end;
|
|
||||||
end;
|
end;
|
||||||
else Result := False;
|
|
||||||
|
Result := FCountState in [rfConst, rfValue, rfExpression];
|
||||||
|
{$PUSH}{$R-}{$Q-}
|
||||||
|
AHighBound := AHighBound + FCountConst;
|
||||||
|
{$POP}
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user