mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 16:41:48 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			569 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			569 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| unit DbgIntfMiscClasses;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
|   Classes, SysUtils, LazClasses;
 | |
| 
 | |
| type
 | |
| 
 | |
|   (* TDbgEntityValue are created with a refcount of 0 (zero)
 | |
|   *)
 | |
| 
 | |
|   TDbgEntityValuesList = class;
 | |
|   TDbgEntitiesThreadStackList = class;
 | |
| 
 | |
|   { TDbgEntityValue
 | |
|     Locals, Watches, Registers
 | |
|   }
 | |
| 
 | |
|   TDbgEntityValue = class(TRefCountedObject)
 | |
|   private
 | |
|     FOwner: TDbgEntityValuesList;
 | |
|     FFlags: set of (devImmutable);
 | |
|     function GetImmutable: Boolean;
 | |
|     function GetStackFrame: Integer;
 | |
|     function GetThreadId: Integer;
 | |
|     procedure SetImmutable(AValue: Boolean);
 | |
|   protected
 | |
|     procedure DoAssign({%H-}AnOther: TDbgEntityValue); virtual;
 | |
|     property Owner: TDbgEntityValuesList read FOwner;
 | |
|   public
 | |
|     procedure Assign({%H-}AnOther: TDbgEntityValue);
 | |
|     property ThreadId: Integer read GetThreadId;
 | |
|     property StackFrame: Integer read GetStackFrame;
 | |
|     property Immutable: Boolean read GetImmutable write SetImmutable; // mainly used by assert
 | |
|   end;
 | |
| 
 | |
|   { TDbgEntityValuesList
 | |
|     All Values for a specifer Thread/StackFrame
 | |
|   }
 | |
| 
 | |
|   TDbgEntityValuesList = class(TRefCountedObject)
 | |
|   private
 | |
|     FStackFrame: Integer;
 | |
|     FThreadId: Integer;
 | |
|     FFlags: set of (devlImmutable);
 | |
|     FList: TRefCntObjList;
 | |
|     FOwner: TDbgEntitiesThreadStackList;
 | |
|     function GetEntry(AnIndex: Integer): TDbgEntityValue;
 | |
|     function GetImmutable: Boolean;
 | |
|     procedure SetImmutable(AValue: Boolean);
 | |
|   protected
 | |
|     function  CreateEntry: TDbgEntityValue; virtual; abstract;
 | |
|     procedure DoAssign(AnOther: TDbgEntityValuesList); virtual;      // assert other has same thread/stack
 | |
|     procedure DoAssignListContent(AnOther: TDbgEntityValuesList); virtual;      // assert other has same thread/stack
 | |
|     procedure DoCleared; virtual;
 | |
|     procedure DoAdded({%H-}AnEntry: TDbgEntityValue); virtual;
 | |
|     procedure Init; virtual;
 | |
|     property Owner: TDbgEntitiesThreadStackList read FOwner;
 | |
|   public
 | |
|     constructor Create(AThreadId, AStackFrame: Integer);
 | |
|     destructor Destroy; override;
 | |
|     procedure Assign(AnOther: TDbgEntityValuesList);       // assert other has same thread/stack
 | |
|     procedure Add(AnEntry: TDbgEntityValue);
 | |
|     procedure Clear;
 | |
|     function Count: Integer;
 | |
|     property Entries[AnIndex: Integer]: TDbgEntityValue read GetEntry;
 | |
| 
 | |
|     property ThreadId: Integer read FThreadId;
 | |
|     property StackFrame: Integer read FStackFrame;
 | |
|     property Immutable: Boolean read GetImmutable write SetImmutable; // mainly used by assert
 | |
|   end;
 | |
| 
 | |
|   TDbgValuesThreadList = record
 | |
|     ThreadId: Integer;
 | |
|     List: TRefCntObjList;
 | |
|   end;
 | |
| 
 | |
|   { TDbgEntitiesThreadStackList }
 | |
| 
 | |
|   TDbgEntitiesThreadStackList = class(TRefCountedObject)
 | |
|   private
 | |
|     FList: array of TDbgValuesThreadList;
 | |
|     FFlags: set of (devtsImmutable);
 | |
|     function GetEntry(AThreadId, AStackFrame: Integer): TDbgEntityValuesList;
 | |
|     function GetEntryByIdx(AnIndex: Integer): TDbgEntityValuesList;
 | |
|     function GetHasEntry(AThreadId, AStackFrame: Integer): Boolean;
 | |
|     function GetImmutable: Boolean;
 | |
|     function IndexOfThread(AThreadId: Integer; ACreateSubList: Boolean = False): Integer;
 | |
