codetools: implemented complete gathering of identifiers in interface

git-svn-id: trunk@19647 -
This commit is contained in:
mattias 2009-04-27 09:35:08 +00:00
parent de39b16ffd
commit cfc67e3541
3 changed files with 256 additions and 97 deletions

View File

@ -43,12 +43,12 @@ uses
type
{
1. interface cache:
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 will improve search time for interface requests
-> This improves search time for interface requests
}
PInterfaceIdentCacheEntry = ^TInterfaceIdentCacheEntry;
TInterfaceIdentCacheEntry = record
@ -56,21 +56,29 @@ type
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 TInterfaceIdentCacheEntry
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;
end;
{
@ -343,6 +351,10 @@ end;
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
@ -411,6 +423,25 @@ begin
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;
@ -422,10 +453,39 @@ end;
destructor TInterfaceIdentifierCache.Destroy;
begin
Clear;
if FItems<>nil then FItems.Free;
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.FindAVLNode(Identifier: PChar): TAVLTreeNode;
var
Entry: PInterfaceIdentCacheEntry;
@ -448,6 +508,14 @@ begin
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;
@ -463,17 +531,26 @@ 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;
FItems.Add(NewEntry);
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;
@ -512,7 +589,8 @@ begin
Len:=0;
while IsIdentChar[Identifier[Len]] do inc(Len);
GetMem(Result,Len+1);
Move(Identifier^,Result^,Len+1);
Move(Identifier^,Result^,Len);
Result[Len]:=#0;
if FItems=nil then
FItems:=TAVLTree.Create(TListSortCompare(@CompareIdentifiers));
FItems.Add(Result);

View File

@ -708,6 +708,7 @@ type
procedure BeginParsing(DeleteNodes, OnlyInterfaceNeeded: boolean); override;
procedure ValidateToolDependencies; override;
function BuildInterfaceIdentifierCache(ExceptionOnNotUnit: boolean): boolean;
function FindDeclaration(const CursorPos: TCodeXYPosition;
out NewPos: TCodeXYPosition; out NewTopLine: integer): boolean;
function FindMainDeclaration(const CursorPos: TCodeXYPosition;
@ -4768,7 +4769,7 @@ function TFindDeclarationTool.FindIdentifierInUsesSection(
{ this function is internally used by FindIdentifierInContext
search backwards through the uses section
compare first the unit name, then load the unit and search there
compare first the all unit names, then load the units and search there
}
var
InAtom, UnitNameAtom: TAtomPosition;
@ -4777,21 +4778,43 @@ var
begin
{$IFDEF CheckNodeTool}CheckNodeTool(UsesNode);{$ENDIF}
Result:=false;
MoveCursorToUsesEnd(UsesNode);
// reparse uses section
MoveCursorToNodeStart(UsesNode);
if (UsesNode.Desc=ctnUsesSection) then begin
ReadNextAtom;
if not UpAtomIs('USES') then
RaiseUsesExpected;
end;
repeat
ReadPriorUsedUnit(UnitNameAtom, InAtom);
ReadNextAtom; // read name
if CurPos.StartPos>SrcLen then break;
if AtomIsChar(';') then break;
AtomIsIdentifier(true);
if (Params.IdentifierTool=Self)
and CompareSrcIdentifiers(UnitNameAtom.StartPos,Params.Identifier) then
and CompareSrcIdentifiers(CurPos.StartPos,Params.Identifier) then
begin
// the searched identifier was a uses unitname, point to the identifier in
// the uses section
Result:=true;
Params.SetResult(Self,UsesNode,UnitNameAtom.StartPos);
Params.SetResult(Self,UsesNode,CurPos.StartPos);
exit;
end;
if (fdfIgnoreUsedUnits in Params.Flags) then begin
// search further
end else begin
ReadNextAtom;
if UpAtomIs('IN') then begin
ReadNextAtom;
if not AtomIsStringConstant then RaiseStrConstExpected;
ReadNextAtom;
end;
if AtomIsChar(';') then break;
if not AtomIsChar(',') then
RaiseExceptionFmt(ctsStrExpectedButAtomFound,[';',GetAtom])
until (CurPos.StartPos>SrcLen);
if not (fdfIgnoreUsedUnits in Params.Flags) then begin
// search in units
MoveCursorToUsesEnd(UsesNode);
repeat
ReadPriorUsedUnit(UnitNameAtom, InAtom);
// open the unit
{$IFDEF ShowTriedUnits}
DebugLn('TFindDeclarationTool.FindIdentifierInUsesSection Self=',MainFilename,
@ -4808,9 +4831,9 @@ begin
if Result and Params.IsFinal then exit;
// restore the cursor
MoveCursorToCleanPos(UnitNameAtom.StartPos);
end;
ReadPriorAtom; // read keyword 'uses' or comma
until not AtomIsChar(',');
ReadPriorAtom; // read keyword 'uses' or comma
until not AtomIsChar(',');
end;
end;
function TFindDeclarationTool.FindCodeToolForUsedUnit(UnitNameAtom,
@ -4952,19 +4975,9 @@ end;
function TFindDeclarationTool.FindIdentifierInInterface(
AskingTool: TFindDeclarationTool; Params: TFindDeclarationParams): boolean;
var InterfaceNode: TCodeTreeNode;
SrcIsUsable: boolean;
OldInput: TFindDeclarationInput;
var
CacheEntry: PInterfaceIdentCacheEntry;
procedure RaiseWrongContext;
begin
DebugLn('TFindDeclarationTool.FindIdentifierInInterface.RaiseWrongContext');
Params.WriteDebugReport;
SaveRaiseException('TFindDeclarationTool.FindIdentifierInInterface '
+'Internal Error: Wrong CodeTool');
end;
IdentFoundResult: TIdentifierFoundResult;
begin
Result:=false;
// build code tree
@ -4978,78 +4991,109 @@ begin
{$ENDIF}
// ToDo: build codetree for ppu, ppw, dcu files
// build tree for pascal source
BuildTree(true);
if not BuildInterfaceIdentifierCache(true) then exit(false);
if (AskingTool<>Self) and (AskingTool<>nil) then
AskingTool.AddToolDependency(Self);
// search identifier in cache
if (FInterfaceIdentifierCache<>nil)
and (not (fdfCollect in Params.Flags)) then begin
CacheEntry:=FInterfaceIdentifierCache.FindIdentifier(Params.Identifier);
if CacheEntry<>nil then begin
// identifier in cache found
{$IFDEF ShowInterfaceCache}
DebugLn('[TFindDeclarationTool.FindIdentifierInInterface] Ident already in cache:',
' Exists=',DbgS(CacheEntry^.Node<>nil));
{$ENDIF}
if CacheEntry^.Node=nil then begin
// identifier not in this interface
end else begin
// identifier in this interface found
Params.SetResult(Self,CacheEntry^.Node,CacheEntry^.CleanPos);
Result:=true;
end;
exit;
end;
CacheEntry:=FInterfaceIdentifierCache.FindIdentifier(Params.Identifier);
while CacheEntry<>nil do begin
Params.SetResult(Self,CacheEntry^.Node,CacheEntry^.CleanPos);
IdentFoundResult:=DoOnIdentifierFound(Params,Params.NewNode);
{$IFDEF ShowProcSearch}
DebugLn(['[TFindDeclarationTool.FindIdentifierInContext.CheckResult] DoOnIdentifierFound=',IdentifierFoundResultNames[IdentFoundResult]]);
{$ENDIF}
if (IdentFoundResult=ifrSuccess) then
exit(true);
if IdentFoundResult=ifrAbortSearch then exit(false);
// proceed
CacheEntry:=CacheEntry^.Overloaded;
end;
// check source name
MoveCursorToNodeStart(Tree.Root);
ReadNextAtom; // read keyword for source type, e.g. 'unit'
SrcIsUsable:=UpAtomIs('UNIT');
if not SrcIsUsable then
RaiseException(ctsSourceIsNotUnit);
ReadNextAtom; // read source name
if CompareSrcIdentifiers(CurPos.StartPos,Params.Identifier) then begin
// identifier is source name
Params.SetResult(Self,Tree.Root,CurPos.StartPos);
Result:=true;
exit;
end;
// search identifier in interface
InterfaceNode:=FindInterfaceNode;
if InterfaceNode=nil then
RaiseException(ctsInterfaceSectionNotFound);
Params.Save(OldInput);
Params.Flags:=(fdfGlobalsSameIdent*Params.Flags)
-[fdfExceptionOnNotFound,fdfSearchInParentNodes]
+[fdfIgnoreUsedUnits];
Params.ContextNode:=InterfaceNode;
Result:=FindIdentifierInContext(Params);
Params.Load(OldInput,true);
exit(false);
end;
// save result in cache
if Params.Flags*[fdfCollect,fdfDoNotCache]=[] then begin
if FInterfaceIdentifierCache=nil then
FInterfaceIdentifierCache:=TInterfaceIdentifierCache.Create(Self);
if Result and (Params.NewCodeTool=Self) then begin
// identifier exists in interface
if (Params.NewNode<>nil) and (Params.NewNode.Desc=ctnProcedure) then begin
//DebugLn('NOTE: TFindDeclarationTool.FindIdentifierInInterface Node is proc');
// ToDo: add param list to cache
// -> do not cache
end else begin
FInterfaceIdentifierCache.Add(OldInput.Identifier,Params.NewNode,
Params.NewCleanPos);
end;
end else if not Result then begin
// identifier does not exist in this interface
FInterfaceIdentifierCache.Add(OldInput.Identifier,nil,-1);
function TFindDeclarationTool.BuildInterfaceIdentifierCache(
ExceptionOnNotUnit: boolean): boolean;
procedure ScanForEnums(Node: TCodeTreeNode);
begin
while Node<>nil do begin
if Node.Desc=ctnEnumIdentifier then
FInterfaceIdentifierCache.Add(@Src[Node.StartPos],Node,Node.StartPos);
Node:=Node.Next;
end;
end;
procedure ScanChilds(ParentNode: TCodeTreeNode);
var
Node: TCodeTreeNode;
begin
Node:=ParentNode.FirstChild;
while Node<>nil do begin
case Node.Desc of
ctnTypeSection,ctnConstSection,ctnVarSection,ctnResStrSection:
ScanChilds(Node);
ctnVarDefinition,ctnConstDefinition,ctnTypeDefinition:
FInterfaceIdentifierCache.Add(@Src[Node.StartPos],Node,Node.StartPos);
ctnGenericType:
if Node.FirstChild<>nil then
FInterfaceIdentifierCache.Add(@Src[Node.FirstChild.StartPos],Node,Node.StartPos);
ctnProperty:
begin
MoveCursorToPropName(Node);
FInterfaceIdentifierCache.Add(@Src[CurPos.StartPos],Node,Node.StartPos);
end;
ctnProcedure:
if (Node.FirstChild<>nil) and (not NodeIsOperator(Node)) then
FInterfaceIdentifierCache.Add(@Src[Node.FirstChild.StartPos],Node,Node.StartPos);
end;
ScanForEnums(Node);
Node:=Node.NextBrother;
end;
end;
var
InterfaceNode: TCodeTreeNode;
begin
// build tree for pascal source
BuildTree(true);
// search interface section
InterfaceNode:=FindInterfaceNode;
if InterfaceNode=nil then begin
// check source type
if ExceptionOnNotUnit then begin
MoveCursorToNodeStart(Tree.Root);
ReadNextAtom; // read keyword for source type, e.g. 'unit'
if not UpAtomIs('UNIT') then
RaiseException(ctsSourceIsNotUnit);
RaiseException(ctsInterfaceSectionNotFound);
end else
exit(true);
end;
// create tree
if (FInterfaceIdentifierCache<>nil) and FInterfaceIdentifierCache.Complete then
exit(true);
if FInterfaceIdentifierCache=nil then
FInterfaceIdentifierCache:=TInterfaceIdentifierCache.Create(Self)
else
FInterfaceIdentifierCache.Clear;
FInterfaceIdentifierCache.Complete:=true;
// add unit node
MoveCursorToNodeStart(Tree.Root);
ReadNextAtom; // keyword unit
ReadNextAtom;
FInterfaceIdentifierCache.Add(@Src[CurPos.StartPos],Tree.Root,CurPos.StartPos);
// create nodes
ScanChilds(InterfaceNode);
Result:=true;
end;
function TFindDeclarationTool.CompareNodeIdentifier(Node: TCodeTreeNode;
@ -5113,7 +5157,7 @@ begin
DebugLn('WARNING: Searching again in hidden unit: "',NewCode.Filename,'"');
end else begin
// source found -> get codetool for it
{$IFDEF ShowTriedContexts}
{$IF defined(ShowTriedContexts) or defined(ShowTriedUnits)}
DebugLn('[TFindDeclarationTool.FindIdentifierInUsedUnit] ',
' This source is=',TCodeBuffer(Scanner.MainCode).Filename,
' NewCode=',NewCode.Filename,' IgnoreUsedUnits=',dbgs(fdfIgnoreUsedUnits in Params.Flags));
@ -7673,8 +7717,10 @@ end;
procedure TFindDeclarationTool.DoDeleteNodes;
begin
ClearNodeCaches(true);
if FInterfaceIdentifierCache<>nil then
if FInterfaceIdentifierCache<>nil then begin
FInterfaceIdentifierCache.Clear;
FInterfaceIdentifierCache.Complete:=false;
end;
inherited DoDeleteNodes;
end;
@ -7862,9 +7908,8 @@ procedure TFindDeclarationTool.ConsistencyCheck;
var ANodeCache: TCodeTreeNodeCache;
begin
inherited ConsistencyCheck;
if FInterfaceIdentifierCache<>nil then begin
end;
if FInterfaceIdentifierCache<>nil then
FInterfaceIdentifierCache.ConsistencyCheck;
ANodeCache:=FFirstNodeCache;
while ANodeCache<>nil do begin
ANodeCache.ConsistencyCheck;

View File

@ -111,6 +111,7 @@ type
function NodeIsConstructor(ProcNode: TCodeTreeNode): boolean;
function NodeIsDestructor(ProcNode: TCodeTreeNode): boolean;
function NodeIsForwardProc(ProcNode: TCodeTreeNode): boolean;
function NodeIsOperator(ProcNode: TCodeTreeNode): boolean;
// classes
function ExtractClassName(ClassNode: TCodeTreeNode;
@ -154,6 +155,7 @@ type
function MoveCursorToParameterSpecifier(DefinitionNode: TCodeTreeNode
): boolean;
function FindEndOfWithVar(WithVarNode: TCodeTreeNode): integer;
function NodeIsIdentifierInInterface(Node: TCodeTreeNode): boolean;
// sections
function GetSourceName(DoBuildTree: boolean = true): string;
@ -1590,6 +1592,16 @@ begin
if (ctnsForwardDeclaration and ProcNode.SubDesc)>0 then exit(true);
end;
function TPascalReaderTool.NodeIsOperator(ProcNode: TCodeTreeNode): boolean;
begin
Result:=false;
if (ProcNode=nil) then exit;
if ProcNode.Desc=ctnProcedureHead then
ProcNode:=ProcNode.Parent;
if ProcNode.Desc<>ctnProcedure then exit;
Result:=CompareIdentifiers('operator',@Src[ProcNode.StartPos])=0;
end;
function TPascalReaderTool.NodeIsPartOfTypeDefinition(ANode: TCodeTreeNode
): boolean;
begin
@ -1674,6 +1686,30 @@ begin
Result:=CurPos.EndPos;
end;
function TPascalReaderTool.NodeIsIdentifierInInterface(Node: TCodeTreeNode
): boolean;
begin
case Node.Desc of
ctnEnumIdentifier:
Result:=true;
ctnVarDefinition:
Result:=(Node.Parent.Desc=ctnVarSection)
and (Node.Parent.Parent.Desc=ctnInterface);
ctnConstDefinition:
Result:=(Node.Parent.Desc=ctnConstSection)
and (Node.Parent.Parent.Desc=ctnInterface);
ctnTypeDefinition,ctnGenericType:
Result:=(Node.Parent.Desc=ctnTypeSection)
and (Node.Parent.Parent.Desc=ctnInterface);
ctnProcedure,ctnProperty:
Result:=Node.Parent.Desc=ctnInterface;
ctnProcedureHead:
Result:=(Node.Parent.Desc=ctnProcedure)
and (Node.Parent.Parent.Desc=ctnInterface);
end;
Result:=false;
end;
function TPascalReaderTool.GetSourceName(DoBuildTree: boolean): string;
var NamePos: TAtomPosition;
begin