fpc/compiler/fmodule.pas
fpc 790a4fe2d3 * log and id tags removed
git-svn-id: trunk@42 -
2005-05-21 09:42:41 +00:00

832 lines
26 KiB
ObjectPascal

{
Copyright (c) 1998-2002 by Florian Klaempfl
This unit implements the first loading and searching of the modules
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 fmodule;
{$i fpcdefs.inc}
{$ifdef go32v2}
{$define shortasmprefix}
{$endif}
{$ifdef watcom}
{$define shortasmprefix}
{$endif}
{$ifdef tos}
{$define shortasmprefix}
{$endif}
{$ifdef OS2}
{ Allthough OS/2 supports long filenames I play it safe and
use 8.3 filenames, because this allows the compiler to run
on a FAT partition. (DM) }
{$define shortasmprefix}
{$endif}
interface
uses
cutils,cclasses,
globals,finput,
symbase,symsym,aasmbase;
type
trecompile_reason = (rr_unknown,
rr_noppu,rr_sourcenewer,rr_build,rr_crcchanged
);
TExternalsItem=class(TLinkedListItem)
public
found : longbool;
data : pstring;
constructor Create(const s:string);
Destructor Destroy;override;
end;
tlinkcontaineritem=class(tlinkedlistitem)
public
data : pstring;
needlink : cardinal;
constructor Create(const s:string;m:cardinal);
destructor Destroy;override;
end;
tlinkcontainer=class(tlinkedlist)
procedure add(const s : string;m:cardinal);
function get(var m:cardinal) : string;
function getusemask(mask:cardinal) : string;
function find(const s:string):boolean;
end;
tmodule = class;
tused_unit = class;
tunitmaprec = record
u : tmodule;
{ number of references }
refs : longint;
{ index in the derefmap }
derefidx : longint;
end;
punitmap = ^tunitmaprec;
tderefmaprec = record
u : tmodule;
{ modulename, used during ppu load }
modulename : pstring;
end;
pderefmap = ^tderefmaprec;
tmodule = class(tmodulebase)
do_reload, { force reloading of the unit }
do_compile, { need to compile the sources }
sources_avail, { if all sources are reachable }
interface_compiled, { if the interface section has been parsed/compiled/loaded }
is_stab_written,
is_reset,
is_unit,
in_interface, { processing the implementation part? }
in_global : boolean; { allow global settings }
mode_switch_allowed : boolean; { Whether a mode switch is still allowed at this point in the parsing.}
mainfilepos : tfileposinfo;
recompile_reason : trecompile_reason; { the reason why the unit should be recompiled }
crc,
interface_crc : cardinal;
flags : cardinal; { the PPU flags }
islibrary : boolean; { if it is a library (win32 dll) }
moduleid : longint;
unitmap : punitmap; { mapping of all used units }
unitmapsize : longint; { number of units in the map }
derefmap : pderefmap; { mapping of all units needed for deref }
derefmapcnt : longint; { number of units in the map }
derefmapsize : longint; { number of units in the map }
derefdataintflen : longint;
derefdata : tdynamicarray;
globalsymtable, { pointer to the global symtable of this unit }
localsymtable : tsymtable;{ pointer to the local symtable of this unit }
globalmacrosymtable, { pointer to the global macro symtable of this unit }
localmacrosymtable : tsymtable;{ pointer to the local macro symtable of this unit }
scanner : pointer; { scanner object used }
procinfo : pointer; { current procedure being compiled }
loaded_from : tmodule;
uses_imports : boolean; { Set if the module imports from DLL's.}
imports : tlinkedlist;
_exports : tlinkedlist;
externals : tlinkedlist; {Only for DLL scanners by using Unix-style $LINKLIB }
resourcefiles : tstringlist;
linkunitofiles,
linkunitstaticlibs,
linkunitsharedlibs,
linkotherofiles, { objects,libs loaded from the source }
linkothersharedlibs, { using $L or $LINKLIB or import lib (for linux) }
linkotherstaticlibs : tlinkcontainer;
used_units : tlinkedlist;
dependent_units : tlinkedlist;
localunitsearchpath, { local searchpaths }
localobjectsearchpath,
localincludesearchpath,
locallibrarysearchpath : TSearchPathList;
asmprefix : pstring; { prefix for the smartlink asmfiles }
librarydata : tasmlibrarydata; { librarydata for this module }
{create creates a new module which name is stored in 's'. LoadedFrom
points to the module calling it. It is nil for the first compiled
module. This allow inheritence of all path lists. MUST pay attention
to that when creating link.res!!!!(mazen)}
constructor create(LoadedFrom:TModule;const s:string;_is_unit:boolean);
destructor destroy;override;
procedure reset;virtual;
procedure adddependency(callermodule:tmodule);
procedure flagdependent(callermodule:tmodule);
function addusedunit(hp:tmodule;inuses:boolean;usym:tunitsym):tused_unit;
procedure updatemaps;
function derefidx_unit(id:longint):longint;
function resolve_unit(id:longint):tmodule;
procedure allunitsused;
procedure setmodulename(const s:string);
end;
tused_unit = class(tlinkedlistitem)
checksum,
interface_checksum : cardinal;
in_uses,
in_interface : boolean;
u : tmodule;
unitsym : tunitsym;
constructor create(_u : tmodule;intface,inuses:boolean;usym:tunitsym);
end;
tdependent_unit = class(tlinkedlistitem)
u : tmodule;
constructor create(_u : tmodule);
end;
var
main_module : tmodule; { Main module of the program }
current_module : tmodule; { Current module which is compiled or loaded }
compiled_module : tmodule; { Current module which is compiled }
usedunits : tlinkedlist; { Used units for this program }
loaded_units : tlinkedlist; { All loaded units }
SmartLinkOFiles : TStringList; { List of .o files which are generated,
used to delete them after linking }
function get_source_file(moduleindex,fileindex : longint) : tinputfile;
procedure addloadedunit(hp:tmodule);
implementation
uses
{$IFDEF USE_SYSUTILS}
SysUtils,
GlobType,
{$ELSE USE_SYSUTILS}
dos,
{$ENDIF USE_SYSUTILS}
verbose,systems,
scanner,ppu,
procinfo;
{*****************************************************************************
Global Functions
*****************************************************************************}
function get_source_file(moduleindex,fileindex : longint) : tinputfile;
var
hp : tmodule;
begin
hp:=tmodule(loaded_units.first);
while assigned(hp) and (hp.unit_index<>moduleindex) do
hp:=tmodule(hp.next);
if assigned(hp) then
get_source_file:=hp.sourcefiles.get_file(fileindex)
else
get_source_file:=nil;
end;
procedure addloadedunit(hp:tmodule);
begin
hp.moduleid:=loaded_units.count;
loaded_units.concat(hp);
end;
{****************************************************************************
TLinkContainerItem
****************************************************************************}
constructor TLinkContainerItem.Create(const s:string;m:cardinal);
begin
inherited Create;
data:=stringdup(s);
needlink:=m;
end;
destructor TLinkContainerItem.Destroy;
begin
stringdispose(data);
end;
{****************************************************************************
TLinkContainer
****************************************************************************}
procedure TLinkContainer.add(const s : string;m:cardinal);
begin
inherited concat(TLinkContainerItem.Create(s,m));
end;
function TLinkContainer.get(var m:cardinal) : string;
var
p : tlinkcontaineritem;
begin
p:=tlinkcontaineritem(inherited getfirst);
if p=nil then
begin
get:='';
m:=0;
end
else
begin
get:=p.data^;
m:=p.needlink;
p.free;
end;
end;
function TLinkContainer.getusemask(mask:cardinal) : string;
var
p : tlinkcontaineritem;
found : boolean;
begin
found:=false;
repeat
p:=tlinkcontaineritem(inherited getfirst);
if p=nil then
begin
getusemask:='';
exit;
end;
getusemask:=p.data^;
found:=(p.needlink and mask)<>0;
p.free;
until found;
end;
function TLinkContainer.find(const s:string):boolean;
var
newnode : tlinkcontaineritem;
begin
find:=false;
newnode:=tlinkcontaineritem(First);
while assigned(newnode) do
begin
if newnode.data^=s then
begin
find:=true;
exit;
end;
newnode:=tlinkcontaineritem(newnode.next);
end;
end;
{****************************************************************************
TExternalsItem
****************************************************************************}
constructor tExternalsItem.Create(const s:string);
begin
inherited Create;
found:=false;
data:=stringdup(s);
end;
destructor tExternalsItem.Destroy;
begin
stringdispose(data);
inherited;
end;
{****************************************************************************
TUSED_UNIT
****************************************************************************}
constructor tused_unit.create(_u : tmodule;intface,inuses:boolean;usym:tunitsym);
begin
u:=_u;
in_interface:=intface;
in_uses:=inuses;
unitsym:=usym;
if _u.state=ms_compiled then
begin
checksum:=u.crc;
interface_checksum:=u.interface_crc;
end
else
begin
checksum:=0;
interface_checksum:=0;
end;
end;
{****************************************************************************
TDENPENDENT_UNIT
****************************************************************************}
constructor tdependent_unit.create(_u : tmodule);
begin
u:=_u;
end;
{****************************************************************************
TMODULE
****************************************************************************}
constructor tmodule.create(LoadedFrom:TModule;const s:string;_is_unit:boolean);
var
p : dirstr;
n : namestr;
e : extstr;
begin
{$IFDEF USE_SYSUTILS}
p := SplitPath(s);
n := SplitName(s);
e := SplitExtension(s);
{$ELSE USE_SYSUTILS}
FSplit(s,p,n,e);
{$ENDIF USE_SYSUTILS}
{ Programs have the name 'Program' to don't conflict with dup id's }
if _is_unit then
inherited create(n)
else
inherited create('Program');
mainsource:=stringdup(s);
{ Dos has the famous 8.3 limit :( }
{$ifdef shortasmprefix}
asmprefix:=stringdup(FixFileName('as'));
{$else}
asmprefix:=stringdup(FixFileName(n));
{$endif}
setfilename(p+n,true);
localunitsearchpath:=TSearchPathList.Create;
localobjectsearchpath:=TSearchPathList.Create;
localincludesearchpath:=TSearchPathList.Create;
locallibrarysearchpath:=TSearchPathList.Create;
used_units:=TLinkedList.Create;
dependent_units:=TLinkedList.Create;
resourcefiles:=TStringList.Create;
linkunitofiles:=TLinkContainer.Create;
linkunitstaticlibs:=TLinkContainer.Create;
linkunitsharedlibs:=TLinkContainer.Create;
linkotherofiles:=TLinkContainer.Create;
linkotherstaticlibs:=TLinkContainer.Create;
linkothersharedlibs:=TLinkContainer.Create;
crc:=0;
interface_crc:=0;
flags:=0;
scanner:=nil;
unitmap:=nil;
unitmapsize:=0;
derefmap:=nil;
derefmapsize:=0;
derefmapcnt:=0;
derefdata:=TDynamicArray.Create(1024);
derefdataintflen:=0;
globalsymtable:=nil;
localsymtable:=nil;
globalmacrosymtable:=nil;
localmacrosymtable:=nil;
loaded_from:=LoadedFrom;
do_reload:=false;
do_compile:=false;
sources_avail:=true;
mainfilepos.line:=0;
mainfilepos.column:=0;
mainfilepos.fileindex:=0;
recompile_reason:=rr_unknown;
in_interface:=true;
in_global:=true;
is_unit:=_is_unit;
islibrary:=false;
is_stab_written:=false;
is_reset:=false;
mode_switch_allowed:= true;
uses_imports:=false;
imports:=TLinkedList.Create;
_exports:=TLinkedList.Create;
externals:=TLinkedList.Create;
librarydata:=tasmlibrarydata.create(realmodulename^);
end;
destructor tmodule.Destroy;
var
{$ifdef MEMDEBUG}
d : tmemdebug;
{$endif}
i : longint;
hpi : tprocinfo;
begin
if assigned(unitmap) then
freemem(unitmap);
if assigned(derefmap) then
begin
for i:=0 to derefmapcnt-1 do
stringdispose(derefmap[i].modulename);
freemem(derefmap);
end;
if assigned(imports) then
imports.free;
if assigned(_exports) then
_exports.free;
if assigned(externals) then
externals.free;
if assigned(scanner) then
begin
{ also update current_scanner if it was pointing
to this module }
if current_scanner=tscannerfile(scanner) then
current_scanner:=nil;
tscannerfile(scanner).free;
end;
if assigned(procinfo) then
begin
if current_procinfo=tprocinfo(procinfo) then
current_procinfo:=nil;
{ release procinfo tree }
while assigned(procinfo) do
begin
hpi:=tprocinfo(procinfo).parent;
tprocinfo(procinfo).free;
procinfo:=hpi;
end;
end;
used_units.free;
dependent_units.free;
resourcefiles.Free;
linkunitofiles.Free;
linkunitstaticlibs.Free;
linkunitsharedlibs.Free;
linkotherofiles.Free;
linkotherstaticlibs.Free;
linkothersharedlibs.Free;
stringdispose(objfilename);
stringdispose(newfilename);
stringdispose(ppufilename);
stringdispose(staticlibfilename);
stringdispose(sharedlibfilename);
stringdispose(exefilename);
stringdispose(outputpath);
stringdispose(path);
stringdispose(realmodulename);
stringdispose(mainsource);
stringdispose(asmprefix);
localunitsearchpath.Free;
localobjectsearchpath.free;
localincludesearchpath.free;
locallibrarysearchpath.free;
{$ifdef MEMDEBUG}
d:=tmemdebug.create(modulename^+' - symtable');
{$endif}
derefdata.free;
if assigned(globalsymtable) then
globalsymtable.free;
if assigned(localsymtable) then
localsymtable.free;
if assigned(globalmacrosymtable) then
globalmacrosymtable.free;
if assigned(localmacrosymtable) then
localmacrosymtable.free;
{$ifdef MEMDEBUG}
d.free;
{$endif}
{$ifdef MEMDEBUG}
d:=tmemdebug.create(modulename^+' - librarydata');
{$endif}
librarydata.free;
{$ifdef MEMDEBUG}
d.free;
{$endif}
stringdispose(modulename);
inherited Destroy;
end;
procedure tmodule.reset;
var
hpi : tprocinfo;
i : longint;
begin
if assigned(scanner) then
begin
{ also update current_scanner if it was pointing
to this module }
if current_scanner=tscannerfile(scanner) then
current_scanner:=nil;
tscannerfile(scanner).free;
scanner:=nil;
end;
if assigned(procinfo) then
begin
if current_procinfo=tprocinfo(procinfo) then
current_procinfo:=nil;
{ release procinfo tree }
while assigned(procinfo) do
begin
hpi:=tprocinfo(procinfo).parent;
tprocinfo(procinfo).free;
procinfo:=hpi;
end;
end;
if assigned(globalsymtable) then
begin
globalsymtable.free;
globalsymtable:=nil;
end;
if assigned(localsymtable) then
begin
localsymtable.free;
localsymtable:=nil;
end;
if assigned(globalmacrosymtable) then
begin
globalmacrosymtable.free;
globalmacrosymtable:=nil;
end;
if assigned(localmacrosymtable) then
begin
localmacrosymtable.free;
localmacrosymtable:=nil;
end;
derefdata.free;
derefdata:=TDynamicArray.Create(1024);
if assigned(unitmap) then
begin
freemem(unitmap);
unitmap:=nil;
end;
if assigned(derefmap) then
begin
for i:=0 to derefmapcnt-1 do
stringdispose(derefmap[i].modulename);
freemem(derefmap);
derefmap:=nil;
end;
unitmapsize:=0;
derefmapsize:=0;
derefmapcnt:=0;
derefdataintflen:=0;
sourcefiles.free;
sourcefiles:=tinputfilemanager.create;
librarydata.free;
librarydata:=tasmlibrarydata.create(realmodulename^);
imports.free;
imports:=tlinkedlist.create;
_exports.free;
_exports:=tlinkedlist.create;
externals.free;
externals:=tlinkedlist.create;
used_units.free;
used_units:=TLinkedList.Create;
dependent_units.free;
dependent_units:=TLinkedList.Create;
resourcefiles.Free;
resourcefiles:=TStringList.Create;
linkunitofiles.Free;
linkunitofiles:=TLinkContainer.Create;
linkunitstaticlibs.Free;
linkunitstaticlibs:=TLinkContainer.Create;
linkunitsharedlibs.Free;
linkunitsharedlibs:=TLinkContainer.Create;
linkotherofiles.Free;
linkotherofiles:=TLinkContainer.Create;
linkotherstaticlibs.Free;
linkotherstaticlibs:=TLinkContainer.Create;
linkothersharedlibs.Free;
linkothersharedlibs:=TLinkContainer.Create;
uses_imports:=false;
do_compile:=false;
do_reload:=false;
interface_compiled:=false;
in_interface:=true;
in_global:=true;
mode_switch_allowed:=true;
is_stab_written:=false;
is_reset:=false;
crc:=0;
interface_crc:=0;
flags:=0;
mainfilepos.line:=0;
mainfilepos.column:=0;
mainfilepos.fileindex:=0;
recompile_reason:=rr_unknown;
{
The following fields should not
be reset:
mainsource
state
loaded_from
sources_avail
}
end;
procedure tmodule.adddependency(callermodule:tmodule);
begin
{ This is not needed for programs }
if not callermodule.is_unit then
exit;
Message2(unit_u_add_depend_to,callermodule.modulename^,modulename^);
dependent_units.concat(tdependent_unit.create(callermodule));
end;
procedure tmodule.flagdependent(callermodule:tmodule);
var
pm : tdependent_unit;
begin
{ flag all units that depend on this unit for reloading }
pm:=tdependent_unit(current_module.dependent_units.first);
while assigned(pm) do
begin
{ We do not have to reload the unit that wants to load
this unit, unless this unit is already compiled during
the loading }
if (pm.u=callermodule) and
(pm.u.state<>ms_compiled) then
Message1(unit_u_no_reload_is_caller,pm.u.modulename^)
else
if pm.u.state=ms_second_compile then
Message1(unit_u_no_reload_in_second_compile,pm.u.modulename^)
else
begin
pm.u.do_reload:=true;
Message1(unit_u_flag_for_reload,pm.u.modulename^);
end;
pm:=tdependent_unit(pm.next);
end;
end;
function tmodule.addusedunit(hp:tmodule;inuses:boolean;usym:tunitsym):tused_unit;
var
pu : tused_unit;
begin
pu:=tused_unit.create(hp,in_interface,inuses,usym);
used_units.concat(pu);
addusedunit:=pu;
end;
procedure tmodule.updatemaps;
var
oldmapsize : longint;
hp : tmodule;
i : longint;
begin
{ Extend unitmap }
oldmapsize:=unitmapsize;
unitmapsize:=loaded_units.count;
reallocmem(unitmap,unitmapsize*sizeof(tunitmaprec));
fillchar(unitmap[oldmapsize],(unitmapsize-oldmapsize)*sizeof(tunitmaprec),0);
{ Extend Derefmap }
oldmapsize:=derefmapsize;
derefmapsize:=loaded_units.count;
reallocmem(derefmap,derefmapsize*sizeof(tderefmaprec));
fillchar(derefmap[oldmapsize],(derefmapsize-oldmapsize)*sizeof(tderefmaprec),0);
{ Add all units to unitmap }
hp:=tmodule(loaded_units.first);
i:=0;
while assigned(hp) do
begin
if hp.moduleid>=unitmapsize then
internalerror(200501151);
{ Verify old entries }
if (i<oldmapsize) then
begin
if (hp.moduleid<>i) or
(unitmap[hp.moduleid].u<>hp) then
internalerror(200501156);
end
else
begin
unitmap[hp.moduleid].u:=hp;
unitmap[hp.moduleid].derefidx:=-1;
end;
inc(i);
hp:=tmodule(hp.next);
end;
end;
function tmodule.derefidx_unit(id:longint):longint;
begin
if id>=unitmapsize then
internalerror(2005011511);
if unitmap[id].derefidx=-1 then
begin
unitmap[id].derefidx:=derefmapcnt;
inc(derefmapcnt);
derefmap[unitmap[id].derefidx].u:=unitmap[id].u;
end;
if unitmap[id].derefidx>=derefmapsize then
internalerror(2005011514);
result:=unitmap[id].derefidx;
end;
function tmodule.resolve_unit(id:longint):tmodule;
var
hp : tmodule;
begin
if id>=derefmapsize then
internalerror(200306231);
result:=derefmap[id].u;
if not assigned(result) then
begin
if not assigned(derefmap[id].modulename) or
(derefmap[id].modulename^='') then
internalerror(200501159);
hp:=tmodule(loaded_units.first);
while assigned(hp) do
begin
if hp.modulename^=derefmap[id].modulename^ then
break;
hp:=tmodule(hp.next);
end;
if not assigned(hp) then
internalerror(2005011510);
derefmap[id].u:=hp;
result:=hp;
end;
end;
procedure tmodule.allunitsused;
var
pu : tused_unit;
begin
pu:=tused_unit(used_units.first);
while assigned(pu) do
begin
if assigned(pu.u.globalsymtable) then
begin
if unitmap[pu.u.moduleid].u<>pu.u then
internalerror(200501157);
{ Give a note when the unit is not referenced, skip
this is for units with an initialization/finalization }
if (unitmap[pu.u.moduleid].refs=0) and
((pu.u.flags and (uf_init or uf_finalize))=0) then
CGMessagePos2(pu.unitsym.fileinfo,sym_n_unit_not_used,pu.u.realmodulename^,realmodulename^);
end;
pu:=tused_unit(pu.next);
end;
end;
procedure tmodule.setmodulename(const s:string);
begin
stringdispose(modulename);
stringdispose(realmodulename);
modulename:=stringdup(upper(s));
realmodulename:=stringdup(s);
{ also update asmlibrary names }
librarydata.name:=modulename^;
librarydata.realname:=realmodulename^;
end;
end.