|     procedure SetImmutable(AValue: Boolean);
 | |
|   protected
 | |
|     function  CreateEntry(AThreadId, AStackFrame: Integer): TDbgEntityValuesList; virtual; abstract;
 | |
|     procedure DoAssign(AnOther: TDbgEntitiesThreadStackList); virtual;
 | |
|     procedure DoAssignListContent(AnOther: TDbgEntitiesThreadStackList); virtual;
 | |
|     procedure DoCleared; virtual;
 | |
|     procedure DoAdded({%H-}AnEntry: TDbgEntityValuesList); virtual;
 | |
|   public
 | |
|     destructor Destroy; override;
 | |
|     procedure Assign(AnOther: TDbgEntitiesThreadStackList);
 | |
|     procedure Add(AnEntry: TDbgEntityValuesList);
 | |
|     procedure Clear;
 | |
|     function Count: Integer;
 | |
|     property EntriesByIdx[AnIndex: Integer]: TDbgEntityValuesList read GetEntryByIdx;
 | |
|     // Entries will automatically be created
 | |
|     property Entries[AThreadId, AStackFrame: Integer]: TDbgEntityValuesList read GetEntry; default;
 | |
|     property HasEntry[AThreadId, AStackFrame: Integer]: Boolean read GetHasEntry;
 | |
|     property Immutable: Boolean read GetImmutable write SetImmutable; // used by assert
 | |
|   end;
 | |
| 
 | |
|   { TDelayedUdateItem }
 | |
| 
 | |
|   TDelayedUdateItem = class(TCollectionItem)
 | |
|   private
 | |
|     FUpdateCount: Integer;
 | |
|     FDoChanged: Boolean;
 | |
|   protected
 | |
|     procedure Changed;
 | |
|     procedure DoChanged; virtual;
 | |
|     procedure DoEndUpdate; virtual; // even if not changed
 | |
|   public
 | |
|     procedure Assign(ASource: TPersistent); override;
 | |
|     procedure BeginUpdate; virtual;
 | |
|     constructor Create(ACollection: TCollection); override;
 | |
|     procedure EndUpdate;
 | |
|     function IsUpdating: Boolean;
 | |
|   end;
 | |
| 
 | |
|   { TRefCountedColectionItem }
 | |
| 
 | |
|   TRefCountedColectionItem = class(TDelayedUdateItem)
 | |
|   public
 | |
|     constructor Create(ACollection: TCollection); override;
 | |
|     destructor  Destroy; override;
 | |
|     procedure AddReference;
 | |
|     procedure ReleaseReference;
 | |
|   private
 | |
|     FRefCount: Integer;
 | |
|   protected
 | |
|     procedure DoFree; virtual;
 | |
|     property  RefCount: Integer read FRefCount;
 | |
|   end;
 | |
| 
 | |
| procedure ReleaseRefAndNil(var ARefCountedObject);
 | |
| 
 | |
| implementation
 | |
| 
 | |
| procedure ReleaseRefAndNil(var ARefCountedObject);
 | |
| begin
 | |
|   Assert( (Pointer(ARefCountedObject) = nil) or
 | |
|           (TObject(ARefCountedObject) is TRefCountedObject) or
 | |
|           (TObject(ARefCountedObject) is TRefCountedColectionItem),
 | |
|          'ReleaseRefAndNil requires TRefCountedObject');
 | |
| 
 | |
|   if Pointer(ARefCountedObject) = nil then
 | |
|     exit;
 | |
| 
 | |
|   if (TObject(ARefCountedObject) is TRefCountedObject) then
 | |
|     TRefCountedObject(ARefCountedObject).ReleaseReference
 | |
|   else
 | |
|   if (TObject(ARefCountedObject) is TRefCountedColectionItem) then
 | |
|     TRefCountedColectionItem(ARefCountedObject).ReleaseReference;
 | |
| 
 | |
|   Pointer(ARefCountedObject) := nil;
 | |
| end;
 | |
| 
 | |
| { TDbgEntityValue }
 | |
| 
 | |
| function TDbgEntityValue.GetImmutable: Boolean;
 | |
| begin
 | |
|   Result := (devImmutable in FFlags) or ((FOwner <> nil) and FOwner.Immutable);
 | |
| end;
 | |
| 
 | |
| function TDbgEntityValue.GetStackFrame: Integer;
 | |
| begin
 | |
|   Result := FOwner.StackFrame;
 | |
| end;
 | |
| 
 | |
| function TDbgEntityValue.GetThreadId: Integer;
 | |
| begin
 | |
|   Result := FOwner.ThreadId;
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValue.SetImmutable(AValue: Boolean);
 | |
