fpc/compiler/symbase.pas
2024-05-04 16:44:32 +02:00

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.