{ $Id$ Copyright (c) 1998-2000 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. **************************************************************************** } {$ifdef TP} {$N+,E+,F+,L-} {$endif} unit symtable; interface uses {$ifdef TP} {$ifndef Delphi} objects, {$endif Delphi} {$endif} strings,cobjects, globtype,globals,tokens,systems, symconst, aasm ,cpubase ,cpuinfo {$ifdef GDB} ,gdb {$endif} ; {************************************************ Some internal constants ************************************************} const hasharraysize = 256; {$ifdef TP} indexgrowsize = 16; {$else} indexgrowsize = 64; {$endif} {************************************************ Needed forward pointers ************************************************} type { needed for owner (table) of symbol } psymtable = ^tsymtable; punitsymtable = ^tunitsymtable; { needed for names by the definitions } psym = ^tsym; pdef = ^tdef; ptypesym = ^ttypesym; penumsym = ^tenumsym; pprocsym = ^tprocsym; pref = ^tref; tref = object nextref : pref; posinfo : tfileposinfo; moduleindex : word; is_written : boolean; constructor init(ref:pref;pos:pfileposinfo); procedure freechain; destructor done; virtual; end; { Deref entry options } tdereftype = (derefnil,derefaktrecordindex,derefaktstaticindex, derefunit,derefrecord,derefindex, dereflocal,derefpara,derefaktlocal); pderef = ^tderef; tderef = object dereftype : tdereftype; index : word; next : pderef; constructor init(typ:tdereftype;i:word); destructor done; end; ttype = object def : pdef; sym : psym; procedure reset; procedure setdef(p:pdef); procedure setsym(p:psym); procedure load; procedure write; procedure resolve; end; psymlistitem = ^tsymlistitem; tsymlistitem = record sym : psym; next : psymlistitem; end; psymlist = ^tsymlist; tsymlist = object def : pdef; firstsym, lastsym : psymlistitem; constructor init; constructor load; destructor done; function empty:boolean; procedure setdef(p:pdef); procedure addsym(p:psym); procedure clear; function getcopy:psymlist; procedure resolve; procedure write; end; psymtableentry = ^tsymtableentry; tsymtableentry = object(tnamedindexobject) owner : psymtable; end; {************************************************ TDef ************************************************} {$i symdefh.inc} {************************************************ TSym ************************************************} {$i symsymh.inc} {************************************************ TSymtable ************************************************} tsymtabletype = (invalidsymtable,withsymtable,staticsymtable, globalsymtable,unitsymtable, objectsymtable,recordsymtable, macrosymtable,localsymtable, parasymtable,inlineparasymtable, inlinelocalsymtable,stt_exceptsymtable, { only used for PPU reading of static part of a unit } staticppusymtable); tcallback = procedure(p : psym); tsearchhasharray = array[0..hasharraysize-1] of psym; psearchhasharray = ^tsearchhasharray; tsymtable = object symtabletype : tsymtabletype; { each symtable gets a number } unitid : word{integer give range check errors PM}; name : pstring; datasize : longint; dataalignment : longint; symindex, defindex : pindexarray; symsearch : pdictionary; next : psymtable; defowner : pdef; { for records and objects } { alignment used in this symtable } alignment : longint; { only used for parameter symtable to determine the offset relative } { to the frame pointer and for local inline } address_fixup : longint; { this saves all definition to allow a proper clean up } { separate lexlevel from symtable type } symtablelevel : byte; constructor init(t : tsymtabletype); destructor done;virtual; { access } function getdefnr(l : longint) : pdef; function getsymnr(l : longint) : psym; { load/write } constructor loadas(typ : tsymtabletype); procedure writeas; procedure loaddefs; procedure loadsyms; procedure writedefs; procedure writesyms; procedure deref; procedure clear; function rename(const olds,news : stringid):psym; procedure foreach(proc2call : tnamedindexcallback); function insert(sym : psym):psym; function search(const s : stringid) : psym; function speedsearch(const s : stringid;speedvalue : longint) : psym; procedure registerdef(p : pdef); procedure allsymbolsused; procedure allprivatesused; procedure allunitsused; procedure check_forwards; procedure checklabels; { change alignment for args only parasymtable } procedure set_alignment(_alignment : byte); { find arg having offset only parasymtable } function find_at_offset(l : longint) : pvarsym; {$ifdef CHAINPROCSYMS} procedure chainprocsyms; {$endif CHAINPROCSYMS} {$ifndef DONOTCHAINOPERATORS} procedure chainoperators; {$endif DONOTCHAINOPERATORS} procedure load_browser; procedure write_browser; {$ifdef BrowserLog} procedure writebrowserlog; {$endif BrowserLog} {$ifdef GDB} procedure concatstabto(asmlist : paasmoutput);virtual; {$endif GDB} function getnewtypecount : word; virtual; end; tunitsymtable = object(tsymtable) unittypecount : word; unitsym : punitsym; {$ifdef GDB} dbx_count : longint; prev_dbx_counter : plongint; dbx_count_ok : boolean; is_stab_written : boolean; {$endif GDB} constructor init(t : tsymtabletype;const n : string); constructor loadasunit; destructor done;virtual; procedure writeasunit; {$ifdef GDB} procedure concattypestabto(asmlist : paasmoutput); {$endif GDB} procedure load_symtable_refs; function getnewtypecount : word; virtual; end; pwithsymtable = ^twithsymtable; twithsymtable = object(tsymtable) { used for withsymtable for allowing constructors } direct_with : boolean; { in fact it is a ptree } withnode : pointer; { ptree to load of direct with var } { already usable before firstwith needed for firstpass of function parameters PM } withrefnode : pointer; constructor init; destructor done;virtual; end; {**************************************************************************** Var / Consts ****************************************************************************} const systemunit : punitsymtable = nil; { pointer to the system unit } current_object_option : tsymoptions = [sp_public]; var { for STAB debugging } globaltypecount : word; pglobaltypecount : pword; registerdef : boolean; { true, when defs should be registered } defaultsymtablestack, { symtablestack after default units have been loaded } symtablestack : psymtable; { linked list of symtables } srsym : psym; { result of the last search } srsymtable : psymtable; lastsrsym : psym; { last sym found in statement } lastsrsymtable : psymtable; lastsymknown : boolean; constsymtable : psymtable; { symtable were the constants can be inserted } voidpointerdef : ppointerdef; { pointer for Void-Pointerdef } charpointerdef : ppointerdef; { pointer for Char-Pointerdef } voidfarpointerdef : ppointerdef; cformaldef : pformaldef; { unique formal definition } voiddef : porddef; { Pointer to Void (procedure) } cchardef : porddef; { Pointer to Char } cwidechardef : porddef; { Pointer to WideChar } booldef : porddef; { pointer to boolean type } u8bitdef : porddef; { Pointer to 8-Bit unsigned } u16bitdef : porddef; { Pointer to 16-Bit unsigned } u32bitdef : porddef; { Pointer to 32-Bit unsigned } s32bitdef : porddef; { Pointer to 32-Bit signed } cu64bitdef : porddef; { pointer to 64 bit unsigned def } cs64bitdef : porddef; { pointer to 64 bit signed def, } { calculated by the int unit on i386 } s32floatdef : pfloatdef; { pointer for realconstn } s64floatdef : pfloatdef; { pointer for realconstn } s80floatdef : pfloatdef; { pointer to type of temp. floats } s32fixeddef : pfloatdef; { pointer to type of temp. fixed } cshortstringdef : pstringdef; { pointer to type of short string const } clongstringdef : pstringdef; { pointer to type of long string const } cansistringdef : pstringdef; { pointer to type of ansi string const } cwidestringdef : pstringdef; { pointer to type of wide string const } openshortstringdef : pstringdef; { pointer to type of an open shortstring, needed for readln() } openchararraydef : parraydef; { pointer to type of an open array of char, needed for readln() } cfiledef : pfiledef; { get the same definition for all file } { uses for stabs } firstglobaldef, { linked list of all globals defs } lastglobaldef : pdef; { used to reset stabs/ranges } class_tobject : pobjectdef; { pointer to the anchestor of all } { clases } pvmtdef : ppointerdef; { type of classrefs } aktprocsym : pprocsym; { pointer to the symbol for the currently be parsed procedure } aktcallprocsym : pprocsym; { pointer to the symbol for the currently be called procedure, only set/unset in firstcall } aktvarsym : pvarsym; { pointer to the symbol for the currently read var, only used for variable directives } procprefix : string; { eindeutige Namen bei geschachtel- } { ten Unterprogrammen erzeugen } lexlevel : longint; { level of code } { 1 for main procedure } { 2 for normal function or proc } { higher for locals } const main_program_level = 1; unit_init_level = 1; normal_function_level = 2; in_loading : boolean = false; {$ifdef i386} bestrealdef : ^pfloatdef = @s80floatdef; {$endif} {$ifdef m68k} bestrealdef : ^pfloatdef = @s64floatdef; {$endif} {$ifdef alpha} bestrealdef : ^pfloatdef = @s64floatdef; {$endif} {$ifdef powerpc} bestrealdef : ^pfloatdef = @s64floatdef; {$endif} var macros : psymtable; { pointer for die Symboltabelle mit } { Makros } read_member : boolean; { true, wenn Members aus einer PPU- } { Datei gelesen werden, d.h. ein } { varsym seine Adresse einlesen soll } generrorsym : psym; { Jokersymbol, wenn das richtige } { Symbol nicht gefunden wird } generrordef : pdef; { Jokersymbol for eine fehlerhafte } { Typdefinition } aktobjectdef : pobjectdef; { used for private functions check !! } const { last operator which can be overloaded } first_overloaded = _PLUS; last_overloaded = _ASSIGNMENT; type toverloaded_operators = array[first_overloaded..last_overloaded] of pprocsym; var overloaded_operators : toverloaded_operators; { unequal is not equal} const overloaded_names : array [first_overloaded..last_overloaded] of string[16] = ('plus','minus','star','slash','equal', 'greater','lower','greater_or_equal', 'lower_or_equal', 'sym_diff','starstar', 'as','is','in','or', 'and','div','mod','shl','shr','xor', 'assign'); {$ifdef UNITALIASES} type punit_alias = ^tunit_alias; tunit_alias = object(tnamedindexobject) newname : pstring; constructor init(const n:string); destructor done;virtual; end; var unitaliases : pdictionary; procedure addunitalias(const n:string); function getunitalias(const n:string):string; {$endif UNITALIASES} {**************************************************************************** Functions ****************************************************************************} {*** Misc ***} function globaldef(const s : string) : pdef; function findunitsymtable(st:psymtable):psymtable; procedure duplicatesym(sym:psym); {*** Search ***} function search_a_symtable(const symbol:string;symtabletype:tsymtabletype):Psym; procedure getsym(const s : stringid;notfounderror : boolean); procedure getsymonlyin(p : psymtable;const s : stringid); {*** PPU Write/Loading ***} procedure writeunitas(const s : string;unittable : punitsymtable;only_crc : boolean); procedure closecurrentppu; procedure numberunits; procedure load_interface; {*** GDB ***} {$ifdef GDB} function typeglobalnumber(const s : string) : string; {$endif} {*** Definition ***} procedure reset_global_defs; {*** Object Helpers ***} function search_class_member(pd : pobjectdef;const n : string) : psym; function search_default_property(pd : pobjectdef) : ppropertysym; {*** Macro ***} procedure def_macro(const s : string); procedure set_macro(const s : string;value : string); {*** symtable stack ***} procedure dellexlevel; procedure RestoreUnitSyms; {$ifdef DEBUG} procedure test_symtablestack; procedure list_symtablestack; {$endif DEBUG} {*** Init / Done ***} procedure InitSymtable; procedure DoneSymtable; implementation uses version,verbose, types,ppu, gendef,files ,tree ,cresstr {$ifdef newcg} ,cgbase {$else} ,hcodegen {$endif} {$ifdef BrowserLog} ,browlog {$endif BrowserLog} ,cpuasm ; var aktrecordsymtable : psymtable; { current record read from ppu symtable } aktstaticsymtable : psymtable; { current static for local ppu symtable } aktlocalsymtable : psymtable; { current proc local for local ppu symtable } {$ifdef GDB} asmoutput : paasmoutput; {$endif GDB} {$ifdef TP} {$ifndef Delphi} {$ifndef dpmi} symbolstream : temsstream; { stream which is used to store some info } {$else} symbolstream : tmemorystream; {$endif} {$endif Delphi} {$endif} {to dispose the global symtable of a unit } const dispose_global : boolean = false; memsizeinc = 2048; { for long stabstrings } tagtypes : Set of tdeftype = [recorddef,enumdef, {$IfNDef GDBKnowsStrings} stringdef, {$EndIf not GDBKnowsStrings} {$IfNDef GDBKnowsFiles} filedef, {$EndIf not GDBKnowsFiles} objectdef]; {***************************************************************************** Helper Routines *****************************************************************************} {$ifdef unused} function demangledparas(s : string) : string; var r : string; l : longint; begin demangledparas:=''; r:=','; { delete leading $$'s } l:=pos('$$',s); while l<>0 do begin delete(s,1,l+1); l:=pos('$$',s); end; { delete leading _$'s } l:=pos('_$',s); while l<>0 do begin delete(s,1,l+1); l:=pos('_$',s); end; l:=pos('$',s); if l=0 then exit; delete(s,1,l); while s<>'' do begin l:=pos('$',s); if l=0 then l:=length(s)+1; r:=r+copy(s,1,l-1)+','; delete(s,1,l); end; delete(r,1,1); delete(r,length(r),1); demangledparas:=r; end; {$endif} procedure numberunits; var counter : longint; hp : pused_unit; hp1 : pmodule; begin { Reset all numbers to -1 } hp1:=pmodule(loaded_units.first); while assigned(hp1) do begin if assigned(hp1^.globalsymtable) then psymtable(hp1^.globalsymtable)^.unitid:=$ffff; hp1:=pmodule(hp1^.next); end; { Our own symtable gets unitid 0, for a program there is no globalsymtable } if assigned(current_module^.globalsymtable) then psymtable(current_module^.globalsymtable)^.unitid:=0; { number units } counter:=1; hp:=pused_unit(current_module^.used_units.first); while assigned(hp) do begin psymtable(hp^.u^.globalsymtable)^.unitid:=counter; inc(counter); hp:=pused_unit(hp^.next); end; end; function findunitsymtable(st:psymtable):psymtable; begin findunitsymtable:=nil; repeat if not assigned(st) then internalerror(5566561); case st^.symtabletype of localsymtable, parasymtable, staticsymtable : break; globalsymtable, unitsymtable : begin findunitsymtable:=st; break; end; objectsymtable, recordsymtable : st:=st^.defowner^.owner; else internalerror(5566562); end; until false; end; procedure setstring(var p : pchar;const s : string); begin {$ifndef Delphi} {$ifdef TP} if use_big then begin p:=pchar(symbolstream.getsize); symbolstream.seek(longint(p)); symbolstream.writestr(@s); end else {$endif TP} {$endif Delphi} p:=strpnew(s); end; procedure duplicatesym(sym:psym); var st : psymtable; begin Message1(sym_e_duplicate_id,sym^.name); st:=findunitsymtable(sym^.owner); with sym^.fileinfo do begin if assigned(st) and (st^.unitid<>0) then Message2(sym_h_duplicate_id_where,'unit '+st^.name^,tostr(line)) else Message2(sym_h_duplicate_id_where,current_module^.sourcefiles^.get_file_name(fileindex),tostr(line)); end; end; {***************************************************************************** PPU Reading Writing *****************************************************************************} {$I symppu.inc} {**************************************************************************** TDeref ****************************************************************************} constructor tderef.init(typ:tdereftype;i:word); begin dereftype:=typ; index:=i; next:=nil; end; destructor tderef.done; begin end; {***************************************************************************** Symbol / Definition Resolving *****************************************************************************} procedure resolvederef(var p:pderef;var st:psymtable;var idx:word); var hp : pderef; pd : pdef; begin st:=nil; idx:=0; while assigned(p) do begin case p^.dereftype of derefaktrecordindex : begin st:=aktrecordsymtable; idx:=p^.index; end; derefaktstaticindex : begin st:=aktstaticsymtable; idx:=p^.index; end; derefaktlocal : begin st:=aktlocalsymtable; idx:=p^.index; end; derefunit : begin {$ifdef NEWMAP} st:=psymtable(current_module^.map^[p^.index]^.globalsymtable); {$else NEWMAP} st:=psymtable(current_module^.map^[p^.index]); {$endif NEWMAP} end; derefrecord : begin pd:=st^.getdefnr(p^.index); case pd^.deftype of recorddef : st:=precorddef(pd)^.symtable; objectdef : st:=pobjectdef(pd)^.symtable; else internalerror(556658); end; end; dereflocal : begin pd:=st^.getdefnr(p^.index); case pd^.deftype of procdef : st:=pprocdef(pd)^.localst; else internalerror(556658); end; end; derefpara : begin pd:=st^.getdefnr(p^.index); case pd^.deftype of procdef : st:=pprocdef(pd)^.parast; else internalerror(556658); end; end; derefindex : begin idx:=p^.index; end; else internalerror(556658); end; hp:=p; p:=p^.next; dispose(hp,done); end; end; procedure resolvedef(var def:pdef); var st : psymtable; idx : word; begin resolvederef(pderef(def),st,idx); if assigned(st) then def:=st^.getdefnr(idx) else def:=nil; end; procedure resolvesym(var sym:psym); var st : psymtable; idx : word; begin resolvederef(pderef(sym),st,idx); if assigned(st) then sym:=st^.getsymnr(idx) else sym:=nil; end; {**************************************************************************** TRef ****************************************************************************} constructor tref.init(ref :pref;pos : pfileposinfo); begin nextref:=nil; if pos<>nil then posinfo:=pos^; if assigned(current_module) then moduleindex:=current_module^.unit_index; if assigned(ref) then ref^.nextref:=@self; is_written:=false; end; procedure tref.freechain; var p,q : pref; begin p:=nextref; nextref:=nil; while assigned(p) do begin q:=p^.nextref; dispose(p,done); p:=q; end; end; destructor tref.done; var inputfile : pinputfile; begin inputfile:=get_source_file(moduleindex,posinfo.fileindex); if inputfile<>nil then dec(inputfile^.ref_count); nextref:=nil; end; {**************************************************************************** TType ****************************************************************************} procedure ttype.reset; begin def:=nil; sym:=nil; end; procedure ttype.setdef(p:pdef); begin def:=p; sym:=nil; end; procedure ttype.setsym(p:psym); begin sym:=p; case p^.typ of typesym : def:=ptypesym(p)^.restype.def; propertysym : def:=ppropertysym(p)^.proptype.def; else internalerror(1234005); end; end; procedure ttype.load; begin def:=pdef(readderef); sym:=psym(readderef); end; procedure ttype.write; begin if assigned(sym) then begin writederef(nil); writederef(sym); end else begin writederef(def); writederef(nil); end; end; procedure ttype.resolve; begin if assigned(sym) then begin resolvesym(sym); setsym(sym); end else resolvedef(def); end; {**************************************************************************** TSymList ****************************************************************************} constructor tsymlist.init; begin def:=nil; { needed for procedures } firstsym:=nil; lastsym:=nil; end; constructor tsymlist.load; var sym : psym; begin def:=readdefref; firstsym:=nil; lastsym:=nil; repeat sym:=readsymref; if sym=nil then break; addsym(sym); until false; end; destructor tsymlist.done; begin clear; end; function tsymlist.empty:boolean; begin empty:=(firstsym=nil); end; procedure tsymlist.clear; var hp : psymlistitem; begin while assigned(firstsym) do begin hp:=firstsym; firstsym:=firstsym^.next; dispose(hp); end; firstsym:=nil; lastsym:=nil; def:=nil; end; procedure tsymlist.setdef(p:pdef); begin def:=p; end; procedure tsymlist.addsym(p:psym); var hp : psymlistitem; begin if not assigned(p) then exit; new(hp); hp^.sym:=p; hp^.next:=nil; if assigned(lastsym) then lastsym^.next:=hp else firstsym:=hp; lastsym:=hp; end; function tsymlist.getcopy:psymlist; var hp : psymlist; hp2 : psymlistitem; begin new(hp,init); hp^.def:=def; hp2:=firstsym; while assigned(hp2) do begin hp^.addsym(hp2^.sym); hp2:=hp2^.next; end; getcopy:=hp; end; procedure tsymlist.write; var hp : psymlistitem; begin writederef(def); hp:=firstsym; while assigned(hp) do begin writederef(hp^.sym); hp:=hp^.next; end; writederef(nil); end; procedure tsymlist.resolve; var hp : psymlistitem; begin resolvedef(def); hp:=firstsym; while assigned(hp) do begin resolvesym(hp^.sym); hp:=hp^.next; end; end; {***************************************************************************** Definition Helpers *****************************************************************************} function globaldef(const s : string) : pdef; var st : string; symt : psymtable; begin srsym := nil; if pos('.',s) > 0 then begin st := copy(s,1,pos('.',s)-1); getsym(st,false); st := copy(s,pos('.',s)+1,255); if assigned(srsym) then begin if srsym^.typ = unitsym then begin symt := punitsym(srsym)^.unitsymtable; srsym := symt^.search(st); end else srsym := nil; end; end else st := s; if srsym = nil then getsym(st,false); if srsym = nil then getsymonlyin(systemunit,st); if srsym^.typ<>typesym then begin Message(type_e_type_id_expected); exit; end; globaldef := ptypesym(srsym)^.restype.def; end; {***************************************************************************** Symbol Call Back Functions *****************************************************************************} procedure derefsym(p : pnamedindexobject); begin psym(p)^.deref; end; procedure check_forward(sym : pnamedindexobject); begin if psym(sym)^.typ=procsym then pprocsym(sym)^.check_forward { check also object method table } { we needn't to test the def list } { because each object has to have a type sym } else if (psym(sym)^.typ=typesym) and assigned(ptypesym(sym)^.restype.def) and (ptypesym(sym)^.restype.def^.deftype=objectdef) then pobjectdef(ptypesym(sym)^.restype.def)^.check_forwards; end; procedure labeldefined(p : pnamedindexobject); begin if (psym(p)^.typ=labelsym) and not(plabelsym(p)^.defined) then begin if plabelsym(p)^.used then Message1(sym_e_label_used_and_not_defined,p^.name) else Message1(sym_w_label_not_defined,p^.name); end; end; procedure unitsymbolused(p : pnamedindexobject); begin if (psym(p)^.typ=unitsym) and (punitsym(p)^.refs=0) and { do not claim for unit name itself !! } (punitsym(p)^.unitsymtable^.symtabletype=unitsymtable) then MessagePos2(psym(p)^.fileinfo,sym_n_unit_not_used, p^.name,current_module^.modulename^); end; procedure varsymbolused(p : pnamedindexobject); begin if (psym(p)^.typ=varsym) and ((psym(p)^.owner^.symtabletype in [parasymtable,localsymtable,objectsymtable,staticsymtable])) then begin { unused symbol should be reported only if no } { error is reported } { if the symbol is in a register it is used } { also don't count the value parameters which have local copies } { also don't claim for high param of open parameters (PM) } if (Errorcount<>0) or (copy(p^.name,1,3)='val') or (copy(p^.name,1,4)='high') then exit; if (pvarsym(p)^.refs=0) then begin if (psym(p)^.owner^.symtabletype=parasymtable) or (vo_is_local_copy in pvarsym(p)^.varoptions) then begin MessagePos1(psym(p)^.fileinfo,sym_h_para_identifier_not_used,p^.name); end else if (psym(p)^.owner^.symtabletype=objectsymtable) then MessagePos2(psym(p)^.fileinfo,sym_n_private_identifier_not_used,psym(p)^.owner^.name^,p^.name) else MessagePos1(psym(p)^.fileinfo,sym_n_local_identifier_not_used,p^.name); end else if pvarsym(p)^.varstate=vs_assigned then begin if (psym(p)^.owner^.symtabletype=parasymtable) then begin if (pvarsym(p)^.varspez<>vs_var) then MessagePos1(psym(p)^.fileinfo,sym_h_para_identifier_only_set,p^.name) end else if (vo_is_local_copy in pvarsym(p)^.varoptions) then begin if (pvarsym(p)^.varspez<>vs_var) then MessagePos1(psym(p)^.fileinfo,sym_h_para_identifier_only_set,p^.name); end else if (psym(p)^.owner^.symtabletype=objectsymtable) then MessagePos2(psym(p)^.fileinfo,sym_n_private_identifier_only_set,psym(p)^.owner^.name^,p^.name) else if (psym(p)^.owner^.symtabletype<>parasymtable) then MessagePos1(psym(p)^.fileinfo,sym_n_local_identifier_only_set,p^.name); end; end else if ((psym(p)^.owner^.symtabletype in [objectsymtable,parasymtable,localsymtable,staticsymtable])) then begin if (Errorcount<>0) then exit; { do not claim for inherited private fields !! } if (psym(p)^.refs=0) and (psym(p)^.owner^.symtabletype=objectsymtable) then MessagePos2(psym(p)^.fileinfo,sym_n_private_method_not_used,psym(p)^.owner^.name^,p^.name) { units references are problematic } else if (psym(p)^.refs=0) and not(psym(p)^.typ in [funcretsym,enumsym,unitsym]) then if (psym(p)^.typ<>procsym) or not (pprocsym(p)^.is_global) or { all program functions are declared global but unused should still be signaled PM } ((psym(p)^.owner^.symtabletype=staticsymtable) and not current_module^.is_unit) then MessagePos2(psym(p)^.fileinfo,sym_h_local_symbol_not_used,SymTypeName[psym(p)^.typ],p^.name); end; end; procedure TestPrivate(p : pnamedindexobject); begin if sp_private in psym(p)^.symoptions then varsymbolused(p); end; procedure objectprivatesymbolused(p : pnamedindexobject); begin { Don't test simple object aliases PM } if (psym(p)^.typ=typesym) and (ptypesym(p)^.restype.def^.deftype=objectdef) and (ptypesym(p)^.restype.def^.typesym=ptypesym(p)) then pobjectdef(ptypesym(p)^.restype.def)^.symtable^.foreach( {$ifndef TP}@{$endif}TestPrivate); end; {$ifdef GDB} procedure concatstab(p : pnamedindexobject); begin if psym(p)^.typ <> procsym then psym(p)^.concatstabto(asmoutput); end; procedure resetstab(p : pnamedindexobject); begin if psym(p)^.typ <> procsym then psym(p)^.isstabwritten:=false; end; procedure concattypestab(p : pnamedindexobject); begin if psym(p)^.typ = typesym then begin psym(p)^.isstabwritten:=false; psym(p)^.concatstabto(asmoutput); end; end; procedure forcestabto(asmlist : paasmoutput; pd : pdef); begin if not pd^.is_def_stab_written then begin if assigned(pd^.typesym) then pd^.typesym^.isusedinstab := true; pd^.concatstabto(asmlist); end; end; {$endif} {$ifdef CHAINPROCSYMS} procedure chainprocsym(p : psym); var storesymtablestack : psymtable; begin if p^.typ=procsym then begin storesymtablestack:=symtablestack; symtablestack:=p^.owner^.next; while assigned(symtablestack) do begin { search for same procsym in other units } getsym(p^.name,false); if assigned(srsym) and (srsym^.typ=procsym) then begin pprocsym(p)^.nextprocsym:=pprocsym(srsym); symtablestack:=storesymtablestack; exit; end else if srsym=nil then symtablestack:=nil else symtablestack:=srsymtable^.next; end; symtablestack:=storesymtablestack; end; end; {$endif} {$ifndef DONOTCHAINOPERATORS} procedure tsymtable.chainoperators; var p : pprocsym; t : ttoken; def : pprocdef; storesymtablestack : psymtable; begin storesymtablestack:=symtablestack; symtablestack:=@self; make_ref:=false; for t:=first_overloaded to last_overloaded do begin p:=nil; def:=nil; overloaded_operators[t]:=nil; { each operator has a unique lowercased internal name PM } while assigned(symtablestack) do begin getsym(overloaded_names[t],false); if (t=_STARSTAR) and (srsym=nil) then begin symtablestack:=systemunit; getsym('POWER',false); end; if assigned(srsym) then begin if (srsym^.typ<>procsym) then internalerror(12344321); if assigned(p) then begin {$ifdef CHAINPROCSYMS} p^.nextprocsym:=pprocsym(srsym); {$endif CHAINPROCSYMS} def^.nextoverloaded:=pprocsym(srsym)^.definition; end else overloaded_operators[t]:=pprocsym(srsym); p:=pprocsym(srsym); def:=p^.definition; while assigned(def^.nextoverloaded) and (def^.nextoverloaded^.owner=p^.owner) do def:=def^.nextoverloaded; def^.nextoverloaded:=nil; symtablestack:=srsymtable^.next; end else begin symtablestack:=nil; {$ifdef CHAINPROCSYMS} if assigned(p) then p^.nextprocsym:=nil; {$endif CHAINPROCSYMS} end; { search for same procsym in other units } end; symtablestack:=@self; end; make_ref:=true; symtablestack:=storesymtablestack; end; {$endif DONOTCHAINOPERATORS} procedure write_refs(sym : pnamedindexobject); begin psym(sym)^.write_references; end; {$ifdef BrowserLog} procedure add_to_browserlog(sym : pnamedindexobject); begin psym(sym)^.add_to_browserlog; end; {$endif UseBrowser} {***************************************************************************** Search Symtables for Syms *****************************************************************************} procedure getsym(const s : stringid;notfounderror : boolean); var speedvalue : longint; begin speedvalue:=getspeedvalue(s); lastsrsym:=nil; srsymtable:=symtablestack; while assigned(srsymtable) do begin srsym:=srsymtable^.speedsearch(s,speedvalue); if assigned(srsym) then exit else srsymtable:=srsymtable^.next; end; if notfounderror then begin Message1(sym_e_id_not_found,s); srsym:=generrorsym; end else srsym:=nil; end; procedure getsymonlyin(p : psymtable;const s : stringid); begin { the caller have to take care if srsym=nil (FK) } srsym:=nil; if assigned(p) then begin srsymtable:=p; srsym:=srsymtable^.search(s); if assigned(srsym) then exit else begin if (punitsymtable(srsymtable)=punitsymtable(current_module^.globalsymtable)) then begin getsymonlyin(psymtable(current_module^.localsymtable),s); if assigned(srsym) then srsymtable:=psymtable(current_module^.localsymtable) else Message1(sym_e_id_not_found,s); end else Message1(sym_e_id_not_found,s); end; end; end; function search_a_symtable(const symbol:string;symtabletype:tsymtabletype):Psym; {Search for a symbol in a specified symbol table. Returns nil if the symtable is not found, and also if the symbol cannot be found in the desired symtable } var hsymtab:Psymtable; res:Psym; begin res:=nil; hsymtab:=symtablestack; while (hsymtab<>nil) and (hsymtab^.symtabletype<>symtabletype) do hsymtab:=hsymtab^.next; if hsymtab<>nil then {We found the desired symtable. Now check if the symbol we search for is defined in it } res:=hsymtab^.search(symbol); search_a_symtable:=res; end; {**************************************************************************** TSYMTABLE ****************************************************************************} constructor tsymtable.init(t : tsymtabletype); begin symtabletype:=t; symtablelevel:=0; defowner:=nil; unitid:=0; next:=nil; name:=nil; address_fixup:=0; datasize:=0; dataalignment:=1; new(symindex,init(indexgrowsize)); new(defindex,init(indexgrowsize)); if symtabletype<>withsymtable then begin new(symsearch,init); symsearch^.noclear:=true; end else symsearch:=nil; alignment:=def_alignment; end; destructor tsymtable.done; begin stringdispose(name); dispose(symindex,done); dispose(defindex,done); { symsearch can already be disposed or set to nil for withsymtable } if assigned(symsearch) then begin dispose(symsearch,done); symsearch:=nil; end; end; constructor twithsymtable.init; begin inherited init(withsymtable); direct_with:=false; withnode:=nil; withrefnode:=nil; end; destructor twithsymtable.done; begin symsearch:=nil; inherited done; end; {*********************************************** Helpers ***********************************************} function tsymtable.getnewtypecount : word; begin getnewtypecount:=pglobaltypecount^; inc(pglobaltypecount^); end; procedure tsymtable.registerdef(p : pdef); begin defindex^.insert(p); { set def owner and indexnb } p^.owner:=@self; end; procedure order_overloads(p : Pnamedindexobject); begin if psym(p)^.typ=procsym then pprocsym(p)^.order_overloaded; end; procedure tsymtable.foreach(proc2call : tnamedindexcallback); begin symindex^.foreach(proc2call); end; {*********************************************** LOAD / WRITE SYMTABLE FROM PPU ***********************************************} procedure tsymtable.loaddefs; var hp : pdef; b : byte; begin { load start of definition section, which holds the amount of defs } if current_ppu^.readentry<>ibstartdefs then Message(unit_f_ppu_read_error); current_ppu^.getlongint; { read definitions } repeat b:=current_ppu^.readentry; case b of ibpointerdef : hp:=new(ppointerdef,load); ibarraydef : hp:=new(parraydef,load); iborddef : hp:=new(porddef,load); ibfloatdef : hp:=new(pfloatdef,load); ibprocdef : hp:=new(pprocdef,load); ibshortstringdef : hp:=new(pstringdef,shortload); iblongstringdef : hp:=new(pstringdef,longload); ibansistringdef : hp:=new(pstringdef,ansiload); ibwidestringdef : hp:=new(pstringdef,wideload); ibrecorddef : hp:=new(precorddef,load); ibobjectdef : hp:=new(pobjectdef,load); ibenumdef : hp:=new(penumdef,load); ibsetdef : hp:=new(psetdef,load); ibprocvardef : hp:=new(pprocvardef,load); ibfiledef : hp:=new(pfiledef,load); ibclassrefdef : hp:=new(pclassrefdef,load); ibformaldef : hp:=new(pformaldef,load); ibenddefs : break; ibend : Message(unit_f_ppu_read_error); else Message1(unit_f_ppu_invalid_entry,tostr(b)); end; hp^.owner:=@self; defindex^.insert(hp); until false; end; procedure tsymtable.loadsyms; var b : byte; sym : psym; begin { load start of definition section, which holds the amount of defs } if current_ppu^.readentry<>ibstartsyms then Message(unit_f_ppu_read_error); { skip amount of symbols, not used currently } current_ppu^.getlongint; { load datasize,dataalignment of this symboltable } datasize:=current_ppu^.getlongint; dataalignment:=current_ppu^.getlongint; { now read the symbols } repeat b:=current_ppu^.readentry; case b of ibtypesym : sym:=new(ptypesym,load); ibprocsym : sym:=new(pprocsym,load); ibconstsym : sym:=new(pconstsym,load); ibvarsym : sym:=new(pvarsym,load); ibfuncretsym : sym:=new(pfuncretsym,load); ibabsolutesym : sym:=new(pabsolutesym,load); ibenumsym : sym:=new(penumsym,load); ibtypedconstsym : sym:=new(ptypedconstsym,load); ibpropertysym : sym:=new(ppropertysym,load); ibunitsym : sym:=new(punitsym,load); iblabelsym : sym:=new(plabelsym,load); ibsyssym : sym:=new(psyssym,load); ibendsyms : break; ibend : Message(unit_f_ppu_read_error); else Message1(unit_f_ppu_invalid_entry,tostr(b)); end; sym^.owner:=@self; symindex^.insert(sym); symsearch^.insert(sym); until false; end; procedure tsymtable.writedefs; var pd : pdef; begin { each definition get a number, write then the amount of defs to the ibstartdef entry } current_ppu^.putlongint(defindex^.count); current_ppu^.writeentry(ibstartdefs); { now write the definition } pd:=pdef(defindex^.first); while assigned(pd) do begin pd^.write; pd:=pdef(pd^.next); end; { write end of definitions } current_ppu^.writeentry(ibenddefs); end; procedure tsymtable.writesyms; var pd : psym; begin { each definition get a number, write then the amount of syms and the datasize to the ibsymdef entry } current_ppu^.putlongint(symindex^.count); current_ppu^.putlongint(datasize); current_ppu^.putlongint(dataalignment); current_ppu^.writeentry(ibstartsyms); { foreach is used to write all symbols } pd:=psym(symindex^.first); while assigned(pd) do begin pd^.write; pd:=psym(pd^.next); end; { end of symbols } current_ppu^.writeentry(ibendsyms); end; procedure tsymtable.deref; var hp : pdef; hs : psym; begin { first deref the ttypesyms } hs:=psym(symindex^.first); while assigned(hs) do begin hs^.prederef; hs:=psym(hs^.next); end; { deref the definitions } hp:=pdef(defindex^.first); while assigned(hp) do begin hp^.deref; hp:=pdef(hp^.next); end; { deref the symbols } hs:=psym(symindex^.first); while assigned(hs) do begin hs^.deref; hs:=psym(hs^.next); end; end; constructor tsymtable.loadas(typ : tsymtabletype); var storesymtable : psymtable; st_loading : boolean; begin st_loading:=in_loading; in_loading:=true; symtabletype:=typ; new(symindex,init(indexgrowsize)); new(defindex,init(indexgrowsize)); new(symsearch,init); symsearch^.noclear:=true; { reset } defowner:=nil; name:=nil; alignment:=def_alignment; datasize:=0; address_fixup:= 0; unitid:=0; { setup symtabletype specific things } case typ of unitsymtable : begin symtablelevel:=0; {$ifndef NEWMAP} current_module^.map^[0]:=@self; {$else NEWMAP} current_module^.globalsymtable:=@self; {$endif NEWMAP} end; recordsymtable, objectsymtable : begin storesymtable:=aktrecordsymtable; aktrecordsymtable:=@self; end; parasymtable, localsymtable : begin storesymtable:=aktlocalsymtable; aktlocalsymtable:=@self; end; { used for local browser } staticppusymtable : begin aktstaticsymtable:=@self; symsearch^.usehash; end; end; { we need the correct symtable for registering } if not (typ in [localsymtable,parasymtable,recordsymtable,objectsymtable]) then begin next:=symtablestack; symtablestack:=@self; end; { load definitions } loaddefs; { load symbols } loadsyms; if not (typ in [localsymtable,parasymtable,recordsymtable,objectsymtable]) then begin { now we can deref the syms and defs } deref; { restore symtablestack } symtablestack:=next; end; case typ of unitsymtable : begin {$ifdef NEWMAP} { necessary for dependencies } current_module^.globalsymtable:=nil; {$endif NEWMAP} end; recordsymtable, objectsymtable : aktrecordsymtable:=storesymtable; localsymtable, parasymtable : aktlocalsymtable:=storesymtable; end; in_loading:=st_loading; end; procedure tsymtable.writeas; var oldtyp : byte; storesymtable : psymtable; begin storesymtable:=aktrecordsymtable; case symtabletype of recordsymtable, objectsymtable : begin storesymtable:=aktrecordsymtable; aktrecordsymtable:=@self; oldtyp:=current_ppu^.entrytyp; current_ppu^.entrytyp:=subentryid; end; parasymtable, localsymtable : begin storesymtable:=aktlocalsymtable; aktlocalsymtable:=@self; end; end; { order procsym overloads } foreach({$ifndef TP}@{$endif}Order_overloads); { write definitions } writedefs; { write symbols } writesyms; case symtabletype of recordsymtable, objectsymtable : begin current_ppu^.entrytyp:=oldtyp; aktrecordsymtable:=storesymtable; end; localsymtable, parasymtable : aktlocalsymtable:=storesymtable; end; end; {*********************************************** Get Symbol / Def by Number ***********************************************} function tsymtable.getsymnr(l : longint) : psym; var hp : psym; begin hp:=psym(symindex^.search(l)); if hp=nil then internalerror(10999); getsymnr:=hp; end; function tsymtable.getdefnr(l : longint) : pdef; var hp : pdef; begin hp:=pdef(defindex^.search(l)); if hp=nil then internalerror(10998); getdefnr:=hp; end; {*********************************************** Table Access ***********************************************} procedure tsymtable.clear; begin { remove no entry from a withsymtable as it is only a pointer to the recorddef or objectdef symtable } if symtabletype=withsymtable then exit; symindex^.clear; defindex^.clear; end; function tsymtable.insert(sym:psym):psym; var hp : psymtable; hsym : psym; begin { set owner and sym indexnb } sym^.owner:=@self; {$ifdef CHAINPROCSYMS} { set the nextprocsym field } if sym^.typ=procsym then chainprocsym(sym); {$endif CHAINPROCSYMS} { writes the symbol in data segment if required } { also sets the datasize of owner } if not in_loading then sym^.insert_in_data; if (symtabletype in [staticsymtable,globalsymtable]) then begin hp:=symtablestack; while assigned(hp) do begin if hp^.symtabletype in [staticsymtable,globalsymtable] then begin hsym:=hp^.search(sym^.name); if assigned(hsym) then DuplicateSym(hsym); end; hp:=hp^.next; end; end; { check the current symtable } hsym:=search(sym^.name); if assigned(hsym) then DuplicateSym(hsym); { check for duplicate id in local and parasymtable symtable } if (symtabletype=localsymtable) then { to be on the save side: } begin if assigned(next) and (next^.symtabletype=parasymtable) then begin hsym:=next^.search(sym^.name); { a parameter and the function can have the same } { name in TP and Delphi } if assigned(hsym) then begin if (sym^.typ<>funcretsym) then DuplicateSym(hsym) else begin sym^.setname('hidden'+sym^.name); end; end; end else if (current_module^.flags and uf_local_browser)=0 then internalerror(43789); end; { check for duplicate id in local symtable of methods } if (symtabletype=localsymtable) and assigned(next) and assigned(next^.next) and { funcretsym is allowed !! } (sym^.typ <> funcretsym) and (next^.next^.symtabletype=objectsymtable) then begin hsym:=search_class_member(pobjectdef(next^.next^.defowner),sym^.name); if assigned(hsym) and { private ids can be reused } (not(sp_private in hsym^.symoptions) or (hsym^.owner^.defowner^.owner^.symtabletype<>unitsymtable)) then begin { delphi allows to reuse the names of properties } { in procedures } if not((hsym^.typ=propertysym) and (m_delphi in aktmodeswitches)) then DuplicateSym(hsym); end; end; { check for duplicate id in para symtable of methods } if (symtabletype=parasymtable) and assigned(procinfo^._class) and { but not in nested procedures !} (not(assigned(procinfo^.parent)) or (assigned(procinfo^.parent) and not(assigned(procinfo^.parent^._class))) ) and { funcretsym is allowed !! } (sym^.typ <> funcretsym) then begin hsym:=search_class_member(procinfo^._class,sym^.name); if assigned(hsym) and { private ids can be reused } (not(sp_private in hsym^.symoptions) or (hsym^.owner^.defowner^.owner^.symtabletype<>unitsymtable)) then begin { delphi allows to reuse the names of properties } { in parameter lists of methods } if not((hsym^.typ=propertysym) and (m_delphi in aktmodeswitches)) then DuplicateSym(hsym); end; end; { check for duplicate field id in inherited classes } if (sym^.typ=varsym) and (symtabletype=objectsymtable) and assigned(defowner) then begin hsym:=search_class_member(pobjectdef(defowner),sym^.name); { but private ids can be reused } if assigned(hsym) and (not(sp_private in hsym^.symoptions) or (hsym^.owner^.defowner^.owner^.symtabletype<>unitsymtable)) then DuplicateSym(hsym); end; { register definition of typesym } if (sym^.typ = typesym) and assigned(ptypesym(sym)^.restype.def) then begin if not(assigned(ptypesym(sym)^.restype.def^.owner)) and (ptypesym(sym)^.restype.def^.deftype<>errordef) then registerdef(ptypesym(sym)^.restype.def); {$ifdef GDB} if (cs_debuginfo in aktmoduleswitches) and assigned(debuglist) and (symtabletype in [globalsymtable,staticsymtable]) then begin ptypesym(sym)^.isusedinstab := true; sym^.concatstabto(debuglist); end; {$endif GDB} end; { insert in index and search hash } symindex^.insert(sym); symsearch^.insert(sym); insert:=sym; end; function tsymtable.search(const s : stringid) : psym; begin {search:=psym(symsearch^.search(s)); this bypasses the ref generation (PM) } search:=speedsearch(s,getspeedvalue(s)); end; function tsymtable.speedsearch(const s : stringid;speedvalue : longint) : psym; var hp : psym; newref : pref; begin hp:=psym(symsearch^.speedsearch(s,speedvalue)); if assigned(hp) then begin { reject non static members in static procedures, be carefull aktprocsym^.definition is not allways loaded already (PFV) } if (symtabletype=objectsymtable) and not(sp_static in hp^.symoptions) and allow_only_static {assigned(aktprocsym) and assigned(aktprocsym^.definition) and ((aktprocsym^.definition^.options and postaticmethod)<>0)} then Message(sym_e_only_static_in_static); if (symtabletype=unitsymtable) and assigned(punitsymtable(@self)^.unitsym) then inc(punitsymtable(@self)^.unitsym^.refs); { unitsym are only loaded for browsing PM } { this was buggy anyway because we could use } { unitsyms from other units in _USES !! } {if (symtabletype=unitsymtable) and (hp^.typ=unitsym) and assigned(current_module) and (current_module^.globalsymtable<>@self) then hp:=nil;} if assigned(hp) and (cs_browser in aktmoduleswitches) and make_ref then begin new(newref,init(hp^.lastref,@tokenpos)); { for symbols that are in tables without browser info or syssyms (PM) } if hp^.refcount=0 then begin hp^.defref:=newref; hp^.lastref:=newref; end else if resolving_forward and assigned(hp^.defref) then { put it as second reference } begin newref^.nextref:=hp^.defref^.nextref; hp^.defref^.nextref:=newref; hp^.lastref^.nextref:=nil; end else hp^.lastref:=newref; inc(hp^.refcount); end; if assigned(hp) and make_ref then begin inc(hp^.refs); end; end; speedsearch:=hp; end; function tsymtable.rename(const olds,news : stringid):psym; begin rename:=psym(symsearch^.rename(olds,news)); end; {*********************************************** Browser ***********************************************} procedure tsymtable.load_browser; var b : byte; sym : psym; prdef : pdef; oldrecsyms : psymtable; begin if symtabletype in [recordsymtable,objectsymtable] then begin oldrecsyms:=aktrecordsymtable; aktrecordsymtable:=@self; end; if symtabletype in [parasymtable,localsymtable] then begin oldrecsyms:=aktlocalsymtable; aktlocalsymtable:=@self; end; if symtabletype=staticppusymtable then aktstaticsymtable:=@self; b:=current_ppu^.readentry; if b <> ibbeginsymtablebrowser then Message1(unit_f_ppu_invalid_entry,tostr(b)); repeat b:=current_ppu^.readentry; case b of ibsymref : begin sym:=readsymref; resolvesym(sym); if assigned(sym) then sym^.load_references; end; ibdefref : begin prdef:=readdefref; resolvedef(prdef); if assigned(prdef) then begin if prdef^.deftype<>procdef then Message(unit_f_ppu_read_error); pprocdef(prdef)^.load_references; end; end; ibendsymtablebrowser : break; else Message1(unit_f_ppu_invalid_entry,tostr(b)); end; until false; if symtabletype in [recordsymtable,objectsymtable] then aktrecordsymtable:=oldrecsyms; if symtabletype in [parasymtable,localsymtable] then aktlocalsymtable:=oldrecsyms; end; procedure tsymtable.write_browser; var oldrecsyms : psymtable; begin { symbol numbering for references should have been done in write PM number_symbols; number_defs; } if symtabletype in [recordsymtable,objectsymtable] then begin oldrecsyms:=aktrecordsymtable; aktrecordsymtable:=@self; end; if symtabletype in [parasymtable,localsymtable] then begin oldrecsyms:=aktlocalsymtable; aktlocalsymtable:=@self; end; current_ppu^.writeentry(ibbeginsymtablebrowser); foreach({$ifndef TP}@{$endif}write_refs); current_ppu^.writeentry(ibendsymtablebrowser); if symtabletype in [recordsymtable,objectsymtable] then aktrecordsymtable:=oldrecsyms; if symtabletype in [parasymtable,localsymtable] then aktlocalsymtable:=oldrecsyms; end; {$ifdef BrowserLog} procedure tsymtable.writebrowserlog; begin if cs_browser in aktmoduleswitches then begin if assigned(name) then Browserlog.AddLog('---Symtable '+name^) else begin if (symtabletype=recordsymtable) and assigned(defowner^.typesym) then Browserlog.AddLog('---Symtable '+defowner^.typesym^.name) else Browserlog.AddLog('---Symtable with no name'); end; Browserlog.Ident; foreach({$ifndef TP}@{$endif}add_to_browserlog); browserlog.Unident; end; end; {$endif BrowserLog} {*********************************************** Process all entries ***********************************************} { checks, if all procsyms and methods are defined } procedure tsymtable.check_forwards; begin foreach({$ifndef TP}@{$endif}check_forward); end; procedure tsymtable.checklabels; begin foreach({$ifndef TP}@{$endif}labeldefined); end; procedure tsymtable.set_alignment(_alignment : byte); var sym : pvarsym; l : longint; begin { this can not be done if there is an hasharray ! } alignment:=_alignment; if (symtabletype<>parasymtable) then internalerror(1111); sym:=pvarsym(symindex^.first); datasize:=0; { there can be only varsyms } while assigned(sym) do begin l:=sym^.getpushsize; sym^.address:=datasize; datasize:=align(datasize+l,alignment); sym:=pvarsym(sym^.next); end; end; function tsymtable.find_at_offset(l : longint) : pvarsym; var sym : pvarsym; begin find_at_offset:=nil; { this can not be done if there is an hasharray ! } if (symtabletype<>parasymtable) then internalerror(1111); sym:=pvarsym(symindex^.first); while assigned(sym) do begin if sym^.address+address_fixup=l then begin find_at_offset:=sym; exit; end; sym:=pvarsym(sym^.next); end; end; procedure tsymtable.allunitsused; begin foreach({$ifndef TP}@{$endif}unitsymbolused); end; procedure tsymtable.allsymbolsused; begin foreach({$ifndef TP}@{$endif}varsymbolused); end; procedure tsymtable.allprivatesused; begin foreach({$ifndef TP}@{$endif}objectprivatesymbolused); end; {$ifdef CHAINPROCSYMS} procedure tsymtable.chainprocsyms; begin foreach({$ifndef TP}@{$endif}chainprocsym); end; {$endif CHAINPROCSYMS} {$ifdef GDB} procedure tsymtable.concatstabto(asmlist : paasmoutput); begin asmoutput:=asmlist; if symtabletype in [inlineparasymtable,inlinelocalsymtable] then foreach({$ifndef TP}@{$endif}resetstab); foreach({$ifndef TP}@{$endif}concatstab); end; {$endif} {**************************************************************************** TUNITSYMTABLE ****************************************************************************} constructor tunitsymtable.init(t : tsymtabletype; const n : string); begin inherited init(t); name:=stringdup(upper(n)); unitid:=0; unitsym:=nil; symsearch^.usehash; { reset GDB things } {$ifdef GDB} if (t = globalsymtable) then begin prev_dbx_counter := dbx_counter; dbx_counter := nil; end; is_stab_written:=false; dbx_count := -1; if cs_gdb_dbx in aktglobalswitches then begin dbx_count := 0; unittypecount:=1; if (symtabletype=globalsymtable) then pglobaltypecount := @unittypecount; unitid:=current_module^.unitcount; debuglist^.concat(new(pai_asm_comment,init(strpnew('Global '+name^+' has index '+tostr(unitid))))); debuglist^.concat(new(pai_stabs,init(strpnew('"'+name^+'",'+tostr(N_BINCL)+',0,0,0')))); inc(current_module^.unitcount); dbx_count_ok:=false; dbx_counter:=@dbx_count; do_count_dbx:=true; end; {$endif GDB} end; constructor tunitsymtable.loadasunit; var storeGlobalTypeCount : pword; b : byte; begin unitsym:=nil; unitid:=0; {$ifdef GDB} if cs_gdb_dbx in aktglobalswitches then begin UnitTypeCount:=1; storeGlobalTypeCount:=PGlobalTypeCount; PglobalTypeCount:=@UnitTypeCount; end; {$endif GDB} { load symtables } inherited loadas(unitsymtable); { set the name after because it is set to nil in tsymtable.load !! } name:=stringdup(current_module^.modulename^); { dbx count } {$ifdef GDB} if (current_module^.flags and uf_has_dbx)<>0 then begin b := current_ppu^.readentry; if b <> ibdbxcount then Message(unit_f_ppu_dbx_count_problem) else dbx_count := readlong; dbx_count_ok := {true}false; end else begin dbx_count := -1; dbx_count_ok:=false; end; if cs_gdb_dbx in aktglobalswitches then PGlobalTypeCount:=storeGlobalTypeCount; is_stab_written:=false; {$endif GDB} b:=current_ppu^.readentry; if b<>ibendimplementation then Message1(unit_f_ppu_invalid_entry,tostr(b)); end; destructor tunitsymtable.done; var pus : punitsym; begin pus:=unitsym; while assigned(pus) do begin unitsym:=pus^.prevsym; pus^.prevsym:=nil; pus^.unitsymtable:=nil; pus:=unitsym; end; inherited done; end; procedure tunitsymtable.load_symtable_refs; var b : byte; unitindex : word; begin if ((current_module^.flags and uf_local_browser)<>0) then begin current_module^.localsymtable:=new(punitsymtable,loadas(staticppusymtable)); psymtable(current_module^.localsymtable)^.name:= stringdup('implementation of '+psymtable(current_module^.globalsymtable)^.name^); end; { load browser } if (current_module^.flags and uf_has_browser)<>0 then begin {if not (cs_browser in aktmoduleswitches) then current_ppu^.skipuntilentry(ibendbrowser) else } begin load_browser; unitindex:=1; while assigned(current_module^.map^[unitindex]) do begin {each unit wrote one browser entry } load_browser; inc(unitindex); end; b:=current_ppu^.readentry; if b<>ibendbrowser then Message1(unit_f_ppu_invalid_entry,tostr(b)); end; end; if ((current_module^.flags and uf_local_browser)<>0) then psymtable(current_module^.localsymtable)^.load_browser; end; procedure tunitsymtable.writeasunit; var pu : pused_unit; begin { first the unitname } current_ppu^.putstring(name^); current_ppu^.writeentry(ibmodulename); writesourcefiles; writeusedmacros; writeusedunit; { write the objectfiles and libraries that come for this unit, preserve the containers becuase they are still needed to load the link.res. All doesn't depend on the crc! It doesn't matter if a unit is in a .o or .a file } current_ppu^.do_crc:=false; writelinkcontainer(current_module^.linkunitofiles,iblinkunitofiles,true); writelinkcontainer(current_module^.linkunitstaticlibs,iblinkunitstaticlibs,true); writelinkcontainer(current_module^.linkunitsharedlibs,iblinkunitsharedlibs,true); writelinkcontainer(current_module^.linkotherofiles,iblinkotherofiles,false); writelinkcontainer(current_module^.linkotherstaticlibs,iblinkotherstaticlibs,true); writelinkcontainer(current_module^.linkothersharedlibs,iblinkothersharedlibs,true); current_ppu^.do_crc:=true; current_ppu^.writeentry(ibendinterface); { write the symtable entries } inherited writeas; { all after doesn't affect crc } current_ppu^.do_crc:=false; { write dbx count } {$ifdef GDB} if cs_gdb_dbx in aktglobalswitches then begin {$IfDef EXTDEBUG} writeln('Writing dbx_count ',dbx_count,' in unit ',name^,'.ppu'); {$ENDIF EXTDEBUG} current_ppu^.putlongint(dbx_count); current_ppu^.writeentry(ibdbxcount); end; {$endif GDB} current_ppu^.writeentry(ibendimplementation); { write static symtable needed for local debugging of unit functions } if ((current_module^.flags and uf_local_browser)<>0) and assigned(current_module^.localsymtable) then psymtable(current_module^.localsymtable)^.writeas; { write all browser section } if (current_module^.flags and uf_has_browser)<>0 then begin write_browser; pu:=pused_unit(current_module^.used_units.first); while assigned(pu) do begin psymtable(pu^.u^.globalsymtable)^.write_browser; pu:=pused_unit(pu^.next); end; current_ppu^.writeentry(ibendbrowser); end; if ((current_module^.flags and uf_local_browser)<>0) and assigned(current_module^.localsymtable) then psymtable(current_module^.localsymtable)^.write_browser; { the last entry ibend is written automaticly } end; function tunitsymtable.getnewtypecount : word; begin {$ifdef GDB} if not (cs_gdb_dbx in aktglobalswitches) then getnewtypecount:=tsymtable.getnewtypecount else {$endif GDB} if symtabletype = staticsymtable then getnewtypecount:=tsymtable.getnewtypecount else begin getnewtypecount:=unittypecount; inc(unittypecount); end; end; {$ifdef GDB} procedure tunitsymtable.concattypestabto(asmlist : paasmoutput); var prev_dbx_count : plongint; begin if is_stab_written then exit; if not assigned(name) then name := stringdup('Main_program'); if (symtabletype = unitsymtable) and (current_module^.globalsymtable<>@Self) then begin unitid:=current_module^.unitcount; inc(current_module^.unitcount); end; asmlist^.concat(new(pai_asm_comment,init(strpnew('Begin unit '+name^ +' has index '+tostr(unitid))))); if cs_gdb_dbx in aktglobalswitches then begin if dbx_count_ok then begin asmlist^.concat(new(pai_asm_comment,init(strpnew('"repeated" unit '+name^ +' has index '+tostr(unitid)+' dbx count = '+tostr(dbx_count))))); asmlist^.concat(new(pai_stabs,init(strpnew('"'+name^+'",' +tostr(N_EXCL)+',0,0,'+tostr(dbx_count))))); exit; end else if (current_module^.globalsymtable<>@Self) then begin prev_dbx_count := dbx_counter; dbx_counter := nil; do_count_dbx:=false; if symtabletype = unitsymtable then asmlist^.concat(new(pai_stabs,init(strpnew('"'+name^+'",' +tostr(N_BINCL)+',0,0,0')))); dbx_counter := @dbx_count; dbx_count:=0; do_count_dbx:=assigned(dbx_counter); end; end; asmoutput:=asmlist; foreach({$ifndef TP}@{$endif}concattypestab); if cs_gdb_dbx in aktglobalswitches then begin if (current_module^.globalsymtable<>@Self) then begin dbx_counter := prev_dbx_count; do_count_dbx:=false; asmlist^.concat(new(pai_asm_comment,init(strpnew('End unit '+name^ +' has index '+tostr(unitid))))); asmlist^.concat(new(pai_stabs,init(strpnew('"'+name^+'",' +tostr(N_EINCL)+',0,0,0')))); do_count_dbx:=assigned(dbx_counter); dbx_count_ok := {true}false; end; end; is_stab_written:=true; end; {$endif} {**************************************************************************** Definitions ****************************************************************************} {$I symdef.inc} {**************************************************************************** Symbols ****************************************************************************} {$I symsym.inc} {**************************************************************************** GDB Helpers ****************************************************************************} {$ifdef GDB} function typeglobalnumber(const s : string) : string; var st : string; symt : psymtable; old_make_ref : boolean; begin old_make_ref:=make_ref; make_ref:=false; typeglobalnumber := '0'; srsym := nil; if pos('.',s) > 0 then begin st := copy(s,1,pos('.',s)-1); getsym(st,false); st := copy(s,pos('.',s)+1,255); if assigned(srsym) then begin if srsym^.typ = unitsym then begin symt := punitsym(srsym)^.unitsymtable; srsym := symt^.search(st); end else srsym := nil; end; end else st := s; if srsym = nil then getsym(st,true); if srsym^.typ<>typesym then begin Message(type_e_type_id_expected); exit; end; typeglobalnumber := ptypesym(srsym)^.restype.def^.numberstring; make_ref:=old_make_ref; end; {$endif GDB} {**************************************************************************** Definition Helpers ****************************************************************************} procedure reset_global_defs; var def : pdef; {$ifdef debug} prevdef : pdef; {$endif debug} begin {$ifdef debug} prevdef:=nil; {$endif debug} {$ifdef GDB} pglobaltypecount:=@globaltypecount; {$endif GDB} def:=firstglobaldef; while assigned(def) do begin {$ifdef GDB} if assigned(def^.typesym) then def^.typesym^.isusedinstab:=false; def^.is_def_stab_written:=false; {$endif GDB} {if not current_module^.in_implementation then} begin { reset rangenr's } case def^.deftype of orddef : porddef(def)^.rangenr:=0; enumdef : penumdef(def)^.rangenr:=0; arraydef : parraydef(def)^.rangenr:=0; end; if def^.deftype<>objectdef then def^.has_rtti:=false; def^.has_inittable:=false; end; {$ifdef debug} prevdef:=def; {$endif debug} def:=def^.nextglobal; end; end; {**************************************************************************** Object Helpers ****************************************************************************} function search_class_member(pd : pobjectdef;const n : string) : psym; { searches n in symtable of pd and all anchestors } var sym : psym; begin sym:=nil; while assigned(pd) do begin sym:=pd^.symtable^.search(n); if assigned(sym) then break; pd:=pd^.childof; end; { this is needed for static methods in do_member_read pexpr unit PM caused bug0214 } if assigned(sym) then begin srsymtable:=pd^.symtable; end; search_class_member:=sym; end; var _defaultprop : ppropertysym; procedure testfordefaultproperty(p : pnamedindexobject); begin if (psym(p)^.typ=propertysym) and (ppo_defaultproperty in ppropertysym(p)^.propoptions) then _defaultprop:=ppropertysym(p); end; function search_default_property(pd : pobjectdef) : ppropertysym; { returns the default property of a class, searches also anchestors } begin _defaultprop:=nil; while assigned(pd) do begin pd^.symtable^.foreach({$ifndef TP}@{$endif}testfordefaultproperty); if assigned(_defaultprop) then break; pd:=pd^.childof; end; search_default_property:=_defaultprop; end; {**************************************************************************** Macro's ****************************************************************************} procedure def_macro(const s : string); var mac : pmacrosym; begin mac:=pmacrosym(macros^.search(s)); if mac=nil then begin mac:=new(pmacrosym,init(s)); Message1(parser_m_macro_defined,mac^.name); macros^.insert(mac); end; mac^.defined:=true; mac^.defined_at_startup:=true; end; procedure set_macro(const s : string;value : string); var mac : pmacrosym; begin mac:=pmacrosym(macros^.search(s)); if mac=nil then begin mac:=new(pmacrosym,init(s)); macros^.insert(mac); end else begin if assigned(mac^.buftext) then freemem(mac^.buftext,mac^.buflen); end; Message2(parser_m_macro_set_to,mac^.name,value); mac^.buflen:=length(value); getmem(mac^.buftext,mac^.buflen); move(value[1],mac^.buftext^,mac^.buflen); mac^.defined:=true; mac^.defined_at_startup:=true; end; {$ifdef UNITALIASES} {**************************************************************************** TUNIT_ALIAS ****************************************************************************} constructor tunit_alias.init(const n:string); var i : longint; begin i:=pos('=',n); if i=0 then fail; inherited initname(Copy(n,1,i-1)); newname:=stringdup(Copy(n,i+1,255)); end; destructor tunit_alias.done; begin stringdispose(newname); inherited done; end; procedure addunitalias(const n:string); begin unitaliases^.insert(new(punit_alias,init(Upper(n)))); end; function getunitalias(const n:string):string; var p : punit_alias; begin p:=punit_alias(unitaliases^.search(Upper(n))); if assigned(p) then getunitalias:=punit_alias(p)^.newname^ else getunitalias:=n; end; {$endif UNITALIASES} {**************************************************************************** Symtable Stack ****************************************************************************} procedure dellexlevel; var p : psymtable; begin p:=symtablestack; symtablestack:=p^.next; { symbol tables of unit interfaces are never disposed } { this is handle by the unit unitm } if not(p^.symtabletype in [unitsymtable,globalsymtable,stt_exceptsymtable]) or dispose_global then dispose(p,done); end; procedure RestoreUnitSyms; var p : psymtable; begin p:=symtablestack; while assigned(p) do begin if (p^.symtabletype=unitsymtable) and assigned(punitsymtable(p)^.unitsym) and ((punitsymtable(p)^.unitsym^.owner=psymtable(current_module^.globalsymtable)) or (punitsymtable(p)^.unitsym^.owner=psymtable(current_module^.localsymtable))) then punitsymtable(p)^.unitsym^.restoreunitsym; p:=p^.next; end; end; {$ifdef DEBUG} procedure test_symtablestack; var p : psymtable; i : longint; begin p:=symtablestack; i:=0; while assigned(p) do begin inc(i); p:=p^.next; if i>500 then Message(sym_f_internal_error_in_symtablestack); end; end; procedure list_symtablestack; var p : psymtable; i : longint; begin p:=symtablestack; i:=0; while assigned(p) do begin inc(i); writeln(i,' ',p^.name^); p:=p^.next; if i>500 then Message(sym_f_internal_error_in_symtablestack); end; end; {$endif DEBUG} {**************************************************************************** Init/Done Symtable ****************************************************************************} {$ifndef Delphi} {$ifdef tp} procedure do_streamerror; begin if symbolstream.status=-2 then WriteLn('Error: Not enough EMS memory') else WriteLn('Error: EMS Error ',symbolstream.status); halt(1); end; {$endif TP} {$endif Delphi} procedure InitSymtable; var token : ttoken; begin {$ifndef Delphi} {$ifdef TP} { Allocate stream } if use_big then begin streamerror:=@do_streamerror; { symbolstream.init('TMPFILE',stcreate,16000); } {$ifndef dpmi} symbolstream.init(10000,4000000); {using ems streams} {$else} symbolstream.init(1000000,16000); {using memory streams} {$endif} if symbolstream.errorinfo=stiniterror then do_streamerror; { write something, because pos 0 means nil pointer } symbolstream.writestr(@inputfile); end; {$endif tp} {$endif Delphi} { Reset symbolstack } registerdef:=false; read_member:=false; symtablestack:=nil; systemunit:=nil; {$ifdef GDB} firstglobaldef:=nil; lastglobaldef:=nil; {$endif GDB} globaltypecount:=1; pglobaltypecount:=@globaltypecount; { create error syms and def } generrorsym:=new(perrorsym,init); generrordef:=new(perrordef,init); {$ifdef UNITALIASES} { unit aliases } unitaliases:=new(pdictionary,init); {$endif} for token:=first_overloaded to last_overloaded do overloaded_operators[token]:=nil; end; procedure DoneSymtable; begin dispose(generrorsym,done); dispose(generrordef,done); {$ifdef UNITALIASES} dispose(unitaliases,done); {$endif} {$ifndef Delphi} {$ifdef TP} { close the stream } if use_big then symbolstream.done; {$endif} {$endif Delphi} end; end. { $Log$ Revision 1.92 2000-05-23 14:15:44 pierre * fix for bug 959 Revision 1.91 2000/05/12 05:59:57 pierre * * get it to compile with Delphi by Kovacs Attila Zoltan Revision 1.90 2000/05/11 09:40:12 pierre * some DBX changes but it still does not work ! Revision 1.89 2000/05/03 14:34:05 pierre * fix the unitsym chain Revision 1.88 2000/04/27 11:35:04 pierre * power to ** operator fixed Revision 1.87 2000/04/27 10:06:04 pierre * fix for snapshot failue * order_overloaded reintrocduced and adapted to operators Revision 1.86 2000/04/26 08:54:19 pierre * More changes for operator bug Order_overloaded method removed because it conflicted with new implementation where the defs are ordered according to the unit loading order ! Revision 1.85 2000/04/25 23:55:30 pierre + Hint about unused unit * Testop bug fixed !! Now the operators are only applied if the unit is explicitly loaded Revision 1.84 2000/04/24 12:45:44 peter * made overloaded_operators local per unit, but it still doesn't work correct Revision 1.83 2000/03/27 21:15:34 pierre * fix bug 294 in a BP compatible way ie. hidding the function result Revision 1.82 2000/03/22 09:25:57 florian * bug 294 fixed: parameters can have now the same name as the function/ procedure, this is compatible with TP/Delphi Revision 1.81 2000/03/20 09:34:33 florian * in delphi mode: method parameters can now have the same name as parameters Revision 1.80 2000/03/01 13:56:31 pierre * fix for bug 840 Revision 1.79 2000/03/01 00:03:10 pierre * fixes for locals in inlined procedures fix for bug797 + stabs generation for inlined paras and locals Revision 1.78 2000/02/20 20:49:45 florian * newcg is compiling * fixed the dup id problem reported by Paul Y. Revision 1.77 2000/02/11 13:53:49 pierre * avoid stack overflow in tref.done (bug 846) Revision 1.76 2000/02/09 13:23:05 peter * log truncated Revision 1.75 2000/01/12 10:38:18 peter * smartlinking fixes for binary writer * release alignreg code and moved instruction writing align to cpuasm, but it doesn't use the specified register yet Revision 1.74 2000/01/09 00:37:56 pierre * avoid testing object types that are simple aliases for unused privates Revision 1.73 2000/01/07 01:14:41 peter * updated copyright to 2000 Revision 1.72 2000/01/03 19:26:04 peter * fixed resolving of ttypesym which are reference from object/record fields. Revision 1.71 1999/12/18 14:55:21 florian * very basic widestring support Revision 1.70 1999/12/02 11:28:27 peter * moved verbose to implementation uses Revision 1.69 1999/12/01 22:32:35 pierre * give info of original duplicated symbol more often Revision 1.68 1999/11/30 10:40:56 peter + ttype, tsymlist Revision 1.67 1999/11/24 11:41:05 pierre * defaultsymtablestack is now restored after parser.compile Revision 1.66 1999/11/22 00:23:09 pierre * also complain about unused functions in program Revision 1.65 1999/11/19 14:49:15 pierre * avoid certain wrong notes/hints Revision 1.64 1999/11/18 15:34:48 pierre * Notes/Hints for local syms changed to Set_varstate function Revision 1.63 1999/11/17 17:05:06 pierre * Notes/hints changes Revision 1.62 1999/11/15 22:00:48 peter * labels used but not defined give error instead of warning, the warning is now only with declared but not defined and not used. Revision 1.61 1999/11/15 17:52:59 pierre + one field added for ttoken record for operator linking the id to the corresponding operator token that can now now all be overloaded * overloaded operators are resetted to nil in InitSymtable (bug when trying to compile a uint that overloads operators twice) Revision 1.60 1999/11/09 23:35:50 pierre + better reference pos for forward defs Revision 1.59 1999/11/06 16:21:57 jonas + search optimial register to use in alignment code (compile with -dalignreg, -dalignregdebug to see chosen register in assembler code). Still needs support in ag386bin. Revision 1.58 1999/11/06 14:34:28 peter * truncated log to 20 revs Revision 1.57 1999/11/05 17:18:03 pierre * local browsing works at first level ie for function defined in interface or implementation not yet for functions inside other functions Revision 1.56 1999/11/04 23:13:25 peter * moved unit alias support into ifdef }