lazarus/components/codetools/finddeclarationcache.pas
2010-07-20 13:06:23 +00:00

1364 lines
39 KiB
ObjectPascal

{
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Abstract:
Cache objects for TFindDeclarationTool.
}
unit FindDeclarationCache;
{$mode objfpc}{$H+}
interface
{$I codetools.inc}
// for debugging
{ $DEFINE HardExceptions}
uses
Classes, SysUtils, FileProcs, BasicCodeTools, CodeTree, LinkScanner,
AVL_Tree, PascalParserTool, KeywordFuncLists,
CodeToolMemManager;
type
{
1. interface cache: (unit interfaces, not class interfaces)
Every FindIdentifierInInterface call is cached
- stores: Identifier -> Node+CleanPos
- cache must be deleted, everytime the codetree is rebuild
this is enough update, because it does only store internals
-> This improves search time for interface requests
}
PInterfaceIdentCacheEntry = ^TInterfaceIdentCacheEntry;
TInterfaceIdentCacheEntry = record
Identifier: PChar;
Node: TCodeTreeNode; // if node = nil then identifier does not exist in
// this interface
CleanPos: integer;
Overloaded: PInterfaceIdentCacheEntry;
NextEntry: PInterfaceIdentCacheEntry; // used by memory manager
end;
{ TInterfaceIdentifierCache }
TInterfaceIdentifierCache = class
private
FComplete: boolean;
FItems: TAVLTree; // tree of PInterfaceIdentCacheEntry
FTool: TPascalParserTool;
function FindAVLNode(Identifier: PChar): TAVLTreeNode;
procedure SetComplete(const AValue: boolean);
public
function FindIdentifier(Identifier: PChar): PInterfaceIdentCacheEntry;
procedure Add(Identifier: PChar; Node: TCodeTreeNode; CleanPos: integer);
procedure Clear;
procedure ClearMissingIdentifiers;
constructor Create(ATool: TPascalParserTool);
destructor Destroy; override;
procedure ConsistencyCheck;
property Tool: TPascalParserTool read FTool;
property Complete: boolean read FComplete write SetComplete;
property Items: TAVLTree read FItems;
function CalcMemSize: PtrUInt;
end;
{
2. code tree node cache:
Some nodes (class, proc, record) contain a node cache. A node cache caches
search results of searched identifiers for child nodes.
Every entry in the node cache describes the following relationship:
Identifier+Range -> Source Position
and can be interpreted as:
Identifier is a PChar to the beginning of an identifier string.
Range is a cleaned source range (CleanStartPos-CleanEndPos).
Source position is a tuple of NewTool, NewNode, NewCleanPos.
If the current context node is a child of a caching node and it is in the
range, then the result is valid. If NewNode=nil then there is no such
identifier valid at the context node.
Every node that defines local identifiers contains a node cache.
These are: class, interface, proc, record, withstatement
Because node caches can store information of used units, the cache must be
deleted every time a used unit is changed. Currently all node caches are
resetted every time the GlobalWriteLock increases.
}
const
AllNodeCacheDescs =
AllClasses+[ctnProcedure, ctnRecordType, ctnWithStatement];
type
TNodeCacheEntryFlag = (ncefSearchedInParents, ncefSearchedInAncestors);
TNodeCacheEntryFlags = set of TNodeCacheEntryFlag;
PCodeTreeNodeCacheEntry = ^TCodeTreeNodeCacheEntry;
TCodeTreeNodeCacheEntry = record
Identifier: PChar;
CleanStartPos: integer;
CleanEndPos: integer;
NewNode: TCodeTreeNode;
NewTool: TPascalParserTool;
NewCleanPos: integer;
Flags: TNodeCacheEntryFlags;
NextEntry: PCodeTreeNodeCacheEntry; // used for mem manager
end;
{ TCodeTreeNodeCache }
TCodeTreeNodeCache = class
private
FItems: TAVLTree; // tree of PCodeTreeNodeCacheEntry
public
Next: TCodeTreeNodeCache;
Owner: TCodeTreeNode;
function FindLeftMostAVLNode(Identifier: PChar): TAVLTreeNode;
function FindRightMostAVLNode(Identifier: PChar): TAVLTreeNode;
function FindAVLNode(Identifier: PChar; CleanPos: integer): TAVLTreeNode;
function FindAVLNodeInRange(Identifier: PChar;
CleanStartPos, CleanEndPos: integer): TAVLTreeNode;
function FindInRange(Identifier: PChar;
CleanStartPos, CleanEndPos: integer): PCodeTreeNodeCacheEntry;
function FindNearestAVLNode(Identifier: PChar;
CleanStartPos, CleanEndPos: integer; InFront: boolean): TAVLTreeNode;
function FindNearest(Identifier: PChar;
CleanStartPos, CleanEndPos: integer;
InFront: boolean): PCodeTreeNodeCacheEntry;
function Find(Identifier: PChar): PCodeTreeNodeCacheEntry;
procedure Add(Identifier: PChar;
SrcTool: TPascalParserTool; CleanStartPos, CleanEndPos: integer;
NewNode: TCodeTreeNode; NewTool: TPascalParserTool; NewCleanPos: integer;
Flags: TNodeCacheEntryFlags);
procedure Clear;
procedure BindToOwner(NewOwner: TCodeTreeNode);
procedure UnbindFromOwner;
constructor Create(AnOwner: TCodeTreeNode);
destructor Destroy; override;
procedure WriteDebugReport(const Prefix: string);
procedure ConsistencyCheck;
function CalcMemSize: PtrUInt;
end;
{
3. Base type node cache
All nodes, that are aliases, has this type of cache.
For example a variable 'i: integer' creates several basetype nodes:
1. i variable node points to its type node 'integer'.
2. 'integer' node points to type definition node 'integer'.
3. 'integer' identifier node points to its base type 'longint'.
4. 'longint' identifier node points points to its range.
FindBaseTypeOfNode will search this chain, and on success will create
TBaseTypeCache(s). All four nodes will point directly to the range.
}
{ TBaseTypeCache }
TBaseTypeCache = class
private
public
NewNode: TCodeTreeNode;
NewTool: TPascalParserTool;
Next: TBaseTypeCache; // used for mem manager
Owner: TCodeTreeNode;
procedure BindToOwner(NewOwner: TCodeTreeNode);
procedure UnbindFromOwner;
constructor Create(AnOwner: TCodeTreeNode);
destructor Destroy; override;
function CalcMemSize: PtrUInt;
end;
{
4. CodeTool Cache Dependencies
Node- and BaseTypeCache depends on their codetool and the
node- and basetypecaches of other codetools (=used codetools). The used
codetools dependencies are saved in the TCodeToolDependencies, which is
simple an TAVLTree of codetools. This allows to decide, wether the cache of
a codetools must be rebuild.
}
//----------------------------------------------------------------------------
type
{ TGlobalIdentifierTree }
TGlobalIdentifierTree = class
private
FItems: TAVLTree; // tree of PChar;
FDefaultDataBlockSize: integer;
FDataBlockSize: integer;
FDataBlock: Pointer;
FDataBlockEnd: integer;
FFullDataBlocks: TFPList; // full blocks of data
function InternalGetMem(Size: integer): Pointer;
public
function AddCopy(Identifier: PChar): PChar;
function Find(Identifier: PChar): PChar;
procedure Clear;
constructor Create;
destructor Destroy; override;
function Count: integer;
function CalcMemSize: PtrUInt;
end;
//----------------------------------------------------------------------------
// Memory Managers
// memory system for PInterfaceIdentCacheEntry(s)
TInterfaceIdentCacheEntryMemManager = class(TCodeToolMemManager)
protected
procedure FreeFirstItem; override;
public
procedure DisposeEntry(Entry: PInterfaceIdentCacheEntry);
function NewEntry: PInterfaceIdentCacheEntry;
end;
// memory system for PCodeTreeNodeCacheEntry(s)
TNodeCacheEntryMemManager = class(TCodeToolMemManager)
protected
procedure FreeFirstItem; override;
public
procedure DisposeEntry(Entry: PCodeTreeNodeCacheEntry);
function NewEntry: PCodeTreeNodeCacheEntry;
end;
// memory system for TCodeTreeNodeCache(s)
TNodeCacheMemManager = class(TCodeToolMemManager)
protected
procedure FreeFirstItem; override;
public
procedure DisposeNodeCache(NodeCache: TCodeTreeNodeCache);
function NewNodeCache(AnOwner: TCodeTreeNode): TCodeTreeNodeCache;
end;
// memory system for TBaseTypeCache(s)
TBaseTypeCacheMemManager = class(TCodeToolMemManager)
protected
procedure FreeFirstItem; override;
public
procedure DisposeBaseTypeCache(BaseTypeCache: TBaseTypeCache);
function NewBaseTypeCache(AnOwner: TCodeTreeNode): TBaseTypeCache;
end;
//----------------------------------------------------------------------------
// stacks for circle checking
type
TCodeTreeNodeStackEntry = TCodeTreeNode;
TCodeTreeNodeStack = record
Fixedtems: array[0..9] of TCodeTreeNodeStackEntry;
DynItems: TFPList; // list of TCodeTreeNodeStackEntry
StackPtr: integer;
end;
PCodeTreeNodeStack = ^TCodeTreeNodeStack;
procedure InitializeNodeStack(NodeStack: PCodeTreeNodeStack);
function GetNodeStackEntry(NodeStack: PCodeTreeNodeStack;
Index: integer): TCodeTreeNodeStackEntry;
procedure AddNodeToStack(NodeStack: PCodeTreeNodeStack;
NewNode: TCodeTreeNode);
function NodeExistsInStack(NodeStack: PCodeTreeNodeStack;
Node: TCodeTreeNode): boolean;
procedure FinalizeNodeStack(NodeStack: PCodeTreeNodeStack);
const
ncefAllSearchRanges = [ncefSearchedInAncestors,ncefSearchedInParents];
NodeCacheEntryFlagNames: array[TNodeCacheEntryFlag] of string = (
'SearchedInParents', 'SearchedInAncestors'
);
var
GlobalIdentifierTree: TGlobalIdentifierTree;
InterfaceIdentCacheEntryMemManager: TInterfaceIdentCacheEntryMemManager;
NodeCacheEntryMemManager: TNodeCacheEntryMemManager;
NodeCacheMemManager: TNodeCacheMemManager;
BaseTypeCacheMemManager: TBaseTypeCacheMemManager;
function NodeCacheEntryFlagsAsString(Flags: TNodeCacheEntryFlags): string;
implementation
function NodeCacheEntryFlagsAsString(Flags: TNodeCacheEntryFlags): string;
var f: TNodeCacheEntryFlag;
begin
Result:='';
for f:=Low(TNodeCacheEntryFlag) to High(TNodeCacheEntryFlag) do begin
if f in Flags then begin
if Result<>'' then Result:=rEsult+', ';
Result:=Result+NodeCacheEntryFlagNames[f];
end;
end;
end;
{ TNodeCacheEntryMemManager }
procedure TNodeCacheEntryMemManager.DisposeEntry(Entry: PCodeTreeNodeCacheEntry);
begin
if (FFreeCount<FMinFree) or (FFreeCount<((FCount shr 3)*FMaxFreeRatio)) then
begin
// add Entry to Free list
Entry^.NextEntry:=PCodeTreeNodeCacheEntry(FFirstFree);
PCodeTreeNodeCacheEntry(FFirstFree):=Entry;
inc(FFreeCount);
end else begin
// free list full -> free the Entry
Dispose(Entry);
{$IFDEF DebugCTMemManager}
inc(FFreedCount);
{$ENDIF}
end;
dec(FCount);
end;
function TNodeCacheEntryMemManager.NewEntry: PCodeTreeNodeCacheEntry;
begin
if FFirstFree<>nil then begin
// take from free list
Result:=PCodeTreeNodeCacheEntry(FFirstFree);
PCodeTreeNodeCacheEntry(FFirstFree):=Result^.NextEntry;
Result^.NextEntry:=nil;
dec(FFreeCount);
end else begin
// free list empty -> create new Entry
New(Result);
{$IFDEF DebugCTMemManager}
inc(FAllocatedCount);
{$ENDIF}
end;
inc(FCount);
end;
procedure TNodeCacheEntryMemManager.FreeFirstItem;
var Entry: PCodeTreeNodeCacheEntry;
begin
Entry:=PCodeTreeNodeCacheEntry(FFirstFree);
PCodeTreeNodeCacheEntry(FFirstFree):=Entry^.NextEntry;
Dispose(Entry);
end;
{ TInterfaceIdentCacheEntryMemManager }
procedure TInterfaceIdentCacheEntryMemManager.DisposeEntry(
Entry: PInterfaceIdentCacheEntry);
begin
if Entry^.Overloaded<>nil then begin
DisposeEntry(Entry^.Overloaded);
Entry^.Overloaded:=nil;
end;
if (FFreeCount<FMinFree) or (FFreeCount<((FCount shr 3)*FMaxFreeRatio)) then
begin
// add Entry to Free list
Entry^.NextEntry:=PInterfaceIdentCacheEntry(FFirstFree);
PInterfaceIdentCacheEntry(FFirstFree):=Entry;
inc(FFreeCount);
end else begin
// free list full -> free the Entry
Dispose(Entry);
{$IFDEF DebugCTMemManager}
inc(FFreedCount);
{$ENDIF}
end;
dec(FCount);
end;
function TInterfaceIdentCacheEntryMemManager.NewEntry: PInterfaceIdentCacheEntry;
begin
if FFirstFree<>nil then begin
// take from free list
Result:=PInterfaceIdentCacheEntry(FFirstFree);
PInterfaceIdentCacheEntry(FFirstFree):=Result^.NextEntry;
Result^.NextEntry:=nil;
dec(FFreeCount);
end else begin
// free list empty -> create new Entry
New(Result);
{$IFDEF DebugCTMemManager}
inc(FAllocatedCount);
{$ENDIF}
end;
inc(FCount);
end;
procedure TInterfaceIdentCacheEntryMemManager.FreeFirstItem;
var Entry: PInterfaceIdentCacheEntry;
begin
Entry:=PInterfaceIdentCacheEntry(FFirstFree);
PInterfaceIdentCacheEntry(FFirstFree):=Entry^.NextEntry;
Dispose(Entry);
end;
{ TInterfaceIdentifierCache }
function CompareTInterfaceIdentCacheEntry(Data1, Data2: Pointer): integer;
begin
Result:=CompareIdentifiers(PInterfaceIdentCacheEntry(Data1)^.Identifier,
PInterfaceIdentCacheEntry(Data2)^.Identifier);
end;
procedure TInterfaceIdentifierCache.Clear;
var
Node: TAVLTreeNode;
Entry: PInterfaceIdentCacheEntry;
begin
if FItems<>nil then begin
Node:=FItems.FindLowest;
while Node<>nil do begin
Entry:=PInterfaceIdentCacheEntry(Node.Data);
InterfaceIdentCacheEntryMemManager.DisposeEntry(Entry);
Node:=FItems.FindSuccessor(Node);
end;
FItems.Clear;
end;
end;
procedure TInterfaceIdentifierCache.ClearMissingIdentifiers;
var
Node: TAVLTreeNode;
NextNode: TAVLTreeNode;
Entry: PInterfaceIdentCacheEntry;
begin
if FItems=nil then exit;
Node:=FItems.FindLowest;
while Node<>nil do begin
NextNode:=FItems.FindSuccessor(Node);
Entry:=PInterfaceIdentCacheEntry(Node.Data);
if Entry^.Node=nil then begin
FItems.Delete(Node);
InterfaceIdentCacheEntryMemManager.DisposeEntry(Entry);
end;
Node:=NextNode;
end;
end;
constructor TInterfaceIdentifierCache.Create(ATool: TPascalParserTool);
begin
inherited Create;
FTool:=ATool;
if ATool=nil then
raise Exception.Create('TInterfaceIdentifierCache.Create ATool=nil');
end;
destructor TInterfaceIdentifierCache.Destroy;
begin
Clear;
FreeAndNil(FItems);
inherited Destroy;
end;
procedure TInterfaceIdentifierCache.ConsistencyCheck;
var
Node: TAVLTreeNode;
Entry: PInterfaceIdentCacheEntry;
begin
if FItems<>nil then begin
if FItems.ConsistencyCheck<>0 then
RaiseCatchableException('');
Node:=FItems.FindLowest;
while Node<>nil do begin
Entry:=PInterfaceIdentCacheEntry(Node.Data);
while Entry<>nil do begin
if (Entry^.Identifier=nil) or (Entry^.Identifier^=#0) then
RaiseCatchableException('');
if (Entry^.Node=nil) and Complete then
RaiseCatchableException('');
if (Entry^.Overloaded<>nil)
and (CompareIdentifiers(Entry^.Identifier,Entry^.Overloaded^.Identifier)<>0)
then begin
debugln(['TInterfaceIdentifierCache.ConsistencyCheck Entry=',GetIdentifier(Entry^.Identifier),'<>',GetIdentifier(Entry^.Overloaded^.Identifier)]);
RaiseCatchableException('');
end;
Entry:=Entry^.Overloaded;
end;
Node:=FItems.FindSuccessor(Node);
end;
end;
end;
function TInterfaceIdentifierCache.CalcMemSize: PtrUInt;
var
Node: TAVLTreeNode;
begin
Result:=PtrUInt(InstanceSize);
if FItems<>nil then begin
inc(Result,FItems.Count*SizeOf(TAVLTreeNode));
Node:=FItems.FindLowest;
while Node<>nil do begin
inc(Result,SizeOf(TInterfaceIdentCacheEntry));
Node:=FItems.FindSuccessor(Node);
end;
end;
end;
function TInterfaceIdentifierCache.FindAVLNode(Identifier: PChar): TAVLTreeNode;
var
Entry: PInterfaceIdentCacheEntry;
comp: integer;
begin
if FItems<>nil then begin
Result:=FItems.Root;
while Result<>nil do begin
Entry:=PInterfaceIdentCacheEntry(Result.Data);
comp:=CompareIdentifiers(Identifier,Entry^.Identifier);
if comp<0 then
Result:=Result.Left
else if comp>0 then
Result:=Result.Right
else
exit;
end;
end else begin
Result:=nil;
end;
end;
procedure TInterfaceIdentifierCache.SetComplete(const AValue: boolean);
begin
if FComplete=AValue then exit;
FComplete:=AValue;
if FComplete then
ClearMissingIdentifiers;
end;
function TInterfaceIdentifierCache.FindIdentifier(Identifier: PChar
): PInterfaceIdentCacheEntry;
var Node: TAVLTreeNode;
begin
Node:=FindAVLNode(Identifier);
if Node<>nil then
Result:=PInterfaceIdentCacheEntry(Node.Data)
else
Result:=nil;
end;
procedure TInterfaceIdentifierCache.Add(Identifier: PChar; Node: TCodeTreeNode;
CleanPos: integer);
var
NewEntry: PInterfaceIdentCacheEntry;
OldNode: TAVLTreeNode;
begin
if (GetIdentLen(Identifier)=0) then
RaiseCatchableException('');
if FItems=nil then
FItems:=TAVLTree.Create(@CompareTInterfaceIdentCacheEntry);
OldNode:=FindAVLNode(Identifier);
NewEntry:=InterfaceIdentCacheEntryMemManager.NewEntry;
NewEntry^.Identifier:=GlobalIdentifierTree.AddCopy(Identifier);
NewEntry^.Node:=Node;
NewEntry^.CleanPos:=CleanPos;
if OldNode<>nil then begin
NewEntry^.Overloaded:=PInterfaceIdentCacheEntry(OldNode.Data);
OldNode.Data:=NewEntry;
end else begin
NewEntry^.Overloaded:=nil;
FItems.Add(NewEntry);
end;
end;
{ TGlobalIdentifierTree }
procedure TGlobalIdentifierTree.Clear;
var
i: Integer;
begin
if FItems<>nil then
FItems.Clear;
if FFullDataBlocks<>nil then begin
for i:=0 to FFullDataBlocks.Count-1 do
FreeMem(FFullDataBlocks[i]);
FFullDataBlocks.Clear;
ReAllocMem(FDataBlock,0);
FDataBlockEnd:=0;
FDataBlockSize:=0;
end;
end;
constructor TGlobalIdentifierTree.Create;
begin
inherited Create;
FItems:=TAVLTree.Create(TListSortCompare(@CompareIdentifiers));
FFullDataBlocks:=TFPList.Create;
FDefaultDataBlockSize:=256*256*2;
end;
destructor TGlobalIdentifierTree.Destroy;
begin
Clear;
FItems.Free;
FFullDataBlocks.Free;
inherited Destroy;
end;
function TGlobalIdentifierTree.Count: integer;
begin
if FItems<>nil then
Result:=FItems.Count
else
Result:=0;
end;
function TGlobalIdentifierTree.CalcMemSize: PtrUInt;
begin
Result:=PtrUInt(InstanceSize)
+PtrUint(FItems.InstanceSize)
+PtrUInt(FItems.Count)*PtrUint(TAVLTreeNode.InstanceSize)
+PtrUInt(FFullDataBlocks.InstanceSize)
+PtrUInt(FFullDataBlocks.Capacity)*SizeOf(Pointer)
+PtrUInt(FFullDataBlocks.Count*FDefaultDataBlockSize)
+PtrUInt(FDataBlockSize);
end;
function TGlobalIdentifierTree.InternalGetMem(Size: integer): Pointer;
begin
if (FDataBlock=nil) or (FDataBlockEnd+Size>FDataBlockSize) then begin
// store old block
FFullDataBlocks.Add(FDataBlock);
// create a new
FDataBlockSize:=FDefaultDataBlockSize;
if FDataBlockSize<Size then
FDataBlockSize:=Size;
GetMem(FDataBlock,FDataBlockSize);
FDataBlockEnd:=0;
end;
Result:=FDataBlock+FDataBlockEnd;
inc(FDataBlockEnd,Size);
end;
function TGlobalIdentifierTree.AddCopy(Identifier: PChar): PChar;
var Len: integer;
begin
Result:=nil;
if (Identifier=nil) or (not IsIdentChar[Identifier[0]]) then exit;
Result:=Find(Identifier);
if Result<>nil then
exit;
Len:=0;
while IsIdentChar[Identifier[Len]] do inc(Len);
Result:=InternalGetMem(Len+1);
// GetMem(Result,Len+1);
Move(Identifier^,Result^,Len);
Result[Len]:=#0;
FItems.Add(Result);
end;
function TGlobalIdentifierTree.Find(Identifier: PChar): PChar;
var
comp: integer;
Node: TAVLTreeNode;
begin
if FItems<>nil then begin
Node:=FItems.Root;
while Node<>nil do begin
Result:=PChar(Node.Data);
comp:=CompareIdentifiers(Identifier,Result);
if comp<0 then
Node:=Node.Left
else if comp>0 then
Node:=Node.Right
else
exit;
end;
end;
Result:=nil;
end;
{ TCodeTreeNodeCache }
function CompareTCodeTreeNodeCacheEntry(Data1, Data2: Pointer): integer;
var Entry1, Entry2: PCodeTreeNodeCacheEntry;
begin
Entry1:=PCodeTreeNodeCacheEntry(Data1);
Entry2:=PCodeTreeNodeCacheEntry(Data2);
Result:=CompareIdentifiers(Entry1^.Identifier,Entry2^.Identifier);
if Result=0 then begin
if Entry1^.CleanStartPos>Entry2^.CleanStartPos then
Result:=-1
else if Entry1^.CleanStartPos<Entry2^.CleanStartPos then
Result:=1
else
Result:=0;
end;
end;
constructor TCodeTreeNodeCache.Create(AnOwner: TCodeTreeNode);
begin
inherited Create;
if AnOwner<>nil then BindToOwner(AnOwner);
end;
destructor TCodeTreeNodeCache.Destroy;
begin
Clear;
UnbindFromOwner;
FItems.Free;
inherited Destroy;
end;
function TCodeTreeNodeCache.FindLeftMostAVLNode(Identifier: PChar): TAVLTreeNode;
// find leftmost avl node with Identifier
var
Entry: PCodeTreeNodeCacheEntry;
Node: TAVLTreeNode;
comp: integer;
begin
if FItems<>nil then begin
Result:=FItems.Root;
while Result<>nil do begin
Entry:=PCodeTreeNodeCacheEntry(Result.Data);
comp:=CompareIdentifiers(Identifier,Entry^.Identifier);
if comp<0 then
Result:=Result.Left
else if comp>0 then
Result:=Result.Right
else begin
repeat
Node:=FItems.FindPrecessor(Result);
if Node<>nil then begin
Entry:=PCodeTreeNodeCacheEntry(Node.Data);
if CompareIdentifiers(Identifier,Entry^.Identifier)=0 then
Result:=Node
else
break;
end else
break;
until false;
exit;
end;
end;
end else begin
Result:=nil;
end;
end;
procedure TCodeTreeNodeCache.Clear;
var
Node: TAVLTreeNode;
Entry: PCodeTreeNodeCacheEntry;
begin
if FItems<>nil then begin
Node:=FItems.FindLowest;
while Node<>nil do begin
Entry:=PCodeTreeNodeCacheEntry(Node.Data);
NodeCacheEntryMemManager.DisposeEntry(Entry);
Node:=FItems.FindSuccessor(Node);
end;
FItems.Clear;
end;
end;
procedure TCodeTreeNodeCache.Add(Identifier: PChar;
SrcTool: TPascalParserTool; CleanStartPos, CleanEndPos: integer;
NewNode: TCodeTreeNode; NewTool: TPascalParserTool; NewCleanPos: integer;
Flags: TNodeCacheEntryFlags);
procedure AddNewEntry;
var NewEntry: PCodeTreeNodeCacheEntry;
begin
NewEntry:=NodeCacheEntryMemManager.NewEntry;
NewEntry^.Identifier:=GlobalIdentifierTree.AddCopy(Identifier);
NewEntry^.CleanStartPos:=CleanStartPos;
NewEntry^.CleanEndPos:=CleanEndPos;
NewEntry^.NewNode:=NewNode;
NewEntry^.NewTool:=NewTool;
NewEntry^.NewCleanPos:=NewCleanPos;
NewEntry^.Flags:=Flags;
FItems.Add(NewEntry);
end;
var
OldEntry: PCodeTreeNodeCacheEntry;
OldNode: TAVLTreeNode;
NewSearchRangeFlags: TNodeCacheEntryFlags;
function P2S(CleanPos: integer): string;
begin
Result:=SrcTool.CleanPosToStr(CleanPos);
end;
function ParamsDebugReport: string;
var
s: string;
begin
s:=' Ident='+GetIdentifier(Identifier);
s:=s+' New: Range='+P2S(CleanStartPos)
+'-'+P2S(CleanEndPos);
if Owner<>nil then begin
s:=s+' Owner='+Owner.DescAsString;
s:=s+' OwnerPos='+P2S(Owner.StartPos);
end;
if OldEntry<>nil then begin
s:=s+' Old: Range='+P2S(OldEntry^.CleanStartPos)
+'-'+P2S(OldEntry^.CleanEndPos);
if OldEntry^.NewNode<>nil then begin
s:=s+' Node='+OldEntry^.NewNode.DescAsString
+' Pos='+OldEntry^.NewTool.CleanPosToStr(OldEntry^.NewNode.StartPos);
end else
s:=s+' Node=nil';
if OldEntry^.NewTool<>nil then begin
s:=s+' Tool='+ExtractFilename(OldEntry^.NewTool.MainFilename);
if OldEntry^.NewNode<>nil then
s:=s+' Src="'
+StringToPascalConst(
copy(OldEntry^.NewTool.Src,OldEntry^.NewNode.StartPos,50))+'"';
end;
end;
if NewNode<>nil then begin
s:=s+' Node='+NewNode.DescAsString
+' Pos='+NewTool.CleanPosToStr(NewNode.StartPos);
end else
s:=s+' Node=nil';
if NewTool<>nil then begin
s:=s+' Tool='+ExtractFileName(NewTool.MainFilename);
if NewNode<>nil then
s:=s+' Src="'
+StringToPascalConst(copy(NewTool.Src,NewNode.StartPos,50))+'"';
end;
Result:=s;
end;
procedure RaiseConflictException(const Msg: string);
var
s: string;
begin
s:='[TCodeTreeNodeCache.Add] internal error:'+Msg+ParamsDebugReport;
{$IFDEF HardExceptions}
DebugLn(s);
RaiseCatchableException('TCodeTreeNodeCache.Add A');
{$ELSE}
raise Exception.Create(s);
{$ENDIF}
end;
begin
OldEntry:=nil;
// consistency checks
if CleanStartPos>=CleanEndPos then
RaiseConflictException('CleanStartPos>=CleanEndPos');
if (NewNode<>nil) then begin
if NewTool=nil then
RaiseConflictException('NewNode<>nil and NewTool=nil');
if not NewTool.Tree.ContainsNode(NewNode) then
RaiseConflictException('NewNode is not a node of NewTool');
end;
{if CompareIdentifiers(Identifier,'FillRect')=0 then begin
DebugLn('[[[[======================================================');
DebugLn(['[TCodeTreeNodeCache.Add] Ident=',GetIdentifier(Identifier),
' CleanStartPos=',CleanStartPos,' CleanEndPos=',CleanEndPos,
' Flags=[',NodeCacheEntryFlagsAsString(Flags),']',
' NewNode=',NewNode<>nil
]);
DebugLn('======================================================]]]]');
CTDumpStack;
end;}
if FItems=nil then
FItems:=TAVLTree.Create(@CompareTCodeTreeNodeCacheEntry);
// if identifier already exists, try to combine them
OldNode:=FindAVLNodeInRange(Identifier,CleanStartPos,CleanEndPos);
if OldNode=nil then begin
// identifier was never searched in this range
AddNewEntry;
end else begin
// identifier was already searched in this range
OldEntry:=PCodeTreeNodeCacheEntry(OldNode.Data);
NewSearchRangeFlags:=(ncefAllSearchRanges * (OldEntry^.Flags+Flags));
if ((NewNode=OldEntry^.NewNode)
and (NewTool=OldEntry^.NewTool))
or ((OldEntry^.NewNode=nil) and (NewSearchRangeFlags<>[])) then
begin
// same FindContext or better FindContext with overlapping search ranges
// -> combine search ranges
if OldEntry^.CleanStartPos>CleanStartPos then
OldEntry^.CleanStartPos:=CleanStartPos;
if OldEntry^.CleanEndPos<CleanEndPos then
OldEntry^.CleanEndPos:=CleanEndPos;
OldEntry^.Flags:=NewSearchRangeFlags;
end else begin
// different FindContext with overlapping search ranges
RaiseConflictException('conflicting cache nodes');
end;
end;
end;
function TCodeTreeNodeCache.Find(Identifier: PChar): PCodeTreeNodeCacheEntry;
var Node: TAVLTreeNode;
begin
Node:=FindLeftMostAVLNode(Identifier);
if Node<>nil then begin
Result:=PCodeTreeNodeCacheEntry(Node.Data);
end else begin
Result:=nil;
end;
end;
function TCodeTreeNodeCache.FindAVLNode(Identifier: PChar; CleanPos: integer
): TAVLTreeNode;
begin
Result:=FindAVLNodeInRange(Identifier,CleanPos,CleanPos);
end;
function TCodeTreeNodeCache.FindRightMostAVLNode(Identifier: PChar
): TAVLTreeNode;
// find rightmost avl node with Identifier
var
Entry: PCodeTreeNodeCacheEntry;
Node: TAVLTreeNode;
comp: integer;
begin
if FItems<>nil then begin
Result:=FItems.Root;
while Result<>nil do begin
Entry:=PCodeTreeNodeCacheEntry(Result.Data);
comp:=CompareIdentifiers(Identifier,Entry^.Identifier);
if comp<0 then
Result:=Result.Left
else if comp>0 then
Result:=Result.Right
else begin
repeat
Node:=FItems.FindSuccessor(Result);
if Node<>nil then begin
Entry:=PCodeTreeNodeCacheEntry(Node.Data);
if CompareIdentifiers(Identifier,Entry^.Identifier)=0 then
Result:=Node
else
break;
end else
break;
until false;
exit;
end;
end;
end else begin
Result:=nil;
end;
end;
function TCodeTreeNodeCache.FindAVLNodeInRange(Identifier: PChar;
CleanStartPos, CleanEndPos: integer): TAVLTreeNode;
var
Entry: PCodeTreeNodeCacheEntry;
begin
Result:=FindNearestAVLNode(Identifier,CleanStartPos,CleanEndPos,true);
if Result<>nil then begin
Entry:=PCodeTreeNodeCacheEntry(Result.Data);
if (CleanStartPos>=Entry^.CleanEndPos)
or (CleanEndPos<=Entry^.CleanStartPos) then begin
// node is not in range
Result:=nil;
end;
end;
end;
function TCodeTreeNodeCache.FindNearestAVLNode(Identifier: PChar;
CleanStartPos, CleanEndPos: integer; InFront: boolean): TAVLTreeNode;
var
Entry: PCodeTreeNodeCacheEntry;
comp: integer;
DirectionSucc: boolean;
NextNode: TAVLTreeNode;
begin
if CleanStartPos>CleanEndPos then begin
raise Exception.Create('[TCodeTreeNodeCache.FindNearestAVLNode]'
+' internal error: CleanStartPos>CleanEndPos');
end;
if (FItems<>nil) and (Identifier<>nil) then begin
Result:=FItems.Root;
while Result<>nil do begin
Entry:=PCodeTreeNodeCacheEntry(Result.Data);
comp:=CompareIdentifiers(Identifier,Entry^.Identifier);
if comp<0 then
Result:=Result.Left
else if comp>0 then
Result:=Result.Right
else begin
// cached result with identifier found
// -> check range
if CleanStartPos>=Entry^.CleanEndPos then begin
NextNode:=FItems.FindSuccessor(Result);
DirectionSucc:=true;
end else if CleanEndPos<=Entry^.CleanStartPos then begin
NextNode:=FItems.FindPrecessor(Result);
DirectionSucc:=false;
end else begin
// cached result in range found
exit;
end;
while (NextNode<>nil) do begin
Entry:=PCodeTreeNodeCacheEntry(NextNode.Data);
if CompareIdentifiers(Identifier,Entry^.Identifier)<>0 then begin
exit;
end;
Result:=NextNode;
if (CleanStartPos<Entry^.CleanEndPos)
and (CleanEndPos>Entry^.CleanStartPos) then begin
// cached result in range found
exit;
end;
if DirectionSucc then
NextNode:=FItems.FindSuccessor(Result)
else
NextNode:=FItems.FindPrecessor(Result);
end;
exit;
end;
end;
end else begin
Result:=nil;
end;
end;
procedure TCodeTreeNodeCache.ConsistencyCheck;
begin
if (FItems<>nil) then begin
if FItems.ConsistencyCheck<>0 then
raise Exception.Create('');
end;
if Owner<>nil then begin
if Owner.Cache<>Self then
raise Exception.Create('');
end;
end;
function TCodeTreeNodeCache.CalcMemSize: PtrUInt;
var
Node: TAVLTreeNode;
begin
Result:=PtrUInt(InstanceSize);
if FItems<>nil then begin
inc(Result,SizeOf(TAVLTreeNode)*FItems.Count);
Node:=FItems.FindLowest;
while Node<>nil do begin
inc(Result,SizeOf(TCodeTreeNodeCacheEntry));
Node:=FItems.FindSuccessor(Node);
end;
end;
end;
procedure TCodeTreeNodeCache.WriteDebugReport(const Prefix: string);
var Node: TAVLTreeNode;
Entry: PCodeTreeNodeCacheEntry;
begin
DebugLn(Prefix+'[TCodeTreeNodeCache.WriteDebugReport] Self='+DbgS(Self));
if FItems<>nil then begin
Node:=FItems.FindLowest;
while Node<>nil do begin
Entry:=PCodeTreeNodeCacheEntry(Node.Data);
write(Prefix,' Ident="',GetIdentifier(Entry^.Identifier),'"');
DbgOut(' Flags=[',NodeCacheEntryFlagsAsString(Entry^.Flags),']');
DbgOut(' Node=',DbgS(Entry^.NewNode<>nil));
DebugLn('');
Node:=FItems.FindSuccessor(Node);
end;
end;
ConsistencyCheck;
end;
procedure TCodeTreeNodeCache.UnbindFromOwner;
begin
if Owner<>nil then begin
if Owner.Cache<>Self then
raise Exception.Create('[TCodeTreeNodeCache.UnbindFromOwner] '
+' internal error: Owner.Cache<>Self');
Owner.Cache:=nil;
Owner:=nil;
end;
end;
procedure TCodeTreeNodeCache.BindToOwner(NewOwner: TCodeTreeNode);
begin
if NewOwner<>nil then begin
if NewOwner.Cache<>nil then
raise Exception.Create('[TCodeTreeNodeCache.BindToOwner] internal error:'
+' NewOwner.Cache<>nil');
NewOwner.Cache:=Self;
end;
Owner:=NewOwner;
end;
function TCodeTreeNodeCache.FindNearest(Identifier: PChar; CleanStartPos,
CleanEndPos: integer; InFront: boolean): PCodeTreeNodeCacheEntry;
var Node: TAVLTreeNode;
begin
Node:=FindNearestAVLNode(Identifier,CleanStartPos,CleanEndPos,InFront);
if Node<>nil then
Result:=PCodeTreeNodeCacheEntry(Node.Data)
else
Result:=nil;
end;
function TCodeTreeNodeCache.FindInRange(Identifier: PChar; CleanStartPos,
CleanEndPos: integer): PCodeTreeNodeCacheEntry;
var Node: TAVLTreeNode;
begin
Node:=FindAVLNodeInRange(Identifier,CleanStartPos,CleanEndPos);
if Node<>nil then
Result:=PCodeTreeNodeCacheEntry(Node.Data)
else
Result:=nil;
end;
{ TNodeCacheMemManager }
procedure TNodeCacheMemManager.DisposeNodeCache(NodeCache: TCodeTreeNodeCache);
begin
NodeCache.UnbindFromOwner;
if (FFreeCount<FMinFree) or (FFreeCount<((FCount shr 3)*FMaxFreeRatio)) then
begin
// add Entry to Free list
NodeCache.Next:=TCodeTreeNodeCache(FFirstFree);
TCodeTreeNodeCache(FFirstFree):=NodeCache;
inc(FFreeCount);
end else begin
// free list full -> free the NodeCache
NodeCache.Free;
{$IFDEF DebugCTMemManager}
inc(FFreedCount);
{$ENDIF}
end;
dec(FCount);
end;
procedure TNodeCacheMemManager.FreeFirstItem;
var NodeCache: TCodeTreeNodeCache;
begin
NodeCache:=TCodeTreeNodeCache(FFirstFree);
TCodeTreeNodeCache(FFirstFree):=NodeCache.Next;
NodeCache.Free;
end;
function TNodeCacheMemManager.NewNodeCache(
AnOwner: TCodeTreeNode): TCodeTreeNodeCache;
begin
if FFirstFree<>nil then begin
// take from free list
Result:=TCodeTreeNodeCache(FFirstFree);
TCodeTreeNodeCache(FFirstFree):=Result.Next;
Result.Clear;
Result.BindToOwner(AnOwner);
dec(FFreeCount);
end else begin
// free list empty -> create new NodeCache
Result:=TCodeTreeNodeCache.Create(AnOwner);
{$IFDEF DebugCTMemManager}
inc(FAllocatedCount);
{$ENDIF}
end;
inc(FCount);
end;
//------------------------------------------------------------------------------
procedure InitializeNodeStack(NodeStack: PCodeTreeNodeStack);
begin
NodeStack^.StackPtr:=0;
NodeStack^.DynItems:=nil;
end;
function GetNodeStackEntry(NodeStack: PCodeTreeNodeStack;
Index: integer): TCodeTreeNodeStackEntry;
begin
if Index<=High(NodeStack^.Fixedtems) then begin
Result:=NodeStack^.Fixedtems[Index];
end else begin
Result:=TCodeTreeNodeStackEntry(
NodeStack^.DynItems[Index-High(NodeStack^.Fixedtems)-1]);
end;
end;
procedure AddNodeToStack(NodeStack: PCodeTreeNodeStack;
NewNode: TCodeTreeNode);
begin
if (NodeStack^.StackPtr<=High(NodeStack^.Fixedtems)) then begin
NodeStack^.Fixedtems[NodeStack^.StackPtr]:=NewNode;
end else begin
if NodeStack^.DynItems=nil then begin
NodeStack^.DynItems:=TFPList.Create;
end;
NodeStack^.DynItems.Add(NewNode);
end;
inc(NodeStack^.StackPtr);
end;
function NodeExistsInStack(NodeStack: PCodeTreeNodeStack;
Node: TCodeTreeNode): boolean;
var i: integer;
begin
Result:=true;
i:=0;
while i<NodeStack^.StackPtr do begin
if i<=High(NodeStack^.Fixedtems) then begin
if NodeStack^.Fixedtems[i]=Node then exit;
end else begin
if NodeStack^.DynItems[i-High(NodeStack^.Fixedtems)-1]=Pointer(Node) then
exit;
end;
inc(i);
end;
Result:=false;
end;
procedure FinalizeNodeStack(NodeStack: PCodeTreeNodeStack);
begin
NodeStack^.DynItems.Free;
end;
{ TBaseTypeCacheMemManager }
procedure TBaseTypeCacheMemManager.DisposeBaseTypeCache(
BaseTypeCache: TBaseTypeCache);
begin
BaseTypeCache.UnbindFromOwner;
if (FFreeCount<FMinFree) or (FFreeCount<((FCount shr 3)*FMaxFreeRatio)) then
begin
// add Entry to Free list
BaseTypeCache.Next:=TBaseTypeCache(FFirstFree);
TBaseTypeCache(FFirstFree):=BaseTypeCache;
inc(FFreeCount);
end else begin
// free list full -> free the BaseType
BaseTypeCache.Free;
{$IFDEF DebugCTMemManager}
inc(FFreedCount);
{$ENDIF}
end;
dec(FCount);
end;
procedure TBaseTypeCacheMemManager.FreeFirstItem;
var BaseTypeCache: TBaseTypeCache;
begin
BaseTypeCache:=TBaseTypeCache(FFirstFree);
TBaseTypeCache(FFirstFree):=BaseTypeCache.Next;
BaseTypeCache.Free;
end;
function TBaseTypeCacheMemManager.NewBaseTypeCache(
AnOwner: TCodeTreeNode): TBaseTypeCache;
begin
if FFirstFree<>nil then begin
// take from free list
Result:=TBaseTypeCache(FFirstFree);
TBaseTypeCache(FFirstFree):=Result.Next;
Result.BindToOwner(AnOwner);
dec(FFreeCount);
end else begin
// free list empty -> create new BaseType
Result:=TBaseTypeCache.Create(AnOwner);
{$IFDEF DebugCTMemManager}
inc(FAllocatedCount);
{$ENDIF}
end;
inc(FCount);
end;
{ TBaseTypeCache }
procedure TBaseTypeCache.BindToOwner(NewOwner: TCodeTreeNode);
begin
if NewOwner<>nil then begin
if NewOwner.Cache<>nil then
raise Exception.Create('[TBaseTypeCache.BindToOwner] internal error:'
+' NewOwner.Cache<>nil');
NewOwner.Cache:=Self;
end;
Owner:=NewOwner;
end;
constructor TBaseTypeCache.Create(AnOwner: TCodeTreeNode);
begin
inherited Create;
if AnOwner<>nil then BindToOwner(AnOwner);
end;
destructor TBaseTypeCache.Destroy;
begin
UnbindFromOwner;
inherited Destroy;
end;
function TBaseTypeCache.CalcMemSize: PtrUInt;
begin
Result:=PtrUInt(InstanceSize);
end;
procedure TBaseTypeCache.UnbindFromOwner;
begin
if Owner<>nil then begin
if Owner.Cache<>Self then
raise Exception.Create('[TBaseTypeCache.UnbindFromOwner] '
+' internal error: Owner.Cache<>Self');
Owner.Cache:=nil;
Owner:=nil;
end;
end;
//------------------------------------------------------------------------------
procedure InternalInit;
begin
GlobalIdentifierTree:=TGlobalIdentifierTree.Create;
InterfaceIdentCacheEntryMemManager:=TInterfaceIdentCacheEntryMemManager.Create;
NodeCacheEntryMemManager:=TNodeCacheEntryMemManager.Create;
NodeCacheMemManager:=TNodeCacheMemManager.Create;
BaseTypeCacheMemManager:=TBaseTypeCacheMemManager.Create;
end;
procedure InternalFinal;
begin
BaseTypeCacheMemManager.Free;
BaseTypeCacheMemManager:=nil;
NodeCacheMemManager.Free;
NodeCacheMemManager:=nil;
NodeCacheEntryMemManager.Free;
NodeCacheEntryMemManager:=nil;
InterfaceIdentCacheEntryMemManager.Free;
InterfaceIdentCacheEntryMemManager:=nil;
GlobalIdentifierTree.Free;
GlobalIdentifierTree:=nil;
end;
initialization
InternalInit;
finalization
InternalFinal;
end.