| begin
 | |
|   assert((AValue = True) or not(Immutable), 'TDbgEntityValue.SetImmutable Not allowed to set to false');
 | |
|   if AValue then Include(FFlags, devImmutable);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValue.DoAssign(AnOther: TDbgEntityValue);
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValue.Assign(AnOther: TDbgEntityValue);
 | |
| begin
 | |
|   Assert(not Immutable, 'TDbgEntityValue.Assign Immutable');
 | |
|   DoAssign(AnOther);
 | |
| end;
 | |
| 
 | |
| { TDbgEntityValuesList }
 | |
| 
 | |
| function TDbgEntityValuesList.GetImmutable: Boolean;
 | |
| begin
 | |
|   Result := devlImmutable in FFlags;
 | |
| end;
 | |
| 
 | |
| function TDbgEntityValuesList.GetEntry(AnIndex: Integer): TDbgEntityValue;
 | |
| begin
 | |
|   Result := TDbgEntityValue(FList[AnIndex]);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.SetImmutable(AValue: Boolean);
 | |
| begin
 | |
|   assert((AValue = True) or not(devlImmutable in FFlags), 'TDbgEntityValuesList.SetImmutable Not allowed to set to false');
 | |
|   if AValue then Include(FFlags, devlImmutable);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.DoCleared;
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.DoAdded(AnEntry: TDbgEntityValue);
 | |
| begin
 | |
| 
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.Init;
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| constructor TDbgEntityValuesList.Create(AThreadId, AStackFrame: Integer);
 | |
| begin
 | |
|   inherited Create;
 | |
|   FFlags := [];
 | |
|   FThreadId   := AThreadId;
 | |
|   FStackFrame := AStackFrame;
 | |
|   FList := TRefCntObjList.Create;
 | |
|   Init;
 | |
| end;
 | |
| 
 | |
| destructor TDbgEntityValuesList.Destroy;
 | |
| begin
 | |
|   Exclude(FFlags, devlImmutable);
 | |
|   Clear;
 | |
|   FList.Free;
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.DoAssign(AnOther: TDbgEntityValuesList);
 | |
| begin
 | |
|   DoAssignListContent(AnOther);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.DoAssignListContent(AnOther: TDbgEntityValuesList);
 | |
| var
 | |
|   e: TDbgEntityValue;
 | |
|   i: Integer;
 | |
| begin
 | |
|   for i := 0 to AnOther.FList.Count - 1 do begin
 | |
|     e := CreateEntry;
 | |
|     e.FOwner := Self;
 | |
|     e.Assign(TDbgEntityValue(AnOther.FList[i]));
 | |
|     FList.Add(e);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.Assign(AnOther: TDbgEntityValuesList);
 | |
| begin
 | |
|   Assert(not Immutable, 'TDbgEntityValuesList.Assign Immutable');
 | |
|   Assert((FThreadId = AnOther.FThreadId) and (FStackFrame = AnOther.FStackFrame), 'TDbgEntityValuesList.Assign same thread and stack');
 | |
| 
 | |
|   Clear;
 | |
|   DoAssign(AnOther);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.Add(AnEntry: TDbgEntityValue);
 | |
| begin
 | |
|   Assert(not Immutable, 'TDbgEntityValuesList.Add  Immutable');
 | |
|   AnEntry.FOwner := Self;
 | |
|   FList.Add(AnEntry);
 | |
|   DoAdded(AnEntry);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntityValuesList.Clear;
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   Assert(not Immutable, 'TDbgEntityValuesList.Clear Immutable');
 | |
|   if FList.Count = 0 then
 | |
|     exit;
 | |
|   for i := 0 to FList.Count - 1 do
 | |
|     TDbgEntityValue(FList[i]).FOwner := nil;
 | |
|   FList.Clear;
 | |
|   DoCleared;
 | |
| end;
 | |
| 
 | |
| function TDbgEntityValuesList.Count: Integer;
 | |
| begin
 | |
|   Result := FList.Count;
 | |
| end;
 | |
| 
 | |
| { TDbgEntitiesThreadStackList }
 | |
| 
 | |
| function TDbgEntitiesThreadStackList.GetEntry(AThreadId, AStackFrame: Integer): TDbgEntityValuesList;
 | |
| var
 | |
|   i, j: Integer;
 | |
| begin
 | |
|   i := IndexOfThread(AThreadId);
 | |
|   if i >= 0 then begin
 | |
|     // TODO: binary search / need sorted list
 | |
|     for j := 0 to FList[i].List.Count - 1 do begin
 | |
|       Result := TDbgEntityValuesList(FList[i].List[j]);
 | |
