mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-05-01 11:33:41 +02:00
2442 lines
80 KiB
ObjectPascal
2442 lines
80 KiB
ObjectPascal
{
|
|
$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.
|
|
****************************************************************************
|
|
}
|
|
unit symtable;
|
|
|
|
{$i defines.inc}
|
|
|
|
interface
|
|
|
|
uses
|
|
{ common }
|
|
cutils,cobjects,
|
|
{ global }
|
|
globtype,tokens,
|
|
{ symtable }
|
|
symconst,symbase,symtype,symdef,symsym,
|
|
{ assembler }
|
|
aasm
|
|
;
|
|
|
|
|
|
{****************************************************************************
|
|
Symtable types
|
|
****************************************************************************}
|
|
|
|
type
|
|
pstoredsymtable = ^tstoredsymtable;
|
|
tstoredsymtable = object(tsymtable)
|
|
constructor init(t : tsymtabletype);
|
|
{ load/write }
|
|
constructor loadas(typ : tsymtabletype);
|
|
procedure writeas;
|
|
procedure loaddefs;
|
|
procedure loadsyms;
|
|
procedure writedefs;
|
|
procedure writesyms;
|
|
procedure deref;
|
|
procedure insert(sym : psymentry);virtual;
|
|
procedure insert_in(psymt : psymtable;offset : longint);
|
|
function speedsearch(const s : stringid;speedvalue : longint) : psymentry;virtual;
|
|
procedure allsymbolsused;
|
|
procedure allprivatesused;
|
|
procedure allunitsused;
|
|
procedure check_forwards;
|
|
procedure checklabels;
|
|
{ change alignment for args only parasymtable }
|
|
procedure set_alignment(_alignment : longint);
|
|
{$ifdef CHAINPROCSYMS}
|
|
procedure chainprocsyms;
|
|
{$endif CHAINPROCSYMS}
|
|
{$ifndef DONOTCHAINOPERATORS}
|
|
procedure chainoperators;
|
|
{$endif DONOTCHAINOPERATORS}
|
|
procedure load_browser;
|
|
procedure write_browser;
|
|
{$ifdef GDB}
|
|
procedure concatstabto(asmlist : paasmoutput);virtual;
|
|
function getnewtypecount : word; virtual;
|
|
{$endif GDB}
|
|
end;
|
|
|
|
punitsymtable = ^tunitsymtable;
|
|
tunitsymtable = object(tstoredsymtable)
|
|
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);
|
|
function getnewtypecount : word; virtual;
|
|
{$endif GDB}
|
|
procedure load_symtable_refs;
|
|
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;
|
|
procedure clear;virtual;
|
|
end;
|
|
|
|
|
|
var
|
|
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 }
|
|
systemunit : punitsymtable; { pointer to the system unit }
|
|
read_member : boolean; { reading members of an symtable }
|
|
|
|
lexlevel : longint; { level of code }
|
|
{ 1 for main procedure }
|
|
{ 2 for normal function or proc }
|
|
{ higher for locals }
|
|
|
|
{****************************************************************************
|
|
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 numberunits;
|
|
procedure load_interface;
|
|
|
|
{*** Object Helpers ***}
|
|
function search_class_member(pd : pobjectdef;const n : string) : psym;
|
|
function search_default_property(pd : pobjectdef) : ppropertysym;
|
|
|
|
{*** symtable stack ***}
|
|
procedure dellexlevel;
|
|
procedure RestoreUnitSyms;
|
|
{$ifdef DEBUG}
|
|
procedure test_symtablestack;
|
|
procedure list_symtablestack;
|
|
{$endif DEBUG}
|
|
|
|
{$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}
|
|
|
|
{*** Init / Done ***}
|
|
procedure InitSymtable;
|
|
procedure DoneSymtable;
|
|
|
|
|
|
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','not','shl','shr','xor',
|
|
'assign');
|
|
|
|
|
|
|
|
implementation
|
|
|
|
uses
|
|
{ global }
|
|
version,verbose,globals,
|
|
{ target }
|
|
systems,
|
|
{ ppu }
|
|
symppu,ppu,
|
|
{ module }
|
|
finput,fmodule,
|
|
{$ifdef GDB}
|
|
gdb,
|
|
{$endif GDB}
|
|
{ scanner }
|
|
scanner,
|
|
{ codegen }
|
|
hcodegen
|
|
;
|
|
|
|
|
|
var
|
|
in_loading : boolean; { remove !!! }
|
|
|
|
|
|
{*****************************************************************************
|
|
Symbol Call Back Functions
|
|
*****************************************************************************}
|
|
|
|
procedure write_refs(sym : pnamedindexobject);
|
|
begin
|
|
pstoredsym(sym)^.write_references;
|
|
end;
|
|
|
|
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,plabelsym(p)^.realname)
|
|
else
|
|
Message1(sym_w_label_not_defined,plabelsym(p)^.realname);
|
|
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,psym(p)^.realname);
|
|
end
|
|
else if (psym(p)^.owner^.symtabletype=objectsymtable) then
|
|
MessagePos2(psym(p)^.fileinfo,sym_n_private_identifier_not_used,psym(p)^.owner^.name^,psym(p)^.realname)
|
|
else
|
|
MessagePos1(psym(p)^.fileinfo,sym_n_local_identifier_not_used,psym(p)^.realname);
|
|
end
|
|
else if pvarsym(p)^.varstate=vs_assigned then
|
|
begin
|
|
if (psym(p)^.owner^.symtabletype=parasymtable) then
|
|
begin
|
|
if not(pvarsym(p)^.varspez in [vs_var,vs_out]) then
|
|
MessagePos1(psym(p)^.fileinfo,sym_h_para_identifier_only_set,psym(p)^.realname)
|
|
end
|
|
else if (vo_is_local_copy in pvarsym(p)^.varoptions) then
|
|
begin
|
|
if not(pvarsym(p)^.varspez in [vs_var,vs_out]) then
|
|
MessagePos1(psym(p)^.fileinfo,sym_h_para_identifier_only_set,psym(p)^.realname);
|
|
end
|
|
else if (psym(p)^.owner^.symtabletype=objectsymtable) then
|
|
MessagePos2(psym(p)^.fileinfo,sym_n_private_identifier_only_set,psym(p)^.owner^.name^,psym(p)^.realname)
|
|
else if (psym(p)^.owner^.symtabletype<>parasymtable) then
|
|
if not (vo_is_exported in pvarsym(p)^.varoptions) then
|
|
MessagePos1(psym(p)^.fileinfo,sym_n_local_identifier_only_set,psym(p)^.realname);
|
|
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 (pstoredsym(p)^.refs=0) and (psym(p)^.owner^.symtabletype=objectsymtable) then
|
|
MessagePos2(psym(p)^.fileinfo,sym_n_private_method_not_used,psym(p)^.owner^.name^,psym(p)^.realname)
|
|
{ units references are problematic }
|
|
else if (pstoredsym(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],psym(p)^.realname);
|
|
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=psym(p)) then
|
|
pobjectdef(ptypesym(p)^.restype.def)^.symtable^.foreach(
|
|
{$ifdef FPCPROCVAR}@{$endif}TestPrivate);
|
|
end;
|
|
|
|
{$ifdef GDB}
|
|
var
|
|
asmoutput : paasmoutput;
|
|
|
|
procedure concatstab(p : pnamedindexobject);
|
|
begin
|
|
if psym(p)^.typ <> procsym then
|
|
pstoredsym(p)^.concatstabto(asmoutput);
|
|
end;
|
|
|
|
procedure resetstab(p : pnamedindexobject);
|
|
begin
|
|
if psym(p)^.typ <> procsym then
|
|
pstoredsym(p)^.isstabwritten:=false;
|
|
end;
|
|
|
|
procedure concattypestab(p : pnamedindexobject);
|
|
begin
|
|
if psym(p)^.typ = typesym then
|
|
begin
|
|
pstoredsym(p)^.isstabwritten:=false;
|
|
pstoredsym(p)^.concatstabto(asmoutput);
|
|
end;
|
|
end;
|
|
{$endif GDB}
|
|
|
|
{$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}
|
|
|
|
|
|
{****************************************************************************
|
|
STORED SYMTABLE
|
|
****************************************************************************}
|
|
|
|
constructor tstoredsymtable.init(t : tsymtabletype);
|
|
begin
|
|
symtabletype:=t;
|
|
symtablelevel:=0;
|
|
defowner:=nil;
|
|
unitid:=0;
|
|
next:=nil;
|
|
name:=nil;
|
|
address_fixup:=0;
|
|
datasize:=0;
|
|
if t=parasymtable then
|
|
dataalignment:=4
|
|
else
|
|
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;
|
|
end;
|
|
|
|
|
|
{$ifndef DONOTCHAINOPERATORS}
|
|
procedure tstoredsymtable.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 tstoredsymtable.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 tstoredsymtable.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 tstoredsymtable.writedefs;
|
|
var
|
|
pd : pstoreddef;
|
|
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:=pstoreddef(defindex^.first);
|
|
while assigned(pd) do
|
|
begin
|
|
pd^.write;
|
|
pd:=pstoreddef(pd^.indexnext);
|
|
end;
|
|
{ write end of definitions }
|
|
current_ppu^.writeentry(ibenddefs);
|
|
end;
|
|
|
|
|
|
procedure tstoredsymtable.writesyms;
|
|
var
|
|
pd : pstoredsym;
|
|
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:=pstoredsym(symindex^.first);
|
|
while assigned(pd) do
|
|
begin
|
|
pd^.write;
|
|
pd:=pstoredsym(pd^.indexnext);
|
|
end;
|
|
{ end of symbols }
|
|
current_ppu^.writeentry(ibendsyms);
|
|
end;
|
|
|
|
|
|
{***********************************************
|
|
Browser
|
|
***********************************************}
|
|
|
|
procedure tstoredsymtable.load_browser;
|
|
var
|
|
b : byte;
|
|
sym : pstoredsym;
|
|
prdef : pstoreddef;
|
|
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:=pstoredsym(readderef);
|
|
resolvesym(sym);
|
|
if assigned(sym) then
|
|
sym^.load_references;
|
|
end;
|
|
ibdefref : begin
|
|
prdef:=pstoreddef(readderef);
|
|
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 tstoredsymtable.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({$ifdef FPCPROCVAR}@{$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 GDB}
|
|
function tstoredsymtable.getnewtypecount : word;
|
|
begin
|
|
getnewtypecount:=pglobaltypecount^;
|
|
inc(pglobaltypecount^);
|
|
end;
|
|
{$endif GDB}
|
|
|
|
|
|
procedure order_overloads(p : Pnamedindexobject);
|
|
begin
|
|
if psym(p)^.typ=procsym then
|
|
pprocsym(p)^.order_overloaded;
|
|
end;
|
|
|
|
|
|
procedure tstoredsymtable.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^.indexnext);
|
|
end;
|
|
{ deref the definitions }
|
|
hp:=pdef(defindex^.first);
|
|
while assigned(hp) do
|
|
begin
|
|
hp^.deref;
|
|
hp:=pdef(hp^.indexnext);
|
|
end;
|
|
{ deref the symbols }
|
|
hs:=psym(symindex^.first);
|
|
while assigned(hs) do
|
|
begin
|
|
hs^.deref;
|
|
hs:=psym(hs^.indexnext);
|
|
end;
|
|
end;
|
|
|
|
{ this procedure is reserved for inserting case variant into
|
|
a record symtable }
|
|
{ the offset is the location of the start of the variant
|
|
and datasize and dataalignment corresponds to
|
|
the complete size (see code in pdecl unit) PM }
|
|
procedure tstoredsymtable.insert_in(psymt : psymtable;offset : longint);
|
|
var
|
|
ps,nps : pvarsym;
|
|
pd,npd : pdef;
|
|
storesize,storealign : longint;
|
|
begin
|
|
storesize:=psymt^.datasize;
|
|
storealign:=psymt^.dataalignment;
|
|
psymt^.datasize:=offset;
|
|
ps:=pvarsym(symindex^.first);
|
|
while assigned(ps) do
|
|
begin
|
|
{ this is used to insert case variant into the main
|
|
record }
|
|
psymt^.datasize:=ps^.address+offset;
|
|
nps:=pvarsym(ps^.indexnext);
|
|
symindex^.deleteindex(ps);
|
|
ps^.indexnext:=nil;
|
|
ps^.left:=nil;
|
|
ps^.right:=nil;
|
|
psymt^.insert(ps);
|
|
ps:=nps;
|
|
end;
|
|
pd:=pdef(defindex^.first);
|
|
while assigned(pd) do
|
|
begin
|
|
npd:=pdef(pd^.indexnext);
|
|
defindex^.deleteindex(pd);
|
|
pd^.indexnext:=nil;
|
|
pd^.left:=nil;
|
|
pd^.right:=nil;
|
|
psymt^.registerdef(pd);
|
|
pd:=npd;
|
|
end;
|
|
psymt^.datasize:=storesize;
|
|
psymt^.dataalignment:=storealign;
|
|
end;
|
|
|
|
constructor tstoredsymtable.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;
|
|
if typ=parasymtable then
|
|
dataalignment:=4
|
|
else
|
|
dataalignment:=1;
|
|
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 tstoredsymtable.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({$ifdef FPCPROCVAR}@{$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;
|
|
|
|
|
|
procedure tstoredsymtable.insert(sym:psymentry);
|
|
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
|
|
pstoredsym(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:=psym(hp^.search(sym^.name));
|
|
if assigned(hsym) then
|
|
DuplicateSym(hsym);
|
|
end;
|
|
hp:=hp^.next;
|
|
end;
|
|
end;
|
|
{ check the current symtable }
|
|
hsym:=psym(search(sym^.name));
|
|
if assigned(hsym) then
|
|
begin
|
|
{ in TP and Delphi you can have a local with the
|
|
same name as the function, the function is then hidden for
|
|
the user. (Under delphi it can still be accessed using result),
|
|
but don't allow hiding of RESULT }
|
|
if (m_tp in aktmodeswitches) and
|
|
(hsym^.typ=funcretsym) and
|
|
not((m_result in aktmodeswitches) and
|
|
(hsym^.name='RESULT')) then
|
|
hsym^.owner^.rename(hsym^.name,'hidden'+hsym^.name)
|
|
else
|
|
begin
|
|
DuplicateSym(hsym);
|
|
exit;
|
|
end;
|
|
end;
|
|
{ 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:=psym(next^.search(sym^.name));
|
|
if assigned(hsym) then
|
|
begin
|
|
{ a parameter and the function can have the same
|
|
name in TP and Delphi, but RESULT not }
|
|
if (m_tp in aktmodeswitches) and
|
|
(sym^.typ=funcretsym) and
|
|
not((m_result in aktmodeswitches) and
|
|
(sym^.name='RESULT')) then
|
|
sym^.setname('hidden'+sym^.name)
|
|
else
|
|
begin
|
|
DuplicateSym(hsym);
|
|
exit;
|
|
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 in a class, but not
|
|
in object (tp7 compatible) }
|
|
if not((m_delphi in aktmodeswitches) and
|
|
is_class(pdef(next^.next^.defowner))) then
|
|
begin
|
|
DuplicateSym(hsym);
|
|
exit;
|
|
end;
|
|
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 in a class, but not
|
|
in object (tp7 compatible) }
|
|
if not((m_delphi in aktmodeswitches) and
|
|
is_class(procinfo^._class)) then
|
|
begin
|
|
DuplicateSym(hsym);
|
|
exit;
|
|
end;
|
|
end;
|
|
end;
|
|
{ check for duplicate field id in inherited classes }
|
|
if (sym^.typ=varsym) and
|
|
(symtabletype=objectsymtable) and
|
|
assigned(defowner) and
|
|
(
|
|
not(m_delphi in aktmodeswitches) or
|
|
is_object(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
|
|
begin
|
|
DuplicateSym(hsym);
|
|
exit;
|
|
end;
|
|
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);
|
|
end;
|
|
|
|
|
|
function tstoredsymtable.speedsearch(const s : stringid;speedvalue : longint) : psymentry;
|
|
var
|
|
hp : pstoredsym;
|
|
newref : pref;
|
|
begin
|
|
hp:=pstoredsym(inherited 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);
|
|
|
|
{$ifdef GDB}
|
|
{ if it is a type, we need the stabs of this type
|
|
this might be the cause of the class debug problems
|
|
as TCHILDCLASS.Create did not generate appropriate
|
|
stabs debug info if TCHILDCLASS wasn't used anywhere else PM }
|
|
if (hp^.typ=typesym) and make_ref then
|
|
begin
|
|
if assigned(ptypesym(hp)^.restype.def) then
|
|
pstoreddef(ptypesym(hp)^.restype.def)^.numberstring
|
|
else
|
|
ptypesym(hp)^.isusedinstab:=true;
|
|
end;
|
|
{$endif GDB}
|
|
{ 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,@akttokenpos));
|
|
{ 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;
|
|
|
|
{***********************************************
|
|
Process all entries
|
|
***********************************************}
|
|
|
|
{ checks, if all procsyms and methods are defined }
|
|
procedure tstoredsymtable.check_forwards;
|
|
begin
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}check_forward);
|
|
end;
|
|
|
|
procedure tstoredsymtable.checklabels;
|
|
begin
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}labeldefined);
|
|
end;
|
|
|
|
procedure tstoredsymtable.set_alignment(_alignment : longint);
|
|
var
|
|
sym : pvarsym;
|
|
l : longint;
|
|
begin
|
|
dataalignment:=_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,dataalignment);
|
|
sym:=pvarsym(sym^.indexnext);
|
|
end;
|
|
end;
|
|
|
|
procedure tstoredsymtable.allunitsused;
|
|
begin
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}unitsymbolused);
|
|
end;
|
|
|
|
procedure tstoredsymtable.allsymbolsused;
|
|
begin
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}varsymbolused);
|
|
end;
|
|
|
|
procedure tstoredsymtable.allprivatesused;
|
|
begin
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}objectprivatesymbolused);
|
|
end;
|
|
|
|
{$ifdef CHAINPROCSYMS}
|
|
procedure tstoredsymtable.chainprocsyms;
|
|
begin
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}chainprocsym);
|
|
end;
|
|
{$endif CHAINPROCSYMS}
|
|
|
|
{$ifdef GDB}
|
|
procedure tstoredsymtable.concatstabto(asmlist : paasmoutput);
|
|
begin
|
|
asmoutput:=asmlist;
|
|
if symtabletype in [inlineparasymtable,inlinelocalsymtable] then
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}resetstab);
|
|
|
|
foreach({$ifdef FPCPROCVAR}@{$endif}concatstab);
|
|
end;
|
|
{$endif}
|
|
|
|
|
|
{****************************************************************************
|
|
TWITHSYMTABLE
|
|
****************************************************************************}
|
|
|
|
constructor twithsymtable.init;
|
|
begin
|
|
inherited init(withsymtable);
|
|
direct_with:=false;
|
|
withnode:=nil;
|
|
withrefnode:=nil;
|
|
end;
|
|
|
|
|
|
destructor twithsymtable.done;
|
|
begin
|
|
symsearch:=nil;
|
|
inherited done;
|
|
end;
|
|
|
|
procedure twithsymtable.clear;
|
|
begin
|
|
{ remove no entry from a withsymtable as it is only a pointer to the
|
|
recorddef or objectdef symtable }
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
PPU Writing Helpers
|
|
****************************************************************************}
|
|
|
|
procedure writesourcefiles;
|
|
var
|
|
hp : pinputfile;
|
|
i,j : longint;
|
|
begin
|
|
{ second write the used source files }
|
|
current_ppu^.do_crc:=false;
|
|
hp:=current_module^.sourcefiles^.files;
|
|
{ write source files directly in good order }
|
|
j:=0;
|
|
while assigned(hp) do
|
|
begin
|
|
inc(j);
|
|
hp:=hp^.ref_next;
|
|
end;
|
|
while j>0 do
|
|
begin
|
|
hp:=current_module^.sourcefiles^.files;
|
|
for i:=1 to j-1 do
|
|
hp:=hp^.ref_next;
|
|
current_ppu^.putstring(hp^.name^);
|
|
dec(j);
|
|
end;
|
|
current_ppu^.writeentry(ibsourcefiles);
|
|
current_ppu^.do_crc:=true;
|
|
end;
|
|
|
|
procedure writeusedmacro(p:pnamedindexobject);
|
|
begin
|
|
if pmacro(p)^.is_used or pmacro(p)^.defined_at_startup then
|
|
begin
|
|
current_ppu^.putstring(p^.name);
|
|
current_ppu^.putbyte(byte(pmacro(p)^.defined_at_startup));
|
|
current_ppu^.putbyte(byte(pmacro(p)^.is_used));
|
|
end;
|
|
end;
|
|
|
|
procedure writeusedmacros;
|
|
begin
|
|
current_ppu^.do_crc:=false;
|
|
current_scanner^.macros^.foreach({$ifdef FPCPROCVAR}@{$endif}writeusedmacro);
|
|
current_ppu^.writeentry(ibusedmacros);
|
|
current_ppu^.do_crc:=true;
|
|
end;
|
|
|
|
|
|
procedure writeusedunit;
|
|
var
|
|
hp : pused_unit;
|
|
begin
|
|
numberunits;
|
|
hp:=pused_unit(current_module^.used_units.first);
|
|
while assigned(hp) do
|
|
begin
|
|
{ implementation units should not change
|
|
the CRC PM }
|
|
current_ppu^.do_crc:=hp^.in_interface;
|
|
current_ppu^.putstring(hp^.name^);
|
|
{ the checksum should not affect the crc of this unit ! (PFV) }
|
|
current_ppu^.do_crc:=false;
|
|
current_ppu^.putlongint(hp^.checksum);
|
|
current_ppu^.putlongint(hp^.interface_checksum);
|
|
current_ppu^.putbyte(byte(hp^.in_interface));
|
|
current_ppu^.do_crc:=true;
|
|
hp:=pused_unit(hp^.next);
|
|
end;
|
|
current_ppu^.do_interface_crc:=true;
|
|
current_ppu^.writeentry(ibloadunit);
|
|
end;
|
|
|
|
|
|
procedure writelinkcontainer(var p:tlinkcontainer;id:byte;strippath:boolean);
|
|
var
|
|
hcontainer : tlinkcontainer;
|
|
s : string;
|
|
mask : longint;
|
|
begin
|
|
hcontainer.init;
|
|
while not p.empty do
|
|
begin
|
|
s:=p.get(mask);
|
|
if strippath then
|
|
current_ppu^.putstring(SplitFileName(s))
|
|
else
|
|
current_ppu^.putstring(s);
|
|
current_ppu^.putlongint(mask);
|
|
hcontainer.insert(s,mask);
|
|
end;
|
|
current_ppu^.writeentry(id);
|
|
p:=hcontainer;
|
|
end;
|
|
|
|
|
|
procedure writeunitas(const s : string;unittable : punitsymtable;only_crc : boolean);
|
|
begin
|
|
Message1(unit_u_ppu_write,s);
|
|
|
|
{ create unit flags }
|
|
with Current_Module^ do
|
|
begin
|
|
{$ifdef GDB}
|
|
if cs_gdb_dbx in aktglobalswitches then
|
|
flags:=flags or uf_has_dbx;
|
|
{$endif GDB}
|
|
if target_os.endian=endian_big then
|
|
flags:=flags or uf_big_endian;
|
|
if cs_browser in aktmoduleswitches then
|
|
flags:=flags or uf_has_browser;
|
|
if cs_local_browser in aktmoduleswitches then
|
|
flags:=flags or uf_local_browser;
|
|
end;
|
|
|
|
{$ifdef Test_Double_checksum_write}
|
|
If only_crc then
|
|
Assign(CRCFile,s+'.INT')
|
|
else
|
|
Assign(CRCFile,s+'.IMP');
|
|
Rewrite(CRCFile);
|
|
{$endif def Test_Double_checksum_write}
|
|
{ open ppufile }
|
|
current_ppu:=new(pppufile,init(s));
|
|
current_ppu^.crc_only:=only_crc;
|
|
if not current_ppu^.create then
|
|
Message(unit_f_ppu_cannot_write);
|
|
|
|
{$ifdef Test_Double_checksum}
|
|
if only_crc then
|
|
begin
|
|
new(current_ppu^.crc_test);
|
|
new(current_ppu^.crc_test2);
|
|
end
|
|
else
|
|
begin
|
|
current_ppu^.crc_test:=Current_Module^.crc_array;
|
|
current_ppu^.crc_index:=Current_Module^.crc_size;
|
|
current_ppu^.crc_test2:=Current_Module^.crc_array2;
|
|
current_ppu^.crc_index2:=Current_Module^.crc_size2;
|
|
end;
|
|
{$endif def Test_Double_checksum}
|
|
|
|
current_ppu^.change_endian:=source_os.endian<>target_os.endian;
|
|
{ write symbols and definitions }
|
|
unittable^.writeasunit;
|
|
|
|
{ flush to be sure }
|
|
current_ppu^.flush;
|
|
{ create and write header }
|
|
current_ppu^.header.size:=current_ppu^.size;
|
|
current_ppu^.header.checksum:=current_ppu^.crc;
|
|
current_ppu^.header.interface_checksum:=current_ppu^.interface_crc;
|
|
current_ppu^.header.compiler:=wordversion;
|
|
current_ppu^.header.cpu:=word(target_cpu);
|
|
current_ppu^.header.target:=word(target_info.target);
|
|
current_ppu^.header.flags:=current_module^.flags;
|
|
If not only_crc then
|
|
current_ppu^.writeheader;
|
|
{ save crc in current_module also }
|
|
current_module^.crc:=current_ppu^.crc;
|
|
current_module^.interface_crc:=current_ppu^.interface_crc;
|
|
if only_crc then
|
|
begin
|
|
{$ifdef Test_Double_checksum}
|
|
Current_Module^.crc_array:=current_ppu^.crc_test;
|
|
current_ppu^.crc_test:=nil;
|
|
Current_Module^.crc_size:=current_ppu^.crc_index2;
|
|
Current_Module^.crc_array2:=current_ppu^.crc_test2;
|
|
current_ppu^.crc_test2:=nil;
|
|
Current_Module^.crc_size2:=current_ppu^.crc_index2;
|
|
{$endif def Test_Double_checksum}
|
|
closecurrentppu;
|
|
end;
|
|
{$ifdef Test_Double_checksum_write}
|
|
close(CRCFile);
|
|
{$endif Test_Double_checksum_write}
|
|
end;
|
|
|
|
|
|
procedure readusedmacros;
|
|
var
|
|
hs : string;
|
|
mac : pmacro;
|
|
was_defined_at_startup,
|
|
was_used : boolean;
|
|
begin
|
|
while not current_ppu^.endofentry do
|
|
begin
|
|
hs:=current_ppu^.getstring;
|
|
was_defined_at_startup:=boolean(current_ppu^.getbyte);
|
|
was_used:=boolean(current_ppu^.getbyte);
|
|
mac:=pmacro(current_scanner^.macros^.search(hs));
|
|
if assigned(mac) then
|
|
begin
|
|
{$ifndef EXTDEBUG}
|
|
{ if we don't have the sources why tell }
|
|
if current_module^.sources_avail then
|
|
{$endif ndef EXTDEBUG}
|
|
if (not was_defined_at_startup) and
|
|
was_used and
|
|
mac^.defined_at_startup then
|
|
Message2(unit_h_cond_not_set_in_last_compile,hs,current_module^.mainsource^);
|
|
end
|
|
else { not assigned }
|
|
if was_defined_at_startup and
|
|
was_used then
|
|
Message2(unit_h_cond_not_set_in_last_compile,hs,current_module^.mainsource^);
|
|
end;
|
|
end;
|
|
|
|
procedure readsourcefiles;
|
|
var
|
|
temp,hs : string;
|
|
temp_dir : string;
|
|
main_dir : string;
|
|
incfile_found,
|
|
main_found,
|
|
is_main : boolean;
|
|
ppufiletime,
|
|
source_time : longint;
|
|
hp : pinputfile;
|
|
begin
|
|
ppufiletime:=getnamedfiletime(current_module^.ppufilename^);
|
|
current_module^.sources_avail:=true;
|
|
is_main:=true;
|
|
main_dir:='';
|
|
while not current_ppu^.endofentry do
|
|
begin
|
|
hs:=current_ppu^.getstring;
|
|
temp_dir:='';
|
|
if (current_module^.flags and uf_in_library)<>0 then
|
|
begin
|
|
current_module^.sources_avail:=false;
|
|
temp:=' library';
|
|
end
|
|
else if pos('Macro ',hs)=1 then
|
|
begin
|
|
{ we don't want to find this file }
|
|
{ but there is a problem with file indexing !! }
|
|
temp:='';
|
|
end
|
|
else
|
|
begin
|
|
{ check the date of the source files }
|
|
Source_Time:=GetNamedFileTime(current_module^.path^+hs);
|
|
incfile_found:=false;
|
|
main_found:=false;
|
|
if Source_Time<>-1 then
|
|
hs:=current_module^.path^+hs
|
|
else
|
|
if not(is_main) then
|
|
begin
|
|
Source_Time:=GetNamedFileTime(main_dir+hs);
|
|
if Source_Time<>-1 then
|
|
hs:=main_dir+hs;
|
|
end;
|
|
if (Source_Time=-1) then
|
|
begin
|
|
if is_main then
|
|
temp_dir:=unitsearchpath.FindFile(hs,main_found)
|
|
else
|
|
temp_dir:=includesearchpath.FindFile(hs,incfile_found);
|
|
if incfile_found or main_found then
|
|
begin
|
|
hs:=temp_dir+hs;
|
|
Source_Time:=GetNamedFileTime(hs);
|
|
end
|
|
end;
|
|
if Source_Time=-1 then
|
|
begin
|
|
current_module^.sources_avail:=false;
|
|
temp:=' not found';
|
|
end
|
|
else
|
|
begin
|
|
if main_found then
|
|
main_dir:=temp_dir;
|
|
{ time newer? But only allow if the file is not searched
|
|
in the include path (PFV), else you've problems with
|
|
units which use the same includefile names }
|
|
if incfile_found then
|
|
temp:=' found'
|
|
else
|
|
begin
|
|
temp:=' time '+filetimestring(source_time);
|
|
if (source_time>ppufiletime) then
|
|
begin
|
|
current_module^.do_compile:=true;
|
|
current_module^.recompile_reason:=rr_sourcenewer;
|
|
temp:=temp+' *'
|
|
end;
|
|
end;
|
|
end;
|
|
new(hp,init(hs));
|
|
{ the indexing is wrong here PM }
|
|
current_module^.sourcefiles^.register_file(hp);
|
|
end;
|
|
if is_main then
|
|
begin
|
|
stringdispose(current_module^.mainsource);
|
|
current_module^.mainsource:=stringdup(hs);
|
|
end;
|
|
Message1(unit_u_ppu_source,hs+temp);
|
|
is_main:=false;
|
|
end;
|
|
{ check if we want to rebuild every unit, only if the sources are
|
|
available }
|
|
if do_build and current_module^.sources_avail then
|
|
begin
|
|
current_module^.do_compile:=true;
|
|
current_module^.recompile_reason:=rr_build;
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure readloadunit;
|
|
var
|
|
hs : string;
|
|
intfchecksum,
|
|
checksum : longint;
|
|
in_interface : boolean;
|
|
begin
|
|
while not current_ppu^.endofentry do
|
|
begin
|
|
hs:=current_ppu^.getstring;
|
|
checksum:=current_ppu^.getlongint;
|
|
intfchecksum:=current_ppu^.getlongint;
|
|
in_interface:=(current_ppu^.getbyte<>0);
|
|
current_module^.used_units.concat(new(pused_unit,init_to_load(hs,checksum,intfchecksum,in_interface)));
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure readlinkcontainer(var p:tlinkcontainer);
|
|
var
|
|
s : string;
|
|
m : longint;
|
|
begin
|
|
while not current_ppu^.endofentry do
|
|
begin
|
|
s:=current_ppu^.getstring;
|
|
m:=current_ppu^.getlongint;
|
|
p.insert(s,m);
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure load_interface;
|
|
var
|
|
b : byte;
|
|
newmodulename : string;
|
|
begin
|
|
{ read interface part }
|
|
repeat
|
|
b:=current_ppu^.readentry;
|
|
case b of
|
|
ibmodulename :
|
|
begin
|
|
newmodulename:=current_ppu^.getstring;
|
|
if upper(newmodulename)<>current_module^.modulename^ then
|
|
Message2(unit_f_unit_name_error,current_module^.realmodulename^,newmodulename);
|
|
stringdispose(current_module^.modulename);
|
|
stringdispose(current_module^.realmodulename);
|
|
current_module^.modulename:=stringdup(upper(newmodulename));
|
|
current_module^.realmodulename:=stringdup(newmodulename);
|
|
end;
|
|
ibsourcefiles :
|
|
readsourcefiles;
|
|
ibusedmacros :
|
|
readusedmacros;
|
|
ibloadunit :
|
|
readloadunit;
|
|
iblinkunitofiles :
|
|
readlinkcontainer(current_module^.LinkUnitOFiles);
|
|
iblinkunitstaticlibs :
|
|
readlinkcontainer(current_module^.LinkUnitStaticLibs);
|
|
iblinkunitsharedlibs :
|
|
readlinkcontainer(current_module^.LinkUnitSharedLibs);
|
|
iblinkotherofiles :
|
|
readlinkcontainer(current_module^.LinkotherOFiles);
|
|
iblinkotherstaticlibs :
|
|
readlinkcontainer(current_module^.LinkotherStaticLibs);
|
|
iblinkothersharedlibs :
|
|
readlinkcontainer(current_module^.LinkotherSharedLibs);
|
|
ibendinterface :
|
|
break;
|
|
else
|
|
Message1(unit_f_ppu_invalid_entry,tostr(b));
|
|
end;
|
|
until false;
|
|
end;
|
|
|
|
|
|
{****************************************************************************
|
|
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
|
|
{$ifdef GDB}
|
|
storeGlobalTypeCount : pword;
|
|
{$endif GDB}
|
|
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 tstoredsymtable.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
|
|
pstoredsymtable(current_module^.localsymtable)^.load_browser;
|
|
end;
|
|
|
|
|
|
procedure tunitsymtable.writeasunit;
|
|
var
|
|
pu : pused_unit;
|
|
begin
|
|
{ first the unitname }
|
|
current_ppu^.putstring(current_module^.realmodulename^);
|
|
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
|
|
pstoredsymtable(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
|
|
pstoredsymtable(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
|
|
pstoredsymtable(current_module^.localsymtable)^.write_browser;
|
|
|
|
{ the last entry ibend is written automaticly }
|
|
end;
|
|
|
|
|
|
{$ifdef GDB}
|
|
function tunitsymtable.getnewtypecount : word;
|
|
|
|
begin
|
|
if not (cs_gdb_dbx in aktglobalswitches) then
|
|
getnewtypecount:=tsymtable.getnewtypecount
|
|
else
|
|
if symtabletype = staticsymtable then
|
|
getnewtypecount:=tsymtable.getnewtypecount
|
|
else
|
|
begin
|
|
getnewtypecount:=unittypecount;
|
|
inc(unittypecount);
|
|
end;
|
|
end;
|
|
|
|
|
|
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({$ifdef FPCPROCVAR}@{$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}
|
|
|
|
{*****************************************************************************
|
|
Helper Routines
|
|
*****************************************************************************}
|
|
|
|
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 duplicatesym(sym:psym);
|
|
var
|
|
st : psymtable;
|
|
begin
|
|
Message1(sym_e_duplicate_id,sym^.realname);
|
|
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;
|
|
|
|
|
|
procedure identifier_not_found(const s:string);
|
|
begin
|
|
Message1(sym_e_id_not_found,s);
|
|
{ show a fatal that you need -S2 or -Sd, but only
|
|
if we just parsed the a token that has m_class }
|
|
if not(m_class in aktmodeswitches) and
|
|
(s=pattern) and
|
|
(tokeninfo^[idtoken].keyword=m_class) then
|
|
Message(parser_f_need_objfpc_or_delphi_mode);
|
|
end;
|
|
|
|
|
|
|
|
{*****************************************************************************
|
|
Search
|
|
*****************************************************************************}
|
|
|
|
procedure getsym(const s : stringid;notfounderror : boolean);
|
|
var
|
|
speedvalue : longint;
|
|
begin
|
|
speedvalue:=getspeedvalue(s);
|
|
lastsrsym:=nil;
|
|
srsymtable:=symtablestack;
|
|
while assigned(srsymtable) do
|
|
begin
|
|
srsym:=psym(srsymtable^.speedsearch(s,speedvalue));
|
|
if assigned(srsym) then
|
|
exit
|
|
else
|
|
srsymtable:=srsymtable^.next;
|
|
end;
|
|
if notfounderror then
|
|
begin
|
|
identifier_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:=psym(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
|
|
identifier_not_found(s);
|
|
end
|
|
else
|
|
identifier_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:=psym(hsymtab^.search(symbol));
|
|
search_a_symtable:=res;
|
|
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 := psym(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 := pdef(ptypesym(srsym)^.restype.def);
|
|
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:=psym(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({$ifdef FPCPROCVAR}@{$endif}testfordefaultproperty);
|
|
if assigned(_defaultprop) then
|
|
break;
|
|
pd:=pd^.childof;
|
|
end;
|
|
search_default_property:=_defaultprop;
|
|
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]) 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
|
|
****************************************************************************}
|
|
|
|
procedure InitSymtable;
|
|
var
|
|
token : ttoken;
|
|
begin
|
|
{ Reset symbolstack }
|
|
registerdef:=false;
|
|
read_member:=false;
|
|
symtablestack:=nil;
|
|
systemunit:=nil;
|
|
{$ifdef GDB}
|
|
firstglobaldef:=nil;
|
|
lastglobaldef:=nil;
|
|
globaltypecount:=1;
|
|
pglobaltypecount:=@globaltypecount;
|
|
{$endif GDB}
|
|
{ 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}
|
|
{$ifdef MEMDEBUG}
|
|
writeln('Manglednames: ',manglenamesize,' bytes');
|
|
{$endif}
|
|
end;
|
|
|
|
end.
|
|
{
|
|
$Log$
|
|
Revision 1.20 2000-12-10 14:14:51 florian
|
|
* fixed web bug 1203: class fields can be now redefined
|
|
in Delphi mode though I don't like this :/
|
|
|
|
Revision 1.19 2000/11/30 22:16:49 florian
|
|
* moved to i386
|
|
|
|
Revision 1.18 2000/11/29 00:30:42 florian
|
|
* unused units removed from uses clause
|
|
* some changes for widestrings
|
|
|
|
Revision 1.17 2000/11/28 00:28:07 pierre
|
|
* stabs fixing
|
|
|
|
Revision 1.1.2.8 2000/11/17 11:14:37 pierre
|
|
* one more class stabs fix
|
|
|
|
Revision 1.16 2000/11/12 22:17:47 peter
|
|
* some realname updates for messages
|
|
|
|
Revision 1.15 2000/11/06 15:54:15 florian
|
|
* fixed two bugs to get make cycle work, but it's not enough
|
|
|
|
Revision 1.14 2000/11/04 14:25:22 florian
|
|
+ merged Attila's changes for interfaces, not tested yet
|
|
|
|
Revision 1.13 2000/11/01 23:04:38 peter
|
|
* tprocdef.fullprocname added for better casesensitve writing of
|
|
procedures
|
|
|
|
Revision 1.12 2000/10/31 22:02:52 peter
|
|
* symtable splitted, no real code changes
|
|
|
|
Revision 1.1.2.7 2000/10/16 19:43:04 pierre
|
|
* trying to correct class stabss once more
|
|
|
|
Revision 1.11 2000/10/15 07:47:53 peter
|
|
* unit names and procedure names are stored mixed case
|
|
|
|
Revision 1.10 2000/10/14 10:14:53 peter
|
|
* moehrendorf oct 2000 rewrite
|
|
|
|
Revision 1.9 2000/10/01 19:48:25 peter
|
|
* lot of compile updates for cg11
|
|
|
|
Revision 1.8 2000/09/24 15:06:29 peter
|
|
* use defines.inc
|
|
|
|
Revision 1.7 2000/08/27 16:11:54 peter
|
|
* moved some util functions from globals,cobjects to cutils
|
|
* splitted files into finput,fmodule
|
|
|
|
Revision 1.6 2000/08/21 11:27:45 pierre
|
|
* fix the stabs problems
|
|
|
|
Revision 1.5 2000/08/20 14:58:41 peter
|
|
* give fatal if objfpc/delphi mode things are found (merged)
|
|
|
|
Revision 1.1.2.6 2000/08/20 14:56:46 peter
|
|
* give fatal if objfpc/delphi mode things are found
|
|
|
|
Revision 1.4 2000/08/16 18:33:54 peter
|
|
* splitted namedobjectitem.next into indexnext and listnext so it
|
|
can be used in both lists
|
|
* don't allow "word = word" type definitions (merged)
|
|
|
|
Revision 1.3 2000/08/08 19:28:57 peter
|
|
* memdebug/memory patches (merged)
|
|
* only once illegal directive (merged)
|
|
|
|
}
|