mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-06 00:07:54 +02:00
527 lines
14 KiB
ObjectPascal
527 lines
14 KiB
ObjectPascal
{
|
|
Copyright (c) 1998-2002 by Florian Klaempfl, Pierre Muller
|
|
|
|
This unit handles the symbol tables
|
|
|
|
This program 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 program 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.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
****************************************************************************
|
|
}
|
|
unit symbase;
|
|
|
|
{$i fpcdefs.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{ common }
|
|
cutils,cclasses,
|
|
{ global }
|
|
globtype,globals,
|
|
{ symtable }
|
|
symconst
|
|
;
|
|
|
|
{************************************************
|
|
Needed forward pointers
|
|
************************************************}
|
|
|
|
type
|
|
TSymtable = class;
|
|
|
|
{ THashedIDString }
|
|
|
|
THashedIDString = record
|
|
private
|
|
FId : TIDString;
|
|
FHash : Longword;
|
|
procedure SetId(const s:TIDString);
|
|
public
|
|
property Id:TIDString read FId write SetId;
|
|
property Hash:longword read FHash;
|
|
end;
|
|
|
|
|
|
{************************************************
|
|
TDefEntry
|
|
************************************************}
|
|
|
|
TDefEntry = class
|
|
typ : tdeftyp;
|
|
defid : longint;
|
|
owner : TSymtable;
|
|
end;
|
|
|
|
|
|
{************************************************
|
|
TSymEntry
|
|
************************************************}
|
|
|
|
{ this object is the base for all symbol objects }
|
|
TSymEntry = class(TFPHashObject)
|
|
private
|
|
FRealName : {$ifdef symansistr}TSymStr{$else}pshortstring{$endif};
|
|
function GetEscapedRealName: TSymStr;
|
|
function GetRealname: TSymStr;
|
|
procedure SetRealname(const ANewName: TSymStr);
|
|
public
|
|
typ : tsymtyp;
|
|
SymId : longint;
|
|
Owner : TSymtable;
|
|
destructor destroy;override;
|
|
property RealName: TSymStr read GetRealName write SetRealName;
|
|
property EscapedRealName: TSymStr read GetEscapedRealName;
|
|
end;
|
|
|
|
{************************************************
|
|
TSymtable
|
|
************************************************}
|
|
|
|
TSymtable = class
|
|
public
|
|
name : pshortstring;
|
|
realname : pshortstring;
|
|
DefList : TFPObjectList;
|
|
SymList : TFPHashObjectList;
|
|
defowner : TDefEntry; { for records and objects }
|
|
moduleid : longint;
|
|
refcount : smallint;
|
|
currentvisibility : tvisibility;
|
|
currentlyoptional : boolean;
|
|
tableoptions : tsymtableoptions;
|
|
{ level of symtable, used for nested procedures }
|
|
symtablelevel : byte;
|
|
{ do not allow to add new definitions, can be extended to symbols probably }
|
|
sealed : boolean;
|
|
symtabletype : TSymtabletype;
|
|
constructor Create(const s:string);
|
|
{ attention: only execute the a child's destructor if refcount is 1! }
|
|
destructor destroy;override;
|
|
procedure freeinstance;override;
|
|
function getcopy:TSymtable;
|
|
procedure clear;virtual;
|
|
function checkduplicate(var s:THashedIDString;sym:TSymEntry):boolean;virtual;
|
|
procedure insertsym(sym:TSymEntry;checkdup:boolean=true);virtual;
|
|
procedure Deletesym(sym:TSymEntry);virtual;
|
|
function Find(const s:TIDString) : TSymEntry;
|
|
function FindWithHash(const s:THashedIDString) : TSymEntry;virtual;
|
|
procedure insertdef(def:TDefEntry);virtual;
|
|
procedure deletedef(def:TDefEntry);
|
|
function iscurrentunit:boolean;virtual;
|
|
{ includes the flag in this symtable and all parent symtables; if
|
|
it's already set the flag is not set again }
|
|
procedure includeoption(option:tsymtableoption);
|
|
end;
|
|
|
|
psymtablestackitem = ^TSymtablestackitem;
|
|
TSymtablestackitem = record
|
|
symtable : TSymtable;
|
|
next : psymtablestackitem;
|
|
end;
|
|
|
|
TSymtablestack = class
|
|
stack : psymtablestackitem;
|
|
constructor create;
|
|
destructor destroy;override;
|
|
procedure clear;
|
|
function finditem(st:TSymtable):psymtablestackitem;
|
|
procedure push(st:TSymtable); virtual;
|
|
procedure pushafter(st,afterst:TSymtable); virtual;
|
|
procedure pop(st:TSymtable); virtual;
|
|
function top:TSymtable;
|
|
function getcopyuntil(finalst: TSymtable): TSymtablestack;
|
|
end;
|
|
|
|
|
|
var
|
|
initialmacrosymtable: TSymtable; { macros initially defined by the compiler or
|
|
given on the command line. Is common
|
|
for all files compiled and do not change. }
|
|
macrosymtablestack,
|
|
symtablestack : TSymtablestack;
|
|
|
|
{$ifdef MEMDEBUG}
|
|
var
|
|
memrealnames : tmemdebug;
|
|
{$endif MEMDEBUG}
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
verbose;
|
|
|
|
{****************************************************************************
|
|
THashedIDString
|
|
****************************************************************************}
|
|
|
|
procedure THashedIDString.SetId(const s:TIDString);
|
|
begin
|
|
FId:=s;
|
|
{$ifdef symansistr}
|
|
if length(FId)>maxidlen then
|
|
SetLength(FId,maxidlen);
|
|
{$endif}
|
|
FHash:=FPHash(FId);
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TSymEntry
|
|
****************************************************************************}
|
|
|
|
destructor TSymEntry.destroy;
|
|
begin
|
|
{$ifdef MEMDEBUG}
|
|
memrealnames.start;
|
|
{$endif MEMDEBUG}
|
|
{$ifndef symansistr}
|
|
stringdispose(Frealname);
|
|
{$endif}
|
|
{$ifdef MEMDEBUG}
|
|
memrealnames.stop;
|
|
{$endif MEMDEBUG}
|
|
inherited destroy;
|
|
end;
|
|
|
|
|
|
function TSymEntry.GetRealname:TSymStr;
|
|
begin
|
|
{$ifndef symansistr}
|
|
if not assigned(FRealname) then
|
|
internalerror(200611011);
|
|
result:=FRealname^;
|
|
{$else}
|
|
if FRealName='' then
|
|
internalerror(200611011);
|
|
result:=FRealName;
|
|
{$endif}
|
|
end;
|
|
|
|
|
|
function TSymEntry.GetEscapedRealName: TSymStr;
|
|
begin
|
|
result:=GetRealname;
|
|
if result=Name then
|
|
result:='$'+result;
|
|
end;
|
|
|
|
|
|
procedure TSymEntry.SetRealname(const ANewName:TSymStr);
|
|
begin
|
|
{$ifndef symansistr}
|
|
stringdispose(FRealname);
|
|
FRealname:=stringdup(ANewName);
|
|
{$else}
|
|
FRealname:=ANewName;
|
|
{$endif}
|
|
if Hash<>$ffffffff then
|
|
begin
|
|
if ANewName[1]='$' then
|
|
Rename(Copy(ANewName,2,length(ANewName)))
|
|
else
|
|
Rename(Upper(ANewName));
|
|
end;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
TSymtable
|
|
****************************************************************************}
|
|
|
|
constructor TSymtable.Create(const s:string);
|
|
begin
|
|
if s<>'' then
|
|
begin
|
|
name:=stringdup(upper(s));
|
|
realname:=stringdup(s);
|
|
end
|
|
else
|
|
begin
|
|
name:=nil;
|
|
realname:=nil;
|
|
end;
|
|
symtabletype:=abstractsymtable;
|
|
symtablelevel:=0;
|
|
defowner:=nil;
|
|
DefList:=TFPObjectList.Create(true);
|
|
SymList:=TFPHashObjectList.Create(true);
|
|
refcount:=1;
|
|
currentvisibility:=vis_public;
|
|
currentlyoptional:=false;
|
|
end;
|
|
|
|
|
|
destructor TSymtable.destroy;
|
|
begin
|
|
{ freeinstance decreases refcount }
|
|
if refcount>1 then
|
|
exit;
|
|
Clear;
|
|
DefList.Free;
|
|
{ SymList can already be disposed or set to nil for withsymtable, }
|
|
{ but in that case Free does nothing }
|
|
SymList.Free;
|
|
stringdispose(name);
|
|
stringdispose(realname);
|
|
end;
|
|
|
|
|
|
procedure TSymtable.freeinstance;
|
|
begin
|
|
dec(refcount);
|
|
if refcount=0 then
|
|
inherited freeinstance;
|
|
end;
|
|
|
|
|
|
function TSymtable.getcopy:TSymtable;
|
|
begin
|
|
inc(refcount);
|
|
result:=self;
|
|
end;
|
|
|
|
|
|
function TSymtable.iscurrentunit:boolean;
|
|
begin
|
|
result:=false;
|
|
end;
|
|
|
|
procedure TSymtable.includeoption(option: tsymtableoption);
|
|
var
|
|
st: tsymtable;
|
|
begin
|
|
if option in tableoptions then
|
|
exit;
|
|
include(tableoptions,option);
|
|
{ iterative approach should be faster than recursion based on calls }
|
|
st:=self;
|
|
while assigned(st.defowner) do
|
|
begin
|
|
st:=st.defowner.owner;
|
|
{ this can happen for specializations of routines that are not yet
|
|
owned cause they might be thrown away again }
|
|
if not assigned(st) then
|
|
break;
|
|
{ the flag is already set, so by definition it is set in the
|
|
owning symtables as well }
|
|
if option in st.tableoptions then
|
|
break;
|
|
include(st.tableoptions,option);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TSymtable.clear;
|
|
var
|
|
i : integer;
|
|
begin
|
|
SymList.Clear;
|
|
{ Prevent recursive calls between TDef.destroy and TSymtable.Remove }
|
|
if DefList.OwnsObjects then
|
|
begin
|
|
for i := 0 to DefList.Count-1 do
|
|
TDefEntry(DefList[i]).Owner:=nil;
|
|
end;
|
|
DefList.Clear;
|
|
end;
|
|
|
|
|
|
function TSymtable.checkduplicate(var s:THashedIDString;sym:TSymEntry):boolean;
|
|
begin
|
|
result:=(FindWithHash(s)<>nil);
|
|
end;
|
|
|
|
|
|
procedure TSymtable.insertsym(sym:TSymEntry;checkdup:boolean=true);
|
|
var
|
|
hashedid : THashedIDString;
|
|
begin
|
|
if checkdup then
|
|
begin
|
|
if sym.realname[1]='$' then
|
|
hashedid.id:=Copy(sym.realname,2,maxidlen+1)
|
|
else
|
|
hashedid.id:=Upper(sym.realname);
|
|
{ First check for duplicates, this can change the symbol name
|
|
in case of a duplicate entry }
|
|
checkduplicate(hashedid,sym);
|
|
end;
|
|
{ Now we can insert the symbol, any duplicate entries
|
|
are renamed to an unique (and for users unaccessible) name }
|
|
if sym.realname[1]='$' then
|
|
sym.ChangeOwnerAndName(SymList,Copy(sym.realname,2,maxidlen+1))
|
|
else if length(sym.realname)>maxidlen then
|
|
sym.ChangeOwnerAndName(SymList,Upper(Copy(sym.realname,1,maxidlen)))
|
|
else
|
|
sym.ChangeOwnerAndName(SymList,Upper(sym.realname));
|
|
sym.Owner:=self;
|
|
end;
|
|
|
|
|
|
procedure TSymtable.Deletesym(sym:TSymEntry);
|
|
begin
|
|
if sym.Owner<>self then
|
|
internalerror(200611121);
|
|
SymList.Remove(sym);
|
|
end;
|
|
|
|
|
|
procedure TSymtable.insertdef(def:TDefEntry);
|
|
begin
|
|
DefList.Add(def);
|
|
def.owner:=self;
|
|
end;
|
|
|
|
|
|
procedure TSymtable.deletedef(def:TDefEntry);
|
|
begin
|
|
if def.Owner<>self then
|
|
internalerror(200611122);
|
|
def.Owner:=nil;
|
|
DefList.Remove(def);
|
|
end;
|
|
|
|
|
|
function TSymtable.Find(const s : TIDString) : TSymEntry;
|
|
begin
|
|
result:=TSymEntry(SymList.Find(s));
|
|
end;
|
|
|
|
|
|
function TSymtable.FindWithHash(const s:THashedIDString) : TSymEntry;
|
|
begin
|
|
result:=TSymEntry(SymList.FindWithHash(s.id,s.hash));
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
Symtable Stack
|
|
****************************************************************************}
|
|
|
|
constructor TSymtablestack.create;
|
|
begin
|
|
stack:=nil;
|
|
end;
|
|
|
|
|
|
destructor TSymtablestack.destroy;
|
|
begin
|
|
clear;
|
|
end;
|
|
|
|
|
|
procedure TSymtablestack.clear;
|
|
var
|
|
hp : psymtablestackitem;
|
|
begin
|
|
while assigned(stack) do
|
|
begin
|
|
hp:=stack;
|
|
stack:=hp^.next;
|
|
dispose(hp);
|
|
end;
|
|
end;
|
|
|
|
function TSymtablestack.finditem(st: TSymtable): psymtablestackitem;
|
|
begin
|
|
if not assigned(stack) then
|
|
internalerror(200601233);
|
|
result:=stack;
|
|
while assigned(result)and(result^.symtable<>st) do
|
|
result:=result^.next;
|
|
end;
|
|
|
|
procedure TSymtablestack.push(st:TSymtable);
|
|
var
|
|
hp : psymtablestackitem;
|
|
begin
|
|
new(hp);
|
|
hp^.symtable:=st;
|
|
hp^.next:=stack;
|
|
stack:=hp;
|
|
end;
|
|
|
|
procedure TSymtablestack.pushafter(st,afterst:TSymtable);
|
|
var
|
|
hp,afteritem: psymtablestackitem;
|
|
begin
|
|
afteritem:=finditem(afterst);
|
|
if assigned(afteritem) then
|
|
begin
|
|
new(hp);
|
|
hp^.symtable:=st;
|
|
hp^.next:=afteritem^.next;
|
|
afteritem^.next:=hp;
|
|
end
|
|
else
|
|
internalerror(201309171);
|
|
end;
|
|
|
|
procedure TSymtablestack.pop(st:TSymtable);
|
|
var
|
|
hp : psymtablestackitem;
|
|
begin
|
|
if not assigned(stack) then
|
|
internalerror(200601231);
|
|
if stack^.symtable<>st then
|
|
internalerror(200601232);
|
|
hp:=stack;
|
|
stack:=hp^.next;
|
|
dispose(hp);
|
|
end;
|
|
|
|
|
|
function TSymtablestack.top:TSymtable;
|
|
begin
|
|
if not assigned(stack) then
|
|
internalerror(2006012304);
|
|
result:=stack^.symtable;
|
|
end;
|
|
|
|
|
|
function addstitemreverse(st: TSymtablestack; finalst: tsymtable; curitem: psymtablestackitem): boolean;
|
|
begin
|
|
if not assigned(curitem) then
|
|
begin
|
|
result:=true;
|
|
exit;
|
|
end;
|
|
if addstitemreverse(st,finalst,curitem^.next) then
|
|
begin
|
|
st.push(curitem^.symtable);
|
|
result:=curitem^.symtable<>finalst
|
|
end
|
|
else
|
|
result:=false
|
|
end;
|
|
|
|
|
|
function TSymtablestack.getcopyuntil(finalst: TSymtable): TSymtablestack;
|
|
begin
|
|
result:=TSymtablestack.create;
|
|
addstitemreverse(result,finalst,stack);
|
|
end;
|
|
|
|
|
|
{$ifdef MEMDEBUG}
|
|
initialization
|
|
memrealnames:=TMemDebug.create('Realnames');
|
|
memrealnames.stop;
|
|
|
|
finalization
|
|
memrealnames.free;
|
|
{$endif MEMDEBUG}
|
|
end.
|