|       if Result.StackFrame = AStackFrame then
 | |
|         exit;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   if Immutable then begin
 | |
|     Result := nil;
 | |
|     exit;
 | |
|   end;
 | |
| 
 | |
|   Result := CreateEntry(AThreadId, AStackFrame);
 | |
|   Add(Result);
 | |
| end;
 | |
| 
 | |
| function TDbgEntitiesThreadStackList.GetEntryByIdx(AnIndex: Integer): TDbgEntityValuesList;
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   Result := nil;
 | |
|   i := 0;
 | |
|   while AnIndex >= FList[i].List.Count do begin
 | |
|     dec(AnIndex, FList[i].List.Count);
 | |
|     inc(i);
 | |
|     if i >= Length(FList) then
 | |
|       exit;
 | |
|   end;
 | |
|   Result := TDbgEntityValuesList(FList[i].List[AnIndex]);
 | |
| end;
 | |
| 
 | |
| function TDbgEntitiesThreadStackList.GetHasEntry(AThreadId, AStackFrame: Integer): Boolean;
 | |
| var
 | |
|   i, j: Integer;
 | |
| begin
 | |
|   Result := False;
 | |
|   i := IndexOfThread(AThreadId);
 | |
|   if i < 0 then exit;
 | |
|   // TODO: binary search / need sorted list
 | |
|   for j := 0 to FList[i].List.Count - 1 do begin
 | |
|     if TDbgEntityValuesList(FList[i].List[j]).StackFrame = AStackFrame then begin
 | |
|       Result := True;
 | |
|       exit;
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TDbgEntitiesThreadStackList.GetImmutable: Boolean;
 | |
| begin
 | |
|   Result := devtsImmutable in FFlags;
 | |
| end;
 | |
| 
 | |
| function TDbgEntitiesThreadStackList.IndexOfThread(AThreadId: Integer;
 | |
|   ACreateSubList: Boolean): Integer;
 | |
| begin
 | |
|   Result := length(FList) - 1;
 | |
|   while (Result >= 0) and (FList[Result].ThreadId <> AThreadId) do
 | |
|     dec(Result);
 | |
|   if (Result >= 0) or (not ACreateSubList) then
 | |
|     exit;
 | |
| 
 | |
|   Result := length(FList);
 | |
|   SetLength(FList, Result + 1);
 | |
|   FList[Result].ThreadId := AThreadId;
 | |
|   FList[Result].List := TRefCntObjList.Create;
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.SetImmutable(AValue: Boolean);
 | |
| begin
 | |
|   assert((AValue = True) or not(devtsImmutable in FFlags), 'TDbgEntityValuesList.SetImmutable Not allowed to set to false');
 | |
|   if AValue then Include(FFlags, devtsImmutable);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.DoCleared;
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.DoAdded(AnEntry: TDbgEntityValuesList);
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| destructor TDbgEntitiesThreadStackList.Destroy;
 | |
| begin
 | |
|   Exclude(FFlags, devtsImmutable);
 | |
|   Clear;
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.DoAssign(AnOther: TDbgEntitiesThreadStackList);
 | |
| begin
 | |
|   DoAssignListContent(AnOther);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.DoAssignListContent(AnOther: TDbgEntitiesThreadStackList);
 | |
| var
 | |
|   i, j: Integer;
 | |
|   t: Integer;
 | |
|   e, o: TDbgEntityValuesList;
 | |
| begin
 | |
|   SetLength(FList, length(AnOther.FList));
 | |
|   for i := 0 to Length(FList) - 1 do begin
 | |
|     t := AnOther.FList[i].ThreadId;
 | |
|     FList[i].ThreadId := t;
 | |
|     FList[i].List := TRefCntObjList.Create;
 | |
|     for j := 0 to AnOther.FList[i].List.Count - 1 do begin
 | |
|       o := TDbgEntityValuesList(AnOther.FList[i].List[j]);
 | |
|       e := CreateEntry(t, o.StackFrame);
 | |
|       e.FOwner := Self;
 | |
|       e.Assign(o);
 | |
|       FList[i].List.Add(e);
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.Assign(AnOther: TDbgEntitiesThreadStackList);
 | |
| begin
 | |
|   Assert(not Immutable, 'TDbgEntitiesThreadStackList.Assign Immutable');
 | |
|   Clear;
 | |
|   DoAssign(AnOther);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.Add(AnEntry: TDbgEntityValuesList);
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   Assert(not Immutable, 'TDbgEntitiesThreadStackList.Add Immutable');
 | |
|   Assert((AnEntry.FOwner = nil) or (AnEntry.FOwner = Self), 'TDbgEntitiesThreadStackList.Add Entry.FThreadStackList');
 | |
|   AnEntry.FOwner := Self;
 | |
|   i := IndexOfThread(AnEntry.ThreadId, True);
 | |
|   FList[i].List.Add(AnEntry);
 | |
|   DoAdded(AnEntry);
 | |
| end;
 | |
| 
 | |
| procedure TDbgEntitiesThreadStackList.Clear;
 | |
| var
 | |
|   i, j: Integer;
 | |
| begin
 | |
|   Assert(not Immutable, 'TDbgEntitiesThreadStackList.Clear Immutable');
 | |
|   if Length(FList) = 0 then
 | |
|     exit;
 | |
|   for i := 0 to Length(FList) - 1 do begin
 | |
|     for j := 0 to FList[i].List.Count - 1 do
 | |
|       TDbgEntityValuesList(FList[i].List[j]).FOwner := nil;
 | |
|     FList[i].List.Free;
 | |
|   end;
 | |
|   SetLength(FList, 0);
 | |
|   DoCleared;
 | |
| end;
 | |
| 
 | |
| function TDbgEntitiesThreadStackList.Count: Integer;
 | |
| var
 | |
|   i: Integer;
 | |
| begin
 | |
|   Result := 0;
 | |
|   for i := 0 to Length(FList) - 1 do
 | |
|     Result := Result + FList[i].List.Count;
 | |
| end;
 | |
| 
 | |
| 
 | |
| { TDelayedUdateItem }
 | |
| 
 | |
| procedure TDelayedUdateItem.Assign(ASource: TPersistent);
 | |
| begin
 | |
|   BeginUpdate;
 | |
|   try
 | |
|     inherited Assign(ASource);
 | |
|   finally
 | |
|     EndUpdate;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TDelayedUdateItem.BeginUpdate;
 | |
| begin
 | |
|   Inc(FUpdateCount);
 | |
|   if FUpdateCount = 1 then FDoChanged := False;
 | |
| end;
 | |
| 
 | |
| procedure TDelayedUdateItem.Changed;
 | |
| begin
 | |
|   if FUpdateCount > 0
 | |
|   then FDoChanged := True
 | |
|   else DoChanged;
 | |
| end;
 | |
| 
 | |
| constructor TDelayedUdateItem.Create(ACollection: TCollection);
 | |
| begin
 | |
|   inherited Create(ACollection);
 | |
|   FUpdateCount := 0;
 | |
| end;
 | |
| 
 | |
| procedure TDelayedUdateItem.DoChanged;
 | |
| begin
 | |
|   inherited Changed(False);
 | |
| end;
 | |
| 
 | |
| procedure TDelayedUdateItem.DoEndUpdate;
 | |
| begin
 | |
|   //
 | |
| end;
 | |
| 
 | |
| procedure TDelayedUdateItem.EndUpdate;
 | |
| begin
 | |
|   Dec(FUpdateCount);
 | |
|   if FUpdateCount < 0 then raise EInvalidOperation.Create('TDelayedUdateItem.EndUpdate');
 | |
|   if (FUpdateCount = 0)
 | |
|   then DoEndUpdate;
 | |
|   if (FUpdateCount = 0) and FDoChanged
 | |
|   then begin
 | |
|     DoChanged;
 | |
|     FDoChanged := False;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| function TDelayedUdateItem.IsUpdating: Boolean;
 | |
| begin
 | |
|   Result := FUpdateCount > 0;
 | |
| end;
 | |
| 
 | |
| 
 | |
| { TRefCountedColectionItem }
 | |
| 
 | |
| constructor TRefCountedColectionItem.Create(ACollection: TCollection);
 | |
| begin
 | |
|   FRefCount := 0;
 | |
|   inherited Create(ACollection);
 | |
| end;
 | |
| 
 | |
| destructor TRefCountedColectionItem.Destroy;
 | |
| begin
 | |
|   Assert(FRefcount = 0, 'Destroying referenced object');
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TRefCountedColectionItem.AddReference;
 | |
| begin
 | |
|   Inc(FRefcount);
 | |
| end;
 | |
| 
 | |
| procedure TRefCountedColectionItem.ReleaseReference;
 | |
| begin
 | |
|   Assert(FRefCount > 0, 'TRefCountedObject.ReleaseReference  RefCount > 0');
 | |
|   Dec(FRefCount);
 | |
|   if FRefCount = 0 then DoFree;
 | |
| end;
 | |
| 
 | |
| procedure TRefCountedColectionItem.DoFree;
 | |
| begin
 | |
|   Self.Free;
 | |
| end;
 | |
| 
 | |
| end.
 | |
| 
 | 
