diff options
Diffstat (limited to 'closures/compiler/symtable.pas')
-rw-r--r-- | closures/compiler/symtable.pas | 3217 |
1 files changed, 3217 insertions, 0 deletions
diff --git a/closures/compiler/symtable.pas b/closures/compiler/symtable.pas new file mode 100644 index 0000000000..9f382ae279 --- /dev/null +++ b/closures/compiler/symtable.pas @@ -0,0 +1,3217 @@ +{ + Copyright (c) 1998-2002 by Florian Klaempfl, Pierre Muller + + This unit handles the symbol tables + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + **************************************************************************** +} +unit symtable; + +{$i fpcdefs.inc} + +interface + + uses + { common } + cutils,cclasses, + { global } + cpuinfo,globtype,tokens, + { symtable } + symconst,symbase,symtype,symdef,symsym, + { ppu } + ppu, + { assembler } + aasmtai,aasmdata + ; + + +{**************************************************************************** + Symtable types +****************************************************************************} + + type + tstoredsymtable = class(TSymtable) + private + b_needs_init_final : boolean; + procedure _needs_init_final(sym:TObject;arg:pointer); + procedure check_forward(sym:TObject;arg:pointer); + procedure labeldefined(sym:TObject;arg:pointer); + procedure varsymbolused(sym:TObject;arg:pointer); + procedure TestPrivate(sym:TObject;arg:pointer); + procedure objectprivatesymbolused(sym:TObject;arg:pointer); + procedure loaddefs(ppufile:tcompilerppufile); + procedure loadsyms(ppufile:tcompilerppufile); + procedure writedefs(ppufile:tcompilerppufile); + procedure writesyms(ppufile:tcompilerppufile); + public + procedure insert(sym:TSymEntry;checkdup:boolean=true);override; + procedure delete(sym:TSymEntry);override; + { load/write } + procedure ppuload(ppufile:tcompilerppufile);virtual; + procedure ppuwrite(ppufile:tcompilerppufile);virtual; + procedure buildderef;virtual; + procedure buildderefimpl;virtual; + procedure deref;virtual; + procedure derefimpl;virtual; + function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; + procedure allsymbolsused; + procedure allprivatesused; + procedure check_forwards; + procedure checklabels; + function needs_init_final : boolean; + procedure testfordefaultproperty(sym:TObject;arg:pointer); + end; + + tabstractrecordsymtable = class(tstoredsymtable) + public + usefieldalignment, { alignment to use for fields (PACKRECORDS value), C_alignment is C style } + recordalignment, { alignment desired when inserting this record } + fieldalignment, { alignment current alignment used when fields are inserted } + padalignment : shortint; { size to a multiple of which the symtable has to be rounded up } + constructor create(const n:string;usealign:shortint); + procedure ppuload(ppufile:tcompilerppufile);override; + procedure ppuwrite(ppufile:tcompilerppufile);override; + procedure alignrecord(fieldoffset:asizeint;varalign:shortint); + procedure addfield(sym:tfieldvarsym;vis:tvisibility); + procedure addalignmentpadding; + procedure insertdef(def:TDefEntry);override; + function is_packed: boolean; + function has_single_field(out sym:tfieldvarsym): boolean; + function get_unit_symtable: tsymtable; + protected + { size in bytes including padding } + _datasize : asizeint; + { size in bits of the data in case of bitpacked record. Only important during construction, } + { no need to save in/restore from ppu file. datasize is always (databitsize+7) div 8. } + databitsize : asizeint; + { size in bytes of padding } + _paddingsize : word; + procedure setdatasize(val: asizeint); + public + function iscurrentunit: boolean; override; + property datasize : asizeint read _datasize write setdatasize; + property paddingsize: word read _paddingsize write _paddingsize; + end; + + trecordsymtable = class(tabstractrecordsymtable) + public + constructor create(const n:string;usealign:shortint); + procedure insertunionst(unionst : trecordsymtable;offset : longint); + end; + + tObjectSymtable = class(tabstractrecordsymtable) + public + constructor create(adefowner:tdef;const n:string;usealign:shortint); + function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; + end; + + { tabstractlocalsymtable } + + tabstractlocalsymtable = class(tstoredsymtable) + public + procedure ppuwrite(ppufile:tcompilerppufile);override; + function count_locals:longint; + end; + + tlocalsymtable = class(tabstractlocalsymtable) + public + constructor create(adefowner:tdef;level:byte); + function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; + end; + + { tparasymtable } + + tparasymtable = class(tabstractlocalsymtable) + public + readonly: boolean; + constructor create(adefowner:tdef;level:byte); + function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; + procedure insertdef(def:TDefEntry);override; + end; + + tabstractuniTSymtable = class(tstoredsymtable) + public + constructor create(const n : string;id:word); + function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; + function iscurrentunit:boolean;override; + procedure insertunit(sym:TSymEntry); + end; + + tglobalsymtable = class(tabstractuniTSymtable) + public + unittypecount : word; + constructor create(const n : string;id:word); + procedure ppuload(ppufile:tcompilerppufile);override; + procedure ppuwrite(ppufile:tcompilerppufile);override; + end; + + tstaticsymtable = class(tabstractuniTSymtable) + public + constructor create(const n : string;id:word); + procedure ppuload(ppufile:tcompilerppufile);override; + procedure ppuwrite(ppufile:tcompilerppufile);override; + function checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean;override; + end; + + tspecializesymtable = class(tglobalsymtable) + public + function iscurrentunit:boolean;override; + end; + + twithsymtable = class(TSymtable) + withrefnode : tobject; { tnode } + constructor create(aowner:tdef;ASymList:TFPHashObjectList;refnode:tobject{tnode}); + destructor destroy;override; + procedure clear;override; + procedure insertdef(def:TDefEntry);override; + end; + + tstt_excepTSymtable = class(TSymtable) + public + constructor create; + end; + + tmacrosymtable = class(tstoredsymtable) + public + constructor create(exported: boolean); + end; + + { tenumsymtable } + + tenumsymtable = class(tstoredsymtable) + public + procedure insert(sym: TSymEntry; checkdup: boolean = true); override; + constructor create(adefowner:tdef); + end; + + { tarraysymtable } + + tarraysymtable = class(tstoredsymtable) + public + procedure insertdef(def:TDefEntry);override; + constructor create(adefowner:tdef); + end; + + var + systemunit : tglobalsymtable; { pointer to the system unit } + + +{**************************************************************************** + Functions +****************************************************************************} + +{*** Misc ***} + function FullTypeName(def,otherdef:tdef):string; + function generate_nested_name(symtable:tsymtable;delimiter:string):string; + procedure incompatibletypes(def1,def2:tdef); + procedure hidesym(sym:TSymEntry); + procedure duplicatesym(var hashedid:THashedIDString;dupsym,origsym:TSymEntry); + function handle_generic_dummysym(sym:TSymEntry;var symoptions:tsymoptions):boolean; + +{*** Search ***} + procedure addsymref(sym:tsym); + function is_owned_by(childdef,ownerdef:tdef):boolean; + function sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean; + function is_visible_for_object(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean; + function is_visible_for_object(pd:tprocdef;contextobjdef:tabstractrecorddef):boolean; + function is_visible_for_object(sym:tsym;contextobjdef:tabstractrecorddef):boolean; + function searchsym(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + function searchsym_maybe_with_symoption(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;searchoption:boolean;option:tsymoption):boolean; + { searches for a symbol with the given name that has the given option in + symoptions set } + function searchsym_with_symoption(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;option:tsymoption):boolean; + function searchsym_type(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + function searchsym_in_module(pm:pointer;const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + function searchsym_in_named_module(const unitname, symname: TIDString; out srsym: tsym; out srsymtable: tsymtable): boolean; + function searchsym_in_class(classh,contextclassh:tobjectdef;const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;searchhelper:boolean):boolean; + function searchsym_in_record(recordh:tabstractrecorddef;const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + function searchsym_in_class_by_msgint(classh:tobjectdef;msgid:longint;out srdef : tdef;out srsym:tsym;out srsymtable:TSymtable):boolean; + function searchsym_in_class_by_msgstr(classh:tobjectdef;const s:string;out srsym:tsym;out srsymtable:TSymtable):boolean; + { searches symbols inside of a helper's implementation } + function searchsym_in_helper(classh,contextclassh:tobjectdef;const s: TIDString;out srsym:tsym;out srsymtable:TSymtable;aHasInherited:boolean):boolean; + function search_system_type(const s: TIDString): ttypesym; + function try_search_system_type(const s: TIDString): ttypesym; + function search_named_unit_globaltype(const unitname, typename: TIDString; throwerror: boolean): ttypesym; + function search_struct_member(pd : tabstractrecorddef;const s : string):tsym; + function search_assignment_operator(from_def,to_def:Tdef;explicit:boolean):Tprocdef; + function search_enumerator_operator(from_def,to_def:Tdef):Tprocdef; + { searches for the helper definition that's currently active for pd } + function search_last_objectpascal_helper(pd,contextclassh : tabstractrecorddef;out odef : tobjectdef):boolean; + { searches whether the symbol s is available in the currently active } + { helper for pd } + function search_objectpascal_helper(pd,contextclassh : tabstractrecorddef;const s : string; out srsym: tsym; out srsymtable: tsymtable):boolean; + function search_objc_helper(pd : tobjectdef;const s : string; out srsym: tsym; out srsymtable: tsymtable):boolean; + function search_objc_method(const s : string; out srsym: tsym; out srsymtable: tsymtable):boolean; + {Looks for macro s (must be given in upper case) in the macrosymbolstack, } + {and returns it if found. Returns nil otherwise.} + function search_macro(const s : string):tsym; + { Additionally to searching for a macro, also checks whether it's still } + { actually defined (could be disable using "undef") } + function defined_macro(const s : string):boolean; + +{*** Object Helpers ***} + function search_default_property(pd : tabstractrecorddef) : tpropertysym; + function find_real_objcclass_definition(pd: tobjectdef; erroronfailure: boolean): tobjectdef; + +{*** Macro Helpers ***} + {If called initially, the following procedures manipulate macros in } + {initialmacrotable, otherwise they manipulate system macros local to a module.} + {Name can be given in any case (it will be converted to upper case).} + procedure def_system_macro(const name : string); + procedure set_system_macro(const name, value : string); + procedure set_system_compvar(const name, value : string); + procedure undef_system_macro(const name : string); + +{*** symtable stack ***} +{ $ifdef DEBUG + procedure test_symtablestack; + procedure list_symtablestack; + $endif DEBUG} + +{$ifdef UNITALIASES} + type + punit_alias = ^tunit_alias; + tunit_alias = object(TNamedIndexItem) + newname : pshortstring; + 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 + overloaded_names : array [NOTOKEN..last_overloaded] of string[16] = ( + { NOTOKEN } 'error', + { _PLUS } 'plus', + { _MINUS } 'minus', + { _STAR } 'star', + { _SLASH } 'slash', + { _EQ } 'equal', + { _GT } 'greater', + { _LT } 'lower', + { _GTE } 'greater_or_equal', + { _LTE } 'lower_or_equal', + { _NE } 'not_equal', + { _SYMDIF } 'sym_diff', + { _STARSTAR } 'starstar', + { _OP_AS } 'as', + { _OP_IN } 'in', + { _OP_IS } 'is', + { _OP_OR } 'or', + { _OP_AND } 'and', + { _OP_DIV } 'div', + { _OP_MOD } 'mod', + { _OP_NOT } 'not', + { _OP_SHL } 'shl', + { _OP_SHR } 'shr', + { _OP_XOR } 'xor', + { _ASSIGNMENT } 'assign', + { _OP_EXPLICIT } 'explicit', + { _OP_ENUMERATOR } 'enumerator', + { _OP_INC } 'inc', + { _OP_DEC } 'dec'); + + + +implementation + + uses + { global } + verbose,globals, + { target } + systems, + { symtable } + symutil,defcmp,defutil, + { module } + fmodule, + { codegen } + procinfo + ; + + + var + dupnr : longint; { unique number for duplicate symbols } + +{***************************************************************************** + TStoredSymtable +*****************************************************************************} + + procedure tstoredsymtable.insert(sym:TSymEntry;checkdup:boolean=true); + begin + inherited insert(sym,checkdup); + end; + + + procedure tstoredsymtable.delete(sym:TSymEntry); + begin + inherited delete(sym); + end; + + + procedure tstoredsymtable.ppuload(ppufile:tcompilerppufile); + begin + { load the table's flags } + if ppufile.readentry<>ibsymtableoptions then + Message(unit_f_ppu_read_error); + ppufile.getsmallset(tableoptions); + + { load definitions } + loaddefs(ppufile); + + { load symbols } + loadsyms(ppufile); + end; + + + procedure tstoredsymtable.ppuwrite(ppufile:tcompilerppufile); + begin + { write the table's flags } + ppufile.putsmallset(tableoptions); + ppufile.writeentry(ibsymtableoptions); + + { write definitions } + writedefs(ppufile); + + { write symbols } + writesyms(ppufile); + end; + + + procedure tstoredsymtable.loaddefs(ppufile:tcompilerppufile); + var + def : tdef; + b : byte; + begin + { load start of definition section, which holds the amount of defs } + if ppufile.readentry<>ibstartdefs then + Message(unit_f_ppu_read_error); + { read definitions } + repeat + b:=ppufile.readentry; + case b of + ibpointerdef : def:=tpointerdef.ppuload(ppufile); + ibarraydef : def:=tarraydef.ppuload(ppufile); + iborddef : def:=torddef.ppuload(ppufile); + ibfloatdef : def:=tfloatdef.ppuload(ppufile); + ibprocdef : def:=tprocdef.ppuload(ppufile); + ibshortstringdef : def:=tstringdef.loadshort(ppufile); + iblongstringdef : def:=tstringdef.loadlong(ppufile); + ibansistringdef : def:=tstringdef.loadansi(ppufile); + ibwidestringdef : def:=tstringdef.loadwide(ppufile); + ibunicodestringdef : def:=tstringdef.loadunicode(ppufile); + ibrecorddef : def:=trecorddef.ppuload(ppufile); + ibobjectdef : def:=tobjectdef.ppuload(ppufile); + ibenumdef : def:=tenumdef.ppuload(ppufile); + ibsetdef : def:=tsetdef.ppuload(ppufile); + ibprocvardef : def:=tprocvardef.ppuload(ppufile); + ibfiledef : def:=tfiledef.ppuload(ppufile); + ibclassrefdef : def:=tclassrefdef.ppuload(ppufile); + ibformaldef : def:=tformaldef.ppuload(ppufile); + ibvariantdef : def:=tvariantdef.ppuload(ppufile); + ibundefineddef : def:=tundefineddef.ppuload(ppufile); + ibenddefs : break; + ibend : Message(unit_f_ppu_read_error); + else + Message1(unit_f_ppu_invalid_entry,tostr(b)); + end; + InsertDef(def); + until false; + end; + + + procedure tstoredsymtable.loadsyms(ppufile:tcompilerppufile); + var + b : byte; + sym : tsym; + begin + { load start of definition section, which holds the amount of defs } + if ppufile.readentry<>ibstartsyms then + Message(unit_f_ppu_read_error); + { now read the symbols } + repeat + b:=ppufile.readentry; + case b of + ibtypesym : sym:=ttypesym.ppuload(ppufile); + ibprocsym : sym:=tprocsym.ppuload(ppufile); + ibconstsym : sym:=tconstsym.ppuload(ppufile); + ibstaticvarsym : sym:=tstaticvarsym.ppuload(ppufile); + iblocalvarsym : sym:=tlocalvarsym.ppuload(ppufile); + ibparavarsym : sym:=tparavarsym.ppuload(ppufile); + ibfieldvarsym : sym:=tfieldvarsym.ppuload(ppufile); + ibabsolutevarsym : sym:=tabsolutevarsym.ppuload(ppufile); + ibenumsym : sym:=tenumsym.ppuload(ppufile); + ibpropertysym : sym:=tpropertysym.ppuload(ppufile); + ibunitsym : sym:=tunitsym.ppuload(ppufile); + iblabelsym : sym:=tlabelsym.ppuload(ppufile); + ibsyssym : sym:=tsyssym.ppuload(ppufile); + ibmacrosym : sym:=tmacro.ppuload(ppufile); + ibnamespacesym : sym:=tnamespacesym.ppuload(ppufile); + ibendsyms : break; + ibend : Message(unit_f_ppu_read_error); + else + Message1(unit_f_ppu_invalid_entry,tostr(b)); + end; + Insert(sym,false); + until false; + end; + + + procedure tstoredsymtable.writedefs(ppufile:tcompilerppufile); + var + i : longint; + def : tstoreddef; + begin + { each definition get a number, write then the amount of defs to the + ibstartdef entry } + ppufile.putlongint(DefList.count); + ppufile.writeentry(ibstartdefs); + { now write the definition } + for i:=0 to DefList.Count-1 do + begin + def:=tstoreddef(DefList[i]); + def.ppuwrite(ppufile); + end; + { write end of definitions } + ppufile.writeentry(ibenddefs); + end; + + + procedure tstoredsymtable.writesyms(ppufile:tcompilerppufile); + var + i : longint; + sym : Tstoredsym; + begin + { each definition get a number, write then the amount of syms and the + datasize to the ibsymdef entry } + ppufile.putlongint(SymList.count); + ppufile.writeentry(ibstartsyms); + { foreach is used to write all symbols } + for i:=0 to SymList.Count-1 do + begin + sym:=tstoredsym(SymList[i]); + sym.ppuwrite(ppufile); + end; + { end of symbols } + ppufile.writeentry(ibendsyms); + end; + + + procedure tstoredsymtable.buildderef; + var + i : longint; + def : tstoreddef; + sym : tstoredsym; + begin + { interface definitions } + for i:=0 to DefList.Count-1 do + begin + def:=tstoreddef(DefList[i]); + def.buildderef; + end; + { interface symbols } + for i:=0 to SymList.Count-1 do + begin + sym:=tstoredsym(SymList[i]); + sym.buildderef; + end; + end; + + + procedure tstoredsymtable.buildderefimpl; + var + i : longint; + def : tstoreddef; + begin + { implementation definitions } + for i:=0 to DefList.Count-1 do + begin + def:=tstoreddef(DefList[i]); + def.buildderefimpl; + end; + end; + + + procedure tstoredsymtable.deref; + var + i : longint; + def : tstoreddef; + sym : tstoredsym; + begin + { first deref the interface ttype symbols. This is needs + to be done before the interface defs are derefed, because + the interface defs can contain references to the type symbols + which then already need to contain a resolved typedef field (PFV) } + for i:=0 to SymList.Count-1 do + begin + sym:=tstoredsym(SymList[i]); + if sym.typ=typesym then + sym.deref; + end; + { interface definitions } + for i:=0 to DefList.Count-1 do + begin + def:=tstoreddef(DefList[i]); + def.deref; + end; + { interface symbols } + for i:=0 to SymList.Count-1 do + begin + sym:=tstoredsym(SymList[i]); + if sym.typ<>typesym then + sym.deref; + end; + end; + + + procedure tstoredsymtable.derefimpl; + var + i : longint; + def : tstoreddef; + begin + { implementation definitions } + for i:=0 to DefList.Count-1 do + begin + def:=tstoreddef(DefList[i]); + def.derefimpl; + end; + end; + + + function tstoredsymtable.checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean; + var + hsym : tsym; + begin + hsym:=tsym(FindWithHash(hashedid)); + if assigned(hsym) then + DuplicateSym(hashedid,sym,hsym); + result:=assigned(hsym); + end; + + +{************************************** + Callbacks +**************************************} + + procedure TStoredSymtable.check_forward(sym:TObject;arg:pointer); + begin + if tsym(sym).typ=procsym then + tprocsym(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, + only test objects declarations, not type renamings } + else + if (tsym(sym).typ=typesym) and + assigned(ttypesym(sym).typedef) and + (ttypesym(sym).typedef.typesym=ttypesym(sym)) and + (ttypesym(sym).typedef.typ in [objectdef,recorddef]) then + tabstractrecorddef(ttypesym(sym).typedef).check_forwards; + end; + + + procedure TStoredSymtable.labeldefined(sym:TObject;arg:pointer); + begin + if (tsym(sym).typ=labelsym) and + not(tlabelsym(sym).defined) then + begin + if tlabelsym(sym).used then + Message1(sym_e_label_used_and_not_defined,tlabelsym(sym).realname) + else + Message1(sym_w_label_not_defined,tlabelsym(sym).realname); + end; + end; + + + procedure TStoredSymtable.varsymbolused(sym:TObject;arg:pointer); + begin + if (tsym(sym).typ in [staticvarsym,localvarsym,paravarsym,fieldvarsym]) and + ((tsym(sym).owner.symtabletype in + [parasymtable,localsymtable,ObjectSymtable,recordsymtable,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) } + { also don't complain about unused symbols in generic procedures } + { and methods } + if (Errorcount<>0) or + ([vo_is_hidden_para,vo_is_funcret] * tabstractvarsym(sym).varoptions = [vo_is_hidden_para]) or + (sp_internal in tsym(sym).symoptions) or + ((assigned(tsym(sym).owner.defowner) and + (tsym(sym).owner.defowner.typ=procdef) and + (df_generic in tprocdef(tsym(sym).owner.defowner).defoptions))) then + exit; + if (tstoredsym(sym).refs=0) then + begin + if (vo_is_funcret in tabstractvarsym(sym).varoptions) then + begin + { don't warn about the result of constructors } + if ((tsym(sym).owner.symtabletype<>localsymtable) or + (tprocdef(tsym(sym).owner.defowner).proctypeoption<>potype_constructor)) and + not(cs_opt_nodedfa in current_settings.optimizerswitches) then + MessagePos(tsym(sym).fileinfo,sym_w_function_result_not_set) + end + else if (tsym(sym).owner.symtabletype=parasymtable) then + MessagePos1(tsym(sym).fileinfo,sym_h_para_identifier_not_used,tsym(sym).prettyname) + else if (tsym(sym).owner.symtabletype in [ObjectSymtable,recordsymtable]) then + MessagePos2(tsym(sym).fileinfo,sym_n_private_identifier_not_used,tabstractrecorddef(tsym(sym).owner.defowner).GetTypeName,tsym(sym).prettyname) + else + MessagePos1(tsym(sym).fileinfo,sym_n_local_identifier_not_used,tsym(sym).prettyname); + end + else if tabstractvarsym(sym).varstate in [vs_written,vs_initialised] then + begin + if (tsym(sym).owner.symtabletype=parasymtable) then + begin + if not(tabstractvarsym(sym).varspez in [vs_var,vs_out,vs_constref]) and + not(vo_is_funcret in tabstractvarsym(sym).varoptions) then + MessagePos1(tsym(sym).fileinfo,sym_h_para_identifier_only_set,tsym(sym).prettyname) + end + else if (tsym(sym).owner.symtabletype in [ObjectSymtable,recordsymtable]) then + MessagePos2(tsym(sym).fileinfo,sym_n_private_identifier_only_set,tabstractrecorddef(tsym(sym).owner.defowner).GetTypeName,tsym(sym).prettyname) + else if tabstractvarsym(sym).varoptions*[vo_is_funcret,vo_is_public,vo_is_external]=[] then + MessagePos1(tsym(sym).fileinfo,sym_n_local_identifier_only_set,tsym(sym).prettyname); + end + else if (tabstractvarsym(sym).varstate = vs_read_not_warned) and + ([vo_is_public,vo_is_external] * tabstractvarsym(sym).varoptions = []) then + MessagePos1(tsym(sym).fileinfo,sym_w_identifier_only_read,tsym(sym).prettyname) + end + else if ((tsym(sym).owner.symtabletype in + [ObjectSymtable,parasymtable,localsymtable,staticsymtable,recordsymtable])) then + begin + if (Errorcount<>0) or + (sp_internal in tsym(sym).symoptions) then + exit; + { do not claim for inherited private fields !! } + if (tsym(sym).refs=0) and (tsym(sym).owner.symtabletype in [ObjectSymtable,recordsymtable]) then + case tsym(sym).typ of + typesym: + MessagePos2(tsym(sym).fileinfo,sym_n_private_type_not_used,tabstractrecorddef(tsym(sym).owner.defowner).GetTypeName,tsym(sym).prettyname); + constsym: + MessagePos2(tsym(sym).fileinfo,sym_n_private_const_not_used,tabstractrecorddef(tsym(sym).owner.defowner).GetTypeName,tsym(sym).prettyname); + propertysym: + MessagePos2(tsym(sym).fileinfo,sym_n_private_property_not_used,tabstractrecorddef(tsym(sym).owner.defowner).GetTypeName,tsym(sym).prettyname); + else + MessagePos2(tsym(sym).fileinfo,sym_n_private_method_not_used,tabstractrecorddef(tsym(sym).owner.defowner).GetTypeName,tsym(sym).prettyname); + end + { units references are problematic } + else + begin + if (tsym(sym).refs=0) and + not(tsym(sym).typ in [enumsym,unitsym,namespacesym]) and + not(is_funcret_sym(tsym(sym))) and + { don't complain about compiler generated syms for specializations, see also #13405 } + not((tsym(sym).typ=typesym) and (df_specialization in ttypesym(sym).typedef.defoptions) and + (pos('$',ttypesym(sym).Realname)<>0)) and + ( + (tsym(sym).typ<>procsym) or + ((tsym(sym).owner.symtabletype=staticsymtable) and + not current_module.is_unit) + ) and + { don't complain about alias for hidden _cmd parameter to + obj-c methods } + not((tsym(sym).typ in [localvarsym,paravarsym,absolutevarsym]) and + (vo_is_msgsel in tabstractvarsym(sym).varoptions)) then + MessagePos2(tsym(sym).fileinfo,sym_h_local_symbol_not_used,SymTypeName[tsym(sym).typ],tsym(sym).prettyname); + end; + end; + end; + + + procedure TStoredSymtable.TestPrivate(sym:TObject;arg:pointer); + begin + if tsym(sym).visibility in [vis_private,vis_strictprivate] then + varsymbolused(sym,arg); + end; + + + procedure TStoredSymtable.objectprivatesymbolused(sym:TObject;arg:pointer); + begin + { + Don't test simple object aliases PM + } + if (tsym(sym).typ=typesym) and + (ttypesym(sym).typedef.typ in [objectdef,recorddef]) and + (ttypesym(sym).typedef.typesym=tsym(sym)) then + tabstractrecorddef(ttypesym(sym).typedef).symtable.SymList.ForEachCall(@TestPrivate,nil); + end; + + + procedure tstoredsymtable.testfordefaultproperty(sym:TObject;arg:pointer); + begin + if (tsym(sym).typ=propertysym) and + (ppo_defaultproperty in tpropertysym(sym).propoptions) then + ppointer(arg)^:=sym; + end; + + +{*********************************************** + Process all entries +***********************************************} + + { checks, if all procsyms and methods are defined } + procedure tstoredsymtable.check_forwards; + begin + SymList.ForEachCall(@check_forward,nil); + end; + + + procedure tstoredsymtable.checklabels; + begin + SymList.ForEachCall(@labeldefined,nil); + end; + + + procedure tstoredsymtable.allsymbolsused; + begin + SymList.ForEachCall(@varsymbolused,nil); + end; + + + procedure tstoredsymtable.allprivatesused; + begin + SymList.ForEachCall(@objectprivatesymbolused,nil); + end; + + + procedure TStoredSymtable._needs_init_final(sym:TObject;arg:pointer); + begin + if b_needs_init_final then + exit; + { don't check static symbols - they can be present in structures only and + always have a reference to a symbol defined on unit level } + if sp_static in tsym(sym).symoptions then + exit; + case tsym(sym).typ of + fieldvarsym, + staticvarsym, + localvarsym, + paravarsym : + begin + if is_managed_type(tabstractvarsym(sym).vardef) then + b_needs_init_final:=true; + end; + end; + end; + + + { returns true, if p contains data which needs init/final code } + function tstoredsymtable.needs_init_final : boolean; + begin + b_needs_init_final:=false; + SymList.ForEachCall(@_needs_init_final,nil); + needs_init_final:=b_needs_init_final; + end; + + +{**************************************************************************** + TAbstractRecordSymtable +****************************************************************************} + + constructor tabstractrecordsymtable.create(const n:string;usealign:shortint); + begin + inherited create(n); + moduleid:=current_module.moduleid; + _datasize:=0; + databitsize:=0; + recordalignment:=1; + usefieldalignment:=usealign; + padalignment:=1; + { recordalign C_alignment means C record packing, that starts + with an alignment of 1 } + case usealign of + C_alignment, + bit_alignment: + fieldalignment:=1; + mac68k_alignment: + fieldalignment:=2; + else + fieldalignment:=usealign; + end; + end; + + + procedure tabstractrecordsymtable.ppuload(ppufile:tcompilerppufile); + begin + if ppufile.readentry<>ibrecsymtableoptions then + Message(unit_f_ppu_read_error); + recordalignment:=shortint(ppufile.getbyte); + usefieldalignment:=shortint(ppufile.getbyte); + if (usefieldalignment=C_alignment) then + fieldalignment:=shortint(ppufile.getbyte); + inherited ppuload(ppufile); + end; + + + procedure tabstractrecordsymtable.ppuwrite(ppufile:tcompilerppufile); + var + oldtyp : byte; + begin + oldtyp:=ppufile.entrytyp; + ppufile.entrytyp:=subentryid; + { in case of classes using C alignment, the alignment of the parent + affects the alignment of fields of the childs } + ppufile.putbyte(byte(recordalignment)); + ppufile.putbyte(byte(usefieldalignment)); + if (usefieldalignment=C_alignment) then + ppufile.putbyte(byte(fieldalignment)); + ppufile.writeentry(ibrecsymtableoptions); + + inherited ppuwrite(ppufile); + + ppufile.entrytyp:=oldtyp; + end; + + + function field2recordalignment(fieldoffs, fieldalign: asizeint): asizeint; + begin + { optimal alignment of the record when declaring a variable of this } + { type is independent of the packrecords setting } + if (fieldoffs mod fieldalign) = 0 then + result:=fieldalign + else if (fieldalign >= 16) and + ((fieldoffs mod 16) = 0) and + ((fieldalign mod 16) = 0) then + result:=16 + else if (fieldalign >= 8) and + ((fieldoffs mod 8) = 0) and + ((fieldalign mod 8) = 0) then + result:=8 + else if (fieldalign >= 4) and + ((fieldoffs mod 4) = 0) and + ((fieldalign mod 4) = 0) then + result:=4 + else if (fieldalign >= 2) and + ((fieldoffs mod 2) = 0) and + ((fieldalign mod 2) = 0) then + result:=2 + else + result:=1; + end; + + procedure tabstractrecordsymtable.alignrecord(fieldoffset:asizeint;varalign:shortint); + var + varalignrecord: shortint; + begin + case usefieldalignment of + C_alignment: + varalignrecord:=used_align(varalign,current_settings.alignment.recordalignmin,current_settings.alignment.maxCrecordalign); + mac68k_alignment: + varalignrecord:=2; + else + varalignrecord:=field2recordalignment(fieldoffset,varalign); + end; + recordalignment:=max(recordalignment,varalignrecord); + end; + + procedure tabstractrecordsymtable.addfield(sym:tfieldvarsym;vis:tvisibility); + var + l : asizeint; + varalignfield, + varalign : shortint; + vardef : tdef; + begin + if (sym.owner<>self) then + internalerror(200602031); + if sym.fieldoffset<>-1 then + internalerror(200602032); + { set visibility for the symbol } + sym.visibility:=vis; + { this symbol can't be loaded to a register } + sym.varregable:=vr_none; + { Calculate field offset } + l:=sym.getsize; + vardef:=sym.vardef; + varalign:=vardef.alignment; + + case usefieldalignment of + bit_alignment: + begin + { bitpacking only happens for ordinals, the rest is aligned at } + { 1 byte (compatible with GPC/GCC) } + if is_ordinal(vardef) then + begin + sym.fieldoffset:=databitsize; + l:=sym.getpackedbitsize; + end + else + begin + databitsize:=_datasize*8; + sym.fieldoffset:=databitsize; + if (l>high(asizeint) div 8) then + Message(sym_e_segment_too_large); + l:=l*8; + end; + if varalign=0 then + varalign:=size_2_align(l); + recordalignment:=max(recordalignment,field2recordalignment(databitsize mod 8,varalign)); + { bit packed records are limited to high(aint) bits } + { instead of bytes to avoid double precision } + { arithmetic in offset calculations } + if int64(l)>high(asizeint)-sym.fieldoffset then + begin + Message(sym_e_segment_too_large); + _datasize:=high(asizeint); + databitsize:=high(asizeint); + end + else + begin + databitsize:=sym.fieldoffset+l; + _datasize:=(databitsize+7) div 8; + end; + { rest is not applicable } + exit; + end; + { Calc the alignment size for C style records } + C_alignment: + begin + if (varalign>4) and + ((varalign mod 4)<>0) and + (vardef.typ=arraydef) then + Message1(sym_w_wrong_C_pack,vardef.typename); + if varalign=0 then + varalign:=l; + if (fieldalignment<current_settings.alignment.maxCrecordalign) then + begin + if (varalign>16) and (fieldalignment<32) then + fieldalignment:=32 + else if (varalign>12) and (fieldalignment<16) then + fieldalignment:=16 + { 12 is needed for long double } + else if (varalign>8) and (fieldalignment<12) then + fieldalignment:=12 + else if (varalign>4) and (fieldalignment<8) then + fieldalignment:=8 + else if (varalign>2) and (fieldalignment<4) then + fieldalignment:=4 + else if (varalign>1) and (fieldalignment<2) then + fieldalignment:=2; + end; + fieldalignment:=min(fieldalignment,current_settings.alignment.maxCrecordalign); + end; + mac68k_alignment: + begin + { mac68k alignment (C description): + * char is aligned to 1 byte + * everything else (except vector) is aligned to 2 bytes + * vector is aligned to 16 bytes + } + if l>1 then + fieldalignment:=2 + else + fieldalignment:=1; + varalign:=2; + end; + end; + if varalign=0 then + varalign:=size_2_align(l); + varalignfield:=used_align(varalign,current_settings.alignment.recordalignmin,fieldalignment); + + sym.fieldoffset:=align(_datasize,varalignfield); + if l>high(asizeint)-sym.fieldoffset then + begin + Message(sym_e_segment_too_large); + _datasize:=high(aint); + end + else + _datasize:=sym.fieldoffset+l; + { Calc alignment needed for this record } + alignrecord(sym.fieldoffset,varalign); + end; + + + procedure tabstractrecordsymtable.addalignmentpadding; + var + padded_datasize: asizeint; + begin + { make the record size aligned correctly so it can be + used as elements in an array. For C records we + use the fieldalignment, because that is updated with the + used alignment. } + if (padalignment = 1) then + case usefieldalignment of + C_alignment: + padalignment:=fieldalignment; + { bitpacked } + bit_alignment: + padalignment:=1; + { mac68k: always round to multiple of 2 } + mac68k_alignment: + padalignment:=2; + { default/no packrecords specified } + 0: + padalignment:=recordalignment + { specific packrecords setting -> use as upper limit } + else + padalignment:=min(recordalignment,usefieldalignment); + end; + padded_datasize:=align(_datasize,padalignment); + _paddingsize:=padded_datasize-_datasize; + _datasize:=padded_datasize; + end; + + + procedure tabstractrecordsymtable.insertdef(def:TDefEntry); + begin + { Enums must also be available outside the record scope, + insert in the owner of this symtable } + if def.typ=enumdef then + defowner.owner.insertdef(def) + else + inherited insertdef(def); + end; + + + function tabstractrecordsymtable.is_packed: boolean; + begin + result:=usefieldalignment=bit_alignment; + end; + + + function tabstractrecordsymtable.has_single_field(out sym: tfieldvarsym): boolean; + var + i: longint; + begin + result:=false; + { If a record contains a union, it does not contain a "single + non-composite field" in the context of certain ABIs requiring + special treatment for such records } + if (defowner.typ=recorddef) and + trecorddef(defowner).isunion then + exit; + { a record/object can contain other things than fields } + for i:=0 to SymList.Count-1 do + begin + if tsym(symlist[i]).typ=fieldvarsym then + begin + if result then + begin + result:=false; + exit; + end; + result:=true; + sym:=tfieldvarsym(symlist[i]) + end; + end; + end; + + function tabstractrecordsymtable.get_unit_symtable: tsymtable; + begin + result:=defowner.owner; + while assigned(result) and (result.symtabletype in [ObjectSymtable,recordsymtable]) do + result:=result.defowner.owner; + end; + + procedure tabstractrecordsymtable.setdatasize(val: asizeint); + begin + _datasize:=val; + if (usefieldalignment=bit_alignment) then + { can overflow in non bitpacked records } + databitsize:=val*8; + end; + + function tabstractrecordsymtable.iscurrentunit: boolean; + begin + Result := Assigned(current_module) and (current_module.moduleid=moduleid); + end; + +{**************************************************************************** + TRecordSymtable +****************************************************************************} + + constructor trecordsymtable.create(const n:string;usealign:shortint); + begin + inherited create(n,usealign); + symtabletype:=recordsymtable; + 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 trecordsymtable.insertunionst(unionst : trecordsymtable;offset : longint); + var + sym : tsym; + def : tdef; + i : integer; + varalignrecord,varalign, + storesize,storealign : aint; + bitsize: tcgint; + begin + storesize:=_datasize; + storealign:=fieldalignment; + _datasize:=offset; + if (usefieldalignment=bit_alignment) then + databitsize:=offset*8; + + { We move the ownership of the defs and symbols to the new recordsymtable. + The old unionsymtable keeps the references, but doesn't own the + objects anymore } + unionst.DefList.OwnsObjects:=false; + unionst.SymList.OwnsObjects:=false; + + { copy symbols } + for i:=0 to unionst.SymList.Count-1 do + begin + sym:=TSym(unionst.SymList[i]); + if sym.typ<>fieldvarsym then + internalerror(200601272); + if tfieldvarsym(sym).fieldoffset=0 then + include(tfieldvarsym(sym).varoptions,vo_is_first_field); + + { add to this record symtable } +// unionst.SymList.List.List^[i].Data:=nil; + sym.ChangeOwner(self); + varalign:=tfieldvarsym(sym).vardef.alignment; + if varalign=0 then + varalign:=size_2_align(tfieldvarsym(sym).getsize); + { retrieve size } + if (usefieldalignment=bit_alignment) then + begin + { bit packed records are limited to high(aint) bits } + { instead of bytes to avoid double precision } + { arithmetic in offset calculations } + if is_ordinal(tfieldvarsym(sym).vardef) then + bitsize:=tfieldvarsym(sym).getpackedbitsize + else + begin + bitsize:=tfieldvarsym(sym).getsize; + if (bitsize>high(asizeint) div 8) then + Message(sym_e_segment_too_large); + bitsize:=bitsize*8; + end; + if bitsize>high(asizeint)-databitsize then + begin + Message(sym_e_segment_too_large); + _datasize:=high(aint); + databitsize:=high(aint); + end + else + begin + databitsize:=tfieldvarsym(sym).fieldoffset+offset*8; + _datasize:=(databitsize+7) div 8; + end; + tfieldvarsym(sym).fieldoffset:=databitsize; + varalignrecord:=field2recordalignment(tfieldvarsym(sym).fieldoffset div 8,varalign); + end + else + begin + if tfieldvarsym(sym).getsize>high(asizeint)-_datasize then + begin + Message(sym_e_segment_too_large); + _datasize:=high(aint); + end + else + _datasize:=tfieldvarsym(sym).fieldoffset+offset; + { update address } + tfieldvarsym(sym).fieldoffset:=_datasize; + varalignrecord:=field2recordalignment(tfieldvarsym(sym).fieldoffset,varalign); + end; + { update alignment of this record } + if (usefieldalignment<>C_alignment) and + (usefieldalignment<>mac68k_alignment) then + recordalignment:=max(recordalignment,varalignrecord); + end; + { update alignment for C records } + if (usefieldalignment=C_alignment) and + (usefieldalignment<>mac68k_alignment) then + recordalignment:=max(recordalignment,unionst.recordalignment); + { Register defs in the new record symtable } + for i:=0 to unionst.DefList.Count-1 do + begin + def:=TDef(unionst.DefList[i]); + def.ChangeOwner(self); + end; + _datasize:=storesize; + fieldalignment:=storealign; + { If a record contains a union, it does not contain a "single + non-composite field" in the context of certain ABIs requiring + special treatment for such records } + if defowner.typ=recorddef then + trecorddef(defowner).isunion:=true; + end; + + +{**************************************************************************** + TObjectSymtable +****************************************************************************} + + constructor tObjectSymtable.create(adefowner:tdef;const n:string;usealign:shortint); + begin + inherited create(n,usealign); + symtabletype:=ObjectSymtable; + defowner:=adefowner; + end; + + + function tObjectSymtable.checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean; + var + hsym : tsym; + begin + result:=false; + if not assigned(defowner) then + internalerror(200602061); + + { procsym and propertysym have special code + to override values in inherited classes. For other + symbols check for duplicates } + if not(sym.typ in [procsym,propertysym]) then + begin + { but private ids can be reused } + hsym:=search_struct_member(tobjectdef(defowner),hashedid.id); + if assigned(hsym) and + ( + ( + not(m_delphi in current_settings.modeswitches) and + is_visible_for_object(hsym,tobjectdef(defowner)) + ) or + ( + { In Delphi, you can repeat members of a parent class. You can't } + { do this for objects however, and you (obviouly) can't } + { declare two fields with the same name in a single class } + (m_delphi in current_settings.modeswitches) and + ( + is_object(tdef(defowner)) or + (hsym.owner = self) + ) + ) + ) then + begin + DuplicateSym(hashedid,sym,hsym); + result:=true; + end; + end + else + result:=inherited checkduplicate(hashedid,sym); + end; + + +{**************************************************************************** + TAbstractLocalSymtable +****************************************************************************} + + procedure tabstractlocalsymtable.ppuwrite(ppufile:tcompilerppufile); + var + oldtyp : byte; + begin + oldtyp:=ppufile.entrytyp; + ppufile.entrytyp:=subentryid; + + inherited ppuwrite(ppufile); + + ppufile.entrytyp:=oldtyp; + end; + + + function tabstractlocalsymtable.count_locals:longint; + var + i : longint; + sym : tsym; + begin + result:=0; + for i:=0 to SymList.Count-1 do + begin + sym:=tsym(SymList[i]); + { Count only varsyms, but ignore the funcretsym } + if (tsym(sym).typ in [localvarsym,paravarsym]) and + (tsym(sym)<>current_procinfo.procdef.funcretsym) and + (not(vo_is_parentfp in tabstractvarsym(sym).varoptions) or + (tstoredsym(sym).refs>0)) then + inc(result); + end; + end; + + +{**************************************************************************** + TLocalSymtable +****************************************************************************} + + constructor tlocalsymtable.create(adefowner:tdef;level:byte); + begin + inherited create(''); + defowner:=adefowner; + symtabletype:=localsymtable; + symtablelevel:=level; + end; + + + function tlocalsymtable.checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean; + var + hsym : tsym; + begin + if not assigned(defowner) or + (defowner.typ<>procdef) then + internalerror(200602042); + + result:=false; + hsym:=tsym(FindWithHash(hashedid)); + if assigned(hsym) then + begin + { a local and the function can have the same + name in TP and Delphi, but RESULT not } + if (m_duplicate_names in current_settings.modeswitches) and + (hsym.typ in [absolutevarsym,localvarsym]) and + (vo_is_funcret in tabstractvarsym(hsym).varoptions) and + not((m_result in current_settings.modeswitches) and + (vo_is_result in tabstractvarsym(hsym).varoptions)) then + HideSym(hsym) + else + DuplicateSym(hashedid,sym,hsym); + result:=true; + exit; + end; + + { check also parasymtable, this needs to be done here because + of the special situation with the funcret sym that needs to be + hidden for tp and delphi modes } + hsym:=tsym(tabstractprocdef(defowner).parast.FindWithHash(hashedid)); + if assigned(hsym) then + begin + { a local and the function can have the same + name in TP and Delphi, but RESULT not } + if (m_duplicate_names in current_settings.modeswitches) and + (sym.typ in [absolutevarsym,localvarsym]) and + (vo_is_funcret in tabstractvarsym(sym).varoptions) and + not((m_result in current_settings.modeswitches) and + (vo_is_result in tabstractvarsym(sym).varoptions)) then + Hidesym(sym) + else + DuplicateSym(hashedid,sym,hsym); + result:=true; + exit; + end; + + { check ObjectSymtable, skip this for funcret sym because + that will always be positive because it has the same name + as the procsym } + if not is_funcret_sym(sym) and + (defowner.typ=procdef) and + assigned(tprocdef(defowner).struct) and + (tprocdef(defowner).owner.defowner=tprocdef(defowner).struct) and + ( + not(m_delphi in current_settings.modeswitches) or + is_object(tprocdef(defowner).struct) + ) then + result:=tprocdef(defowner).struct.symtable.checkduplicate(hashedid,sym); + end; + + +{**************************************************************************** + TParaSymtable +****************************************************************************} + + constructor tparasymtable.create(adefowner:tdef;level:byte); + begin + inherited create(''); + readonly:=false; + defowner:=adefowner; + symtabletype:=parasymtable; + symtablelevel:=level; + end; + + + function tparasymtable.checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean; + begin + result:=inherited checkduplicate(hashedid,sym); + if result then + exit; + if not(m_duplicate_names in current_settings.modeswitches) and + assigned(defowner) and (defowner.typ=procdef) and + assigned(tprocdef(defowner).struct) and + (tprocdef(defowner).owner.defowner=tprocdef(defowner).struct) and + ( + not(m_delphi in current_settings.modeswitches) or + is_object(tprocdef(defowner).struct) + ) then + result:=tprocdef(defowner).struct.symtable.checkduplicate(hashedid,sym); + end; + + procedure tparasymtable.insertdef(def: TDefEntry); + begin + if readonly then + defowner.owner.insertdef(def) + else + inherited insertdef(def); + end; + + +{**************************************************************************** + TAbstractUniTSymtable +****************************************************************************} + + constructor tabstractuniTSymtable.create(const n : string;id:word); + begin + inherited create(n); + moduleid:=id; + end; + + + function tabstractuniTSymtable.checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean; + var + hsym : tsym; + begin + result:=false; + hsym:=tsym(FindWithHash(hashedid)); + if assigned(hsym) then + begin + if (sym is tstoredsym) and handle_generic_dummysym(hsym,tstoredsym(sym).symoptions) then + exit; + if hsym.typ=symconst.namespacesym then + begin + case sym.typ of + symconst.namespacesym:; + symconst.unitsym: + begin + HideSym(sym); { if we add a unit and there is a namespace with the same name then hide the unit name and not the namespace } + tnamespacesym(hsym).unitsym:=tsym(sym); + end + else + HideSym(hsym); + end; + end + else + { In delphi (contrary to TP) you can have a symbol with the same name as the + unit, the unit can then not be accessed anymore using + <unit>.<id>, so we can hide the symbol. + Do the same if we add a namespace and there is a unit with the same name } + if (hsym.typ=symconst.unitsym) and + ((m_delphi in current_settings.modeswitches) or (sym.typ=symconst.namespacesym)) then + begin + HideSym(hsym); + if sym.typ=symconst.namespacesym then + tnamespacesym(sym).unitsym:=tsym(hsym); + end + else + DuplicateSym(hashedid,sym,hsym); + result:=true; + exit; + end; + end; + + function tabstractuniTSymtable.iscurrentunit:boolean; + begin + result:=assigned(current_module) and + ( + (current_module.globalsymtable=self) or + (current_module.localsymtable=self) + ); + end; + + procedure tabstractuniTSymtable.insertunit(sym:TSymEntry); + var + p:integer; + n,ns:string; + oldsym:TSymEntry; + begin + insert(sym); + n:=sym.realname; + p:=pos('.',n); + ns:=''; + while p>0 do + begin + if ns='' then + ns:=copy(n,1,p-1) + else + ns:=ns+'.'+copy(n,1,p-1); + system.delete(n,1,p); + oldsym:=Find(upper(ns)); + if not Assigned(oldsym) or (oldsym.typ<>namespacesym) then + insert(tnamespacesym.create(ns)); + p:=pos('.',n); + end; + end; + +{**************************************************************************** + TStaticSymtable +****************************************************************************} + + constructor tstaticsymtable.create(const n : string;id:word); + begin + inherited create(n,id); + symtabletype:=staticsymtable; + symtablelevel:=main_program_level; + end; + + + procedure tstaticsymtable.ppuload(ppufile:tcompilerppufile); + begin + inherited ppuload(ppufile); + + { now we can deref the syms and defs } + deref; + end; + + + procedure tstaticsymtable.ppuwrite(ppufile:tcompilerppufile); + begin + inherited ppuwrite(ppufile); + end; + + + function tstaticsymtable.checkduplicate(var hashedid:THashedIDString;sym:TSymEntry):boolean; + begin + result:=inherited checkduplicate(hashedid,sym); + + if not result and + (current_module.localsymtable=self) and + assigned(current_module.globalsymtable) then + result:=tglobalsymtable(current_module.globalsymtable).checkduplicate(hashedid,sym); + end; + + +{**************************************************************************** + TGlobalSymtable +****************************************************************************} + + constructor tglobalsymtable.create(const n : string;id:word); + begin + inherited create(n,id); + symtabletype:=globalsymtable; + symtablelevel:=main_program_level; + end; + + + procedure tglobalsymtable.ppuload(ppufile:tcompilerppufile); + begin + inherited ppuload(ppufile); + + { now we can deref the syms and defs } + deref; + end; + + + procedure tglobalsymtable.ppuwrite(ppufile:tcompilerppufile); + begin + { write the symtable entries } + inherited ppuwrite(ppufile); + end; + + +{***************************************************************************** + tspecializesymtable +*****************************************************************************} + + function tspecializesymtable.iscurrentunit: boolean; + begin + Result := true; + end; + + +{**************************************************************************** + TWITHSYMTABLE +****************************************************************************} + + constructor twithsymtable.create(aowner:tdef;ASymList:TFPHashObjectList;refnode:tobject{tnode}); + begin + inherited create(''); + symtabletype:=withsymtable; + withrefnode:=refnode; + { Replace SymList with the passed symlist } + SymList.free; + SymList:=ASymList; + defowner:=aowner; + end; + + + destructor twithsymtable.destroy; + begin + withrefnode.free; + { Disable SymList because we don't Own it } + SymList:=nil; + inherited destroy; + end; + + + procedure twithsymtable.clear; + begin + { remove no entry from a withsymtable as it is only a pointer to the + recorddef or objectdef symtable } + end; + + + procedure twithsymtable.insertdef(def:TDefEntry); + begin + { Definitions can't be registered in the withsymtable + because the withsymtable is removed after the with block. + We can't easily solve it here because the next symtable in the + stack is not known. } + internalerror(200602046); + end; + +{**************************************************************************** + TSTT_ExceptionSymtable +****************************************************************************} + + constructor tstt_excepTSymtable.create; + begin + inherited create(''); + symtabletype:=stt_excepTSymtable; + end; + + +{**************************************************************************** + TMacroSymtable +****************************************************************************} + + constructor tmacrosymtable.create(exported: boolean); + begin + inherited create(''); + if exported then + symtabletype:=exportedmacrosymtable + else + symtabletype:=localmacrosymtable; + symtablelevel:=main_program_level; + end; + +{**************************************************************************** + TEnumSymtable +****************************************************************************} + + procedure tenumsymtable.insert(sym: TSymEntry; checkdup: boolean); + var + value: longint; + def: tenumdef; + begin + // defowner = nil only when we are loading from ppu + if defowner<>nil then + begin + { First entry? Then we need to set the minval } + value:=tenumsym(sym).value; + def:=tenumdef(defowner); + if SymList.count=0 then + begin + if value>0 then + def.has_jumps:=true; + def.setmin(value); + def.setmax(value); + end + else + begin + { check for jumps } + if value>def.max+1 then + def.has_jumps:=true; + { update low and high } + if def.min>value then + def.setmin(value); + if def.max<value then + def.setmax(value); + end; + end; + inherited insert(sym, checkdup); + end; + + constructor tenumsymtable.create(adefowner: tdef); + begin + inherited Create(''); + symtabletype:=enumsymtable; + defowner:=adefowner; + end; + +{**************************************************************************** + TArraySymtable +****************************************************************************} + + procedure tarraysymtable.insertdef(def: TDefEntry); + begin + { Enums must also be available outside the record scope, + insert in the owner of this symtable } + if def.typ=enumdef then + defowner.owner.insertdef(def) + else + inherited insertdef(def); + end; + + constructor tarraysymtable.create(adefowner: tdef); + begin + inherited Create(''); + symtabletype:=arraysymtable; + defowner:=adefowner; + end; + +{***************************************************************************** + Helper Routines +*****************************************************************************} + + function FullTypeName(def,otherdef:tdef):string; + var + s1,s2 : string; + begin + if def.typ in [objectdef,recorddef] then + s1:=tabstractrecorddef(def).RttiName + else + s1:=def.typename; + { When the names are the same try to include the unit name } + if assigned(otherdef) and + (def.owner.symtabletype in [globalsymtable,staticsymtable]) then + begin + s2:=otherdef.typename; + if upper(s1)=upper(s2) then + s1:=def.owner.realname^+'.'+s1; + end; + FullTypeName:=s1; + end; + + function generate_nested_name(symtable:tsymtable;delimiter:string):string; + begin + result:=''; + while assigned(symtable) and (symtable.symtabletype in [ObjectSymtable,recordsymtable]) do + begin + if (result='') then + result:=symtable.name^ + else + result:=symtable.name^+delimiter+result; + symtable:=symtable.defowner.owner; + end; + end; + + procedure incompatibletypes(def1,def2:tdef); + begin + { When there is an errordef there is already an error message show } + if (def2.typ=errordef) or + (def1.typ=errordef) then + exit; + CGMessage2(type_e_incompatible_types,FullTypeName(def1,def2),FullTypeName(def2,def1)); + end; + + + procedure hidesym(sym:TSymEntry); + begin + sym.realname:='$hidden'+sym.realname; + tsym(sym).visibility:=vis_hidden; + end; + + + procedure duplicatesym(var hashedid:THashedIDString;dupsym,origsym:TSymEntry); + var + st : TSymtable; + begin + Message1(sym_e_duplicate_id,tsym(origsym).realname); + { Write hint where the original symbol was found } + st:=finduniTSymtable(origsym.owner); + with tsym(origsym).fileinfo do + begin + if assigned(st) and + (st.symtabletype=globalsymtable) and + st.iscurrentunit then + Message2(sym_h_duplicate_id_where,current_module.sourcefiles.get_file_name(fileindex),tostr(line)) + else if assigned(st.name) then + Message2(sym_h_duplicate_id_where,'unit '+st.name^,tostr(line)); + end; + { Rename duplicate sym to an unreachable name, but it can be + inserted in the symtable without errors } + inc(dupnr); + hashedid.id:='dup'+tostr(dupnr)+hashedid.id; + if assigned(dupsym) then + include(tsym(dupsym).symoptions,sp_implicitrename); + end; + + function handle_generic_dummysym(sym:TSymEntry;var symoptions:tsymoptions):boolean; + begin + result:=false; + if not assigned(sym) or not (sym is tstoredsym) then + Internalerror(2011081101); + { For generics a dummy symbol without the parameter count is created + if such a symbol not yet exists so that different parts of the + parser can find that symbol. If that symbol is still a + undefineddef we replace the generic dummy symbol's + name with a "dup" name and use the new symbol as the generic dummy + symbol } + if (sp_generic_dummy in tstoredsym(sym).symoptions) and + (sym.typ=typesym) and (ttypesym(sym).typedef.typ=undefineddef) and + (m_delphi in current_settings.modeswitches) then + begin + inc(dupnr); + sym.Owner.SymList.Rename(upper(sym.realname),'dup_'+tostr(dupnr)+sym.realname); + include(tsym(sym).symoptions,sp_implicitrename); + { we need to find the new symbol now if checking for a dummy } + include(symoptions,sp_generic_dummy); + result:=true; + end; + end; + +{***************************************************************************** + Search +*****************************************************************************} + + procedure addsymref(sym:tsym); + begin + { symbol uses count } + sym.IncRefCount; + { unit uses count } + if assigned(current_module) and + (sym.owner.symtabletype=globalsymtable) then + begin + if tglobalsymtable(sym.owner).moduleid>=current_module.unitmapsize then + internalerror(200501152); + inc(current_module.unitmap[tglobalsymtable(sym.owner).moduleid].refs); + end; + end; + + + function is_owned_by(childdef,ownerdef:tdef):boolean; + begin + result:=childdef=ownerdef; + if not result and assigned(childdef.owner.defowner) then + result:=is_owned_by(tdef(childdef.owner.defowner),ownerdef); + end; + + function sym_is_owned_by(childsym:tsym;symtable:tsymtable):boolean; + begin + result:=childsym.owner=symtable; + if not result and (childsym.owner.symtabletype in [objectsymtable,recordsymtable]) then + result:=sym_is_owned_by(tabstractrecorddef(childsym.owner.defowner).typesym,symtable); + end; + + function is_visible_for_object(symst:tsymtable;symvisibility:tvisibility;contextobjdef:tabstractrecorddef):boolean; + var + symownerdef : tabstractrecorddef; + begin + result:=false; + + { Get objdectdef owner of the symtable for the is_related checks } + if not assigned(symst) or + not (symst.symtabletype in [objectsymtable,recordsymtable]) then + internalerror(200810285); + symownerdef:=tabstractrecorddef(symst.defowner); + case symvisibility of + vis_private : + begin + { private symbols are allowed when we are in the same + module as they are defined } + result:=( + (symownerdef.owner.symtabletype in [globalsymtable,staticsymtable]) and + (symownerdef.owner.iscurrentunit) + ) or + ( // the case of specialize inside the generic declaration and nested types + (symownerdef.owner.symtabletype in [objectsymtable,recordsymtable]) and + ( + assigned(current_structdef) and + ( + (current_structdef=symownerdef) or + (current_structdef.owner.iscurrentunit) + ) + ) or + ( + not assigned(current_structdef) and + (symownerdef.owner.iscurrentunit) + ) + ); + end; + vis_strictprivate : + begin + result:=assigned(current_structdef) and + is_owned_by(current_structdef,symownerdef); + end; + vis_strictprotected : + begin + result:=( + assigned(current_structdef) and + (current_structdef.is_related(symownerdef) or + is_owned_by(current_structdef,symownerdef)) + ) or + ( + { helpers can access strict protected symbols } + is_objectpascal_helper(contextobjdef) and + tobjectdef(contextobjdef).extendeddef.is_related(symownerdef) + ); + end; + vis_protected : + begin + { protected symbols are visible in the module that defines them and + also visible to related objects. The related object must be defined + in the current module } + result:=( + ( + (symownerdef.owner.symtabletype in [globalsymtable,staticsymtable]) and + (symownerdef.owner.iscurrentunit) + ) or + ( + assigned(contextobjdef) and + (contextobjdef.owner.symtabletype in [globalsymtable,staticsymtable,ObjectSymtable]) and + (contextobjdef.owner.iscurrentunit) and + contextobjdef.is_related(symownerdef) + ) or + ( // the case of specialize inside the generic declaration and nested types + (symownerdef.owner.symtabletype in [objectsymtable,recordsymtable]) and + ( + assigned(current_structdef) and + ( + (current_structdef=symownerdef) or + (current_structdef.owner.iscurrentunit) + ) + ) or + ( + not assigned(current_structdef) and + (symownerdef.owner.iscurrentunit) + ) or + ( + { helpers can access protected symbols } + is_objectpascal_helper(contextobjdef) and + tobjectdef(contextobjdef).extendeddef.is_related(symownerdef) + ) + ) + ); + end; + vis_public, + vis_published : + result:=true; + end; + end; + + + function is_visible_for_object(pd:tprocdef;contextobjdef:tabstractrecorddef):boolean; + begin + result:=is_visible_for_object(pd.owner,pd.visibility,contextobjdef); + end; + + + function is_visible_for_object(sym:tsym;contextobjdef:tabstractrecorddef):boolean; + var + i : longint; + pd : tprocdef; + begin + if sym.typ=procsym then + begin + { A procsym is visible, when there is at least one of the procdefs visible } + result:=false; + for i:=0 to tprocsym(sym).ProcdefList.Count-1 do + begin + pd:=tprocdef(tprocsym(sym).ProcdefList[i]); + if (pd.owner=sym.owner) and + is_visible_for_object(pd,contextobjdef) then + begin + result:=true; + exit; + end; + end; + end + else + result:=is_visible_for_object(sym.owner,sym.visibility,contextobjdef); + end; + + + function searchsym(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + begin + result:=searchsym_maybe_with_symoption(s,srsym,srsymtable,false,sp_none); + end; + + function searchsym_maybe_with_symoption(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;searchoption:boolean;option:tsymoption):boolean; + var + hashedid : THashedIDString; + contextstructdef : tabstractrecorddef; + stackitem : psymtablestackitem; + begin + result:=false; + hashedid.id:=s; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + srsymtable:=stackitem^.symtable; + if (srsymtable.symtabletype=objectsymtable) then + begin + { TODO : implement the search for an option in classes as well } + if searchoption then + begin + result:=false; + exit; + end; + if searchsym_in_class(tobjectdef(srsymtable.defowner),tobjectdef(srsymtable.defowner),s,srsym,srsymtable,true) then + begin + result:=true; + exit; + end; + end + else + begin + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) then + begin + { use the class from withsymtable only when it is + defined in this unit } + if (srsymtable.symtabletype=withsymtable) and + assigned(srsymtable.defowner) and + (srsymtable.defowner.typ in [recorddef,objectdef]) and + (srsymtable.defowner.owner.symtabletype in [globalsymtable,staticsymtable,objectsymtable,recordsymtable]) and + (srsymtable.defowner.owner.iscurrentunit) then + contextstructdef:=tabstractrecorddef(srsymtable.defowner) + else + contextstructdef:=current_structdef; + if not (srsym.owner.symtabletype in [objectsymtable,recordsymtable]) or + is_visible_for_object(srsym,contextstructdef) and + (not searchoption or (option in srsym.symoptions)) then + begin + { we need to know if a procedure references symbols + in the static symtable, because then it can't be + inlined from outside this unit } + if assigned(current_procinfo) and + (srsym.owner.symtabletype=staticsymtable) then + include(current_procinfo.flags,pi_uses_static_symtable); + addsymref(srsym); + result:=true; + exit; + end; + end; + end; + stackitem:=stackitem^.next; + end; + srsym:=nil; + srsymtable:=nil; + end; + + function searchsym_with_symoption(const s: TIDString;out srsym:tsym;out + srsymtable:TSymtable;option:tsymoption):boolean; + begin + result:=searchsym_maybe_with_symoption(s,srsym,srsymtable,true,option); + end; + + function searchsym_type(const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + var + hashedid : THashedIDString; + stackitem : psymtablestackitem; + classh : tobjectdef; + begin + result:=false; + hashedid.id:=s; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + { + It is not possible to have type symbols in: + parameters + Exception are classes, objects, records, generic definitions and specializations + that have the parameterized types inserted in the symtable. + } + srsymtable:=stackitem^.symtable; + if (srsymtable.symtabletype=ObjectSymtable) then + begin + classh:=tobjectdef(srsymtable.defowner); + while assigned(classh) do + begin + srsymtable:=classh.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + not(srsym.typ in [fieldvarsym,paravarsym,propertysym,procsym,labelsym]) and + is_visible_for_object(srsym,current_structdef) then + begin + addsymref(srsym); + result:=true; + exit; + end; + classh:=classh.childof; + end; + end + else + begin + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + not(srsym.typ in [fieldvarsym,paravarsym,propertysym,procsym,labelsym]) and + (not (srsym.owner.symtabletype in [objectsymtable,recordsymtable]) or is_visible_for_object(srsym,current_structdef)) then + begin + { we need to know if a procedure references symbols + in the static symtable, because then it can't be + inlined from outside this unit } + if assigned(current_procinfo) and + (srsym.owner.symtabletype=staticsymtable) then + include(current_procinfo.flags,pi_uses_static_symtable); + addsymref(srsym); + result:=true; + exit; + end; + end; + stackitem:=stackitem^.next; + end; + result:=false; + srsym:=nil; + srsymtable:=nil; + end; + + + function searchsym_in_module(pm:pointer;const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + var + pmod : tmodule; + begin + pmod:=tmodule(pm); + result:=false; + if assigned(pmod.globalsymtable) then + begin + srsym:=tsym(pmod.globalsymtable.Find(s)); + if assigned(srsym) then + begin + srsymtable:=pmod.globalsymtable; + addsymref(srsym); + result:=true; + exit; + end; + end; + { If the module is the current unit we also need + to search the local symtable } + if (pmod=current_module) and + assigned(pmod.localsymtable) then + begin + srsym:=tsym(pmod.localsymtable.Find(s)); + if assigned(srsym) then + begin + srsymtable:=pmod.localsymtable; + addsymref(srsym); + result:=true; + exit; + end; + end; + srsym:=nil; + srsymtable:=nil; + end; + + + function searchsym_in_named_module(const unitname, symname: TIDString; out srsym: tsym; out srsymtable: tsymtable): boolean; + var + stackitem : psymtablestackitem; + begin + result:=false; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + srsymtable:=stackitem^.symtable; + if (srsymtable.symtabletype=globalsymtable) and + (srsymtable.name^=unitname) then + begin + srsym:=tsym(srsymtable.find(symname)); + if not assigned(srsym) then + break; + result:=true; + exit; + end; + stackitem:=stackitem^.next; + end; + + { If the module is the current unit we also need + to search the local symtable } + if (current_module.globalsymtable=srsymtable) and + assigned(current_module.localsymtable) then + begin + srsymtable:=current_module.localsymtable; + srsym:=tsym(srsymtable.find(symname)); + if assigned(srsym) then + begin + result:=true; + exit; + end; + end; + end; + + + function find_real_objcclass_definition(pd: tobjectdef; erroronfailure: boolean): tobjectdef; + var + hashedid : THashedIDString; + stackitem : psymtablestackitem; + srsymtable : tsymtable; + srsym : tsym; + begin + { not a formal definition -> return it } + if not(oo_is_formal in pd.objectoptions) then + begin + result:=pd; + exit; + end; + hashedid.id:=pd.typesym.name; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + srsymtable:=stackitem^.symtable; + { ObjC classes can't appear in generics or as nested class + definitions } + if not(srsymtable.symtabletype in [recordsymtable,ObjectSymtable,parasymtable]) then + begin + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + (srsym.typ=typesym) and + is_objcclass(ttypesym(srsym).typedef) and + not(oo_is_formal in tobjectdef(ttypesym(srsym).typedef).objectoptions) then + begin + { the external name for the formal and the real definition must match } + if tobjectdef(ttypesym(srsym).typedef).objextname^<>pd.objextname^ then + begin + Message2(sym_e_external_class_name_mismatch1,pd.objextname^,pd.typename); + MessagePos1(srsym.fileinfo,sym_e_external_class_name_mismatch2,tobjectdef(ttypesym(srsym).typedef).objextname^); + end; + result:=tobjectdef(ttypesym(srsym).typedef); + if assigned(current_procinfo) and + (srsym.owner.symtabletype=staticsymtable) then + include(current_procinfo.flags,pi_uses_static_symtable); + addsymref(srsym); + exit; + end; + end; + stackitem:=stackitem^.next; + end; + { nothing found: optionally give an error and return the original + (empty) one } + if erroronfailure then + Message1(sym_e_objc_formal_class_not_resolved,pd.objrealname^); + result:=pd; + end; + + + function searchsym_in_class(classh,contextclassh:tobjectdef;const s : TIDString;out srsym:tsym;out srsymtable:TSymtable;searchhelper:boolean):boolean; + var + hashedid : THashedIDString; + orgclass : tobjectdef; + i : longint; + begin + orgclass:=classh; + { in case this is a formal objcclass, first find the real definition } + if assigned(classh) then + begin + if (oo_is_formal in classh.objectoptions) then + classh:=find_real_objcclass_definition(classh,true); + { The contextclassh is used for visibility. The classh must be equal to + or be a parent of contextclassh. E.g. for inherited searches the classh is the + parent or a class helper. } + if not (contextclassh.is_related(classh) or + (assigned(contextclassh.extendeddef) and + (contextclassh.extendeddef.typ=objectdef) and + contextclassh.extendeddef.is_related(classh))) then + internalerror(200811161); + end; + result:=false; + hashedid.id:=s; + { an Objective-C protocol can inherit from multiple other protocols + -> uses ImplementedInterfaces instead } + if is_objcprotocol(classh) then + begin + srsymtable:=classh.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + is_visible_for_object(srsym,contextclassh) then + begin + addsymref(srsym); + result:=true; + exit; + end; + for i:=0 to classh.ImplementedInterfaces.count-1 do + begin + if searchsym_in_class(TImplementedInterface(classh.ImplementedInterfaces[i]).intfdef,contextclassh,s,srsym,srsymtable,false) then + begin + result:=true; + exit; + end; + end; + end + else + if is_objectpascal_helper(classh) then + begin + { helpers have their own obscure search logic... } + result:=searchsym_in_helper(classh,contextclassh,s,srsym,srsymtable,false); + if result then + exit; + end + else + begin + while assigned(classh) do + begin + { search for a class helper method first if this is an Object + Pascal class } + if is_class(classh) and searchhelper then + begin + result:=search_objectpascal_helper(classh,contextclassh,s,srsym,srsymtable); + if result then + { if the procsym is overloaded we need to use the + "original" symbol; the helper symbol will be found when + searching for overloads } + if (srsym.typ<>procsym) or + not (sp_has_overloaded in tprocsym(srsym).symoptions) then + exit; + end; + srsymtable:=classh.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + is_visible_for_object(srsym,contextclassh) then + begin + addsymref(srsym); + result:=true; + exit; + end; + classh:=classh.childof; + end; + end; + if is_objcclass(orgclass) then + result:=search_objc_helper(orgclass,s,srsym,srsymtable) + else + begin + srsym:=nil; + srsymtable:=nil; + end; + end; + + function searchsym_in_record(recordh:tabstractrecorddef;const s : TIDString;out srsym:tsym;out srsymtable:TSymtable):boolean; + var + hashedid : THashedIDString; + begin + result:=false; + hashedid.id:=s; + { search for a record helper method first } + result:=search_objectpascal_helper(recordh,recordh,s,srsym,srsymtable); + if result then + { if the procsym is overloaded we need to use the + "original" symbol; the helper symbol will be found when + searching for overloads } + if (srsym.typ<>procsym) or + not (sp_has_overloaded in tprocsym(srsym).symoptions) then + exit; + srsymtable:=recordh.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and is_visible_for_object(srsym,recordh) then + begin + addsymref(srsym); + result:=true; + exit; + end; + srsym:=nil; + srsymtable:=nil; + end; + + function searchsym_in_class_by_msgint(classh:tobjectdef;msgid:longint;out srdef : tdef;out srsym:tsym;out srsymtable:TSymtable):boolean; + var + def : tdef; + i : longint; + begin + { in case this is a formal objcclass, first find the real definition } + if assigned(classh) and + (oo_is_formal in classh.objectoptions) then + classh:=find_real_objcclass_definition(classh,true); + result:=false; + def:=nil; + while assigned(classh) do + begin + for i:=0 to classh.symtable.DefList.Count-1 do + begin + def:=tstoreddef(classh.symtable.DefList[i]); + { Find also all hidden private methods to + be compatible with delphi, see tw6203 (PFV) } + if (def.typ=procdef) and + (po_msgint in tprocdef(def).procoptions) and + (tprocdef(def).messageinf.i=msgid) then + begin + srdef:=def; + srsym:=tprocdef(def).procsym; + srsymtable:=classh.symtable; + addsymref(srsym); + result:=true; + exit; + end; + end; + classh:=classh.childof; + end; + srdef:=nil; + srsym:=nil; + srsymtable:=nil; + end; + + + function searchsym_in_class_by_msgstr(classh:tobjectdef;const s:string;out srsym:tsym;out srsymtable:TSymtable):boolean; + var + def : tdef; + i : longint; + begin + { in case this is a formal objcclass, first find the real definition } + if assigned(classh) and + (oo_is_formal in classh.objectoptions) then + classh:=find_real_objcclass_definition(classh,true); + result:=false; + def:=nil; + while assigned(classh) do + begin + for i:=0 to classh.symtable.DefList.Count-1 do + begin + def:=tstoreddef(classh.symtable.DefList[i]); + { Find also all hidden private methods to + be compatible with delphi, see tw6203 (PFV) } + if (def.typ=procdef) and + (po_msgstr in tprocdef(def).procoptions) and + (tprocdef(def).messageinf.str^=s) then + begin + srsym:=tprocdef(def).procsym; + srsymtable:=classh.symtable; + addsymref(srsym); + result:=true; + exit; + end; + end; + classh:=classh.childof; + end; + srsym:=nil; + srsymtable:=nil; + end; + + function searchsym_in_helper(classh,contextclassh:tobjectdef;const s: TIDString;out srsym:tsym;out srsymtable:TSymtable;aHasInherited:boolean):boolean; + var + hashedid : THashedIDString; + parentclassh : tobjectdef; + begin + result:=false; + if not is_objectpascal_helper(classh) then + Internalerror(2011030101); + hashedid.id:=s; + { in a helper things are a bit more complex: + 1. search the symbol in the helper (if not "inherited") + 2. search the symbol in the extended type + 3. search the symbol in the parent helpers + 4. only classes: search the symbol in the parents of the extended type + } + if not aHasInherited then + begin + { search in the helper itself } + srsymtable:=classh.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + is_visible_for_object(srsym,contextclassh) then + begin + addsymref(srsym); + result:=true; + exit; + end; + end; + { now search in the extended type itself } + if classh.extendeddef.typ in [recorddef,objectdef] then + begin + srsymtable:=tabstractrecorddef(classh.extendeddef).symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + is_visible_for_object(srsym,contextclassh) then + begin + addsymref(srsym); + result:=true; + exit; + end; + end; + { now search in the parent helpers } + parentclassh:=classh.childof; + while assigned(parentclassh) do + begin + srsymtable:=parentclassh.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) and + is_visible_for_object(srsym,contextclassh) then + begin + addsymref(srsym); + result:=true; + exit; + end; + parentclassh:=parentclassh.childof; + end; + if is_class(classh.extendeddef) then + { now search in the parents of the extended class (with helpers!) } + result:=searchsym_in_class(tobjectdef(classh.extendeddef).childof,contextclassh,s,srsym,srsymtable,true); + { addsymref is already called by searchsym_in_class } + end; + + function search_specific_assignment_operator(assignment_type:ttoken;from_def,to_def:Tdef):Tprocdef; + var + sym : Tprocsym; + hashedid : THashedIDString; + curreq, + besteq : tequaltype; + currpd, + bestpd : tprocdef; + stackitem : psymtablestackitem; + begin + hashedid.id:=overloaded_names[assignment_type]; + besteq:=te_incompatible; + bestpd:=nil; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + sym:=Tprocsym(stackitem^.symtable.FindWithHash(hashedid)); + if sym<>nil then + begin + if sym.typ<>procsym then + internalerror(200402031); + { if the source type is an alias then this is only the second choice, + if you mess with this code, check tw4093 } + currpd:=sym.find_procdef_assignment_operator(from_def,to_def,curreq); + if curreq>besteq then + begin + besteq:=curreq; + bestpd:=currpd; + if (besteq=te_exact) then + break; + end; + end; + stackitem:=stackitem^.next; + end; + result:=bestpd; + end; + + + function search_assignment_operator(from_def,to_def:Tdef;explicit:boolean):Tprocdef; + begin + { search record/object symtable first for a suitable operator } + if from_def.typ in [recorddef,objectdef] then + symtablestack.push(tabstractrecorddef(from_def).symtable); + if to_def.typ in [recorddef,objectdef] then + symtablestack.push(tabstractrecorddef(to_def).symtable); + + { if type conversion is explicit then search first for explicit + operator overload and if not found then use implicit operator } + if explicit then + result:=search_specific_assignment_operator(_OP_EXPLICIT,from_def,to_def) + else + result:=nil; + if result=nil then + result:=search_specific_assignment_operator(_ASSIGNMENT,from_def,to_def); + + { restore symtable stack } + if to_def.typ in [recorddef,objectdef] then + symtablestack.pop(tabstractrecorddef(to_def).symtable); + if from_def.typ in [recorddef,objectdef] then + symtablestack.pop(tabstractrecorddef(from_def).symtable); + end; + + + function search_enumerator_operator(from_def,to_def:Tdef): Tprocdef; + var + sym : Tprocsym; + hashedid : THashedIDString; + curreq, + besteq : tequaltype; + currpd, + bestpd : tprocdef; + stackitem : psymtablestackitem; + begin + hashedid.id:='enumerator'; + besteq:=te_incompatible; + bestpd:=nil; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + sym:=Tprocsym(stackitem^.symtable.FindWithHash(hashedid)); + if sym<>nil then + begin + if sym.typ<>procsym then + internalerror(200910241); + { if the source type is an alias then this is only the second choice, + if you mess with this code, check tw4093 } + currpd:=sym.find_procdef_enumerator_operator(from_def,to_def,curreq); + if curreq>besteq then + begin + besteq:=curreq; + bestpd:=currpd; + if (besteq=te_exact) then + break; + end; + end; + stackitem:=stackitem^.next; + end; + result:=bestpd; + end; + + + function search_system_type(const s: TIDString): ttypesym; + var + sym : tsym; + begin + sym:=tsym(systemunit.Find(s)); + if not assigned(sym) or + (sym.typ<>typesym) then + cgmessage1(cg_f_unknown_system_type,s); + result:=ttypesym(sym); + end; + + + function try_search_system_type(const s: TIDString): ttypesym; + var + sym : tsym; + begin + sym:=tsym(systemunit.Find(s)); + if not assigned(sym) then + result:=nil + else + begin + if sym.typ<>typesym then + cgmessage1(cg_f_unknown_system_type,s); + result:=ttypesym(sym); + end; + end; + + + function search_named_unit_globaltype(const unitname, typename: TIDString; throwerror: boolean): ttypesym; + var + srsymtable: tsymtable; + sym: tsym; + begin + if searchsym_in_named_module(unitname,typename,sym,srsymtable) and + (sym.typ=typesym) then + begin + result:=ttypesym(sym); + exit; + end + else + begin + if throwerror then + cgmessage2(cg_f_unknown_type_in_unit,typename,unitname); + result:=nil; + end; + end; + + function search_last_objectpascal_helper(pd,contextclassh : tabstractrecorddef;out odef : tobjectdef):boolean; + var + s: string; + list: TFPObjectList; + i: integer; + st: tsymtable; + begin + result:=false; + odef:=nil; + { when there are no helpers active currently then we don't need to do + anything } + if current_module.extendeddefs.count=0 then + exit; + { no helpers for anonymous types } + if not assigned(pd.objrealname) or (pd.objrealname^='') then + exit; + { if pd is defined inside a procedure we must not use make_mangledname + (as a helper may not be defined in a procedure this is no problem...)} + st:=pd.owner; + while st.symtabletype in [objectsymtable,recordsymtable] do + st:=st.defowner.owner; + if st.symtabletype=localsymtable then + exit; + { the mangled name is used as the key for tmodule.extendeddefs } + s:=make_mangledname('',pd.symtable,''); + list:=TFPObjectList(current_module.extendeddefs.Find(s)); + if assigned(list) and (list.count>0) then + begin + i:=list.count-1; + repeat + odef:=tobjectdef(list[list.count-1]); + result:=(odef.owner.symtabletype in [staticsymtable,globalsymtable]) or + is_visible_for_object(tobjectdef(list[i]).typesym,contextclassh); + dec(i); + until result or (i<0); + if not result then + { just to be sure that noone uses odef } + odef:=nil; + end; + end; + + function search_objectpascal_helper(pd,contextclassh : tabstractrecorddef;const s: string; out srsym: tsym; out srsymtable: tsymtable):boolean; + + var + hashedid : THashedIDString; + classh : tobjectdef; + i : integer; + pdef : tprocdef; + begin + result:=false; + + { if there is no class helper for the class then there is no need to + search further } + if not search_last_objectpascal_helper(pd,contextclassh,classh) then + exit; + + hashedid.id:=s; + + repeat + srsymtable:=classh.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + + if srsym<>nil then + begin + if srsym.typ=propertysym then + begin + result:=true; + exit; + end; + for i:=0 to tprocsym(srsym).procdeflist.count-1 do + begin + pdef:=tprocdef(tprocsym(srsym).procdeflist[i]); + if not is_visible_for_object(pdef.owner,pdef.visibility,contextclassh) then + continue; + { we need to know if a procedure references symbols + in the static symtable, because then it can't be + inlined from outside this unit } + if assigned(current_procinfo) and + (srsym.owner.symtabletype=staticsymtable) then + include(current_procinfo.flags,pi_uses_static_symtable); + { the first found method wins } + srsym:=tprocdef(tprocsym(srsym).procdeflist[i]).procsym; + srsymtable:=srsym.owner; + addsymref(srsym); + result:=true; + exit; + end; + end; + + { try the helper parent if available } + classh:=classh.childof; + until classh=nil; + + srsym:=nil; + srsymtable:=nil; + end; + + function search_objc_helper(pd : tobjectdef;const s : string; out srsym: tsym; out srsymtable: tsymtable):boolean; + var + hashedid : THashedIDString; + stackitem : psymtablestackitem; + i : longint; + defowner : tobjectdef; + begin + hashedid.id:=class_helper_prefix+s; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + srsymtable:=stackitem^.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) then + begin + if not(srsymtable.symtabletype in [globalsymtable,staticsymtable]) or + not(srsym.owner.symtabletype in [globalsymtable,staticsymtable]) or + (srsym.typ<>procsym) then + internalerror(2009111505); + { check whether this procsym includes a helper for this particular class } + for i:=0 to tprocsym(srsym).procdeflist.count-1 do + begin + { does pd inherit from (or is the same as) the class + that this method's category extended? + + Warning: this list contains both category and objcclass methods + (for id.randommethod), so only check category methods here + } + defowner:=tobjectdef(tprocdef(tprocsym(srsym).procdeflist[i]).owner.defowner); + if (oo_is_classhelper in defowner.objectoptions) and + pd.is_related(defowner.childof) then + begin + { we need to know if a procedure references symbols + in the static symtable, because then it can't be + inlined from outside this unit } + if assigned(current_procinfo) and + (srsym.owner.symtabletype=staticsymtable) then + include(current_procinfo.flags,pi_uses_static_symtable); + { no need to keep looking. There might be other + categories that extend this, a parent or child + class with a method with the same name (either + overriding this one, or overridden by this one), + but that doesn't matter as far as the basic + procsym is concerned. + } + srsym:=tprocdef(tprocsym(srsym).procdeflist[i]).procsym; + srsymtable:=srsym.owner; + addsymref(srsym); + result:=true; + exit; + end; + end; + end; + stackitem:=stackitem^.next; + end; + srsym:=nil; + srsymtable:=nil; + result:=false; + end; + + + function search_objc_method(const s : string; out srsym: tsym; out srsymtable: tsymtable):boolean; + var + hashedid : THashedIDString; + stackitem : psymtablestackitem; + i : longint; + begin + hashedid.id:=class_helper_prefix+s; + stackitem:=symtablestack.stack; + while assigned(stackitem) do + begin + srsymtable:=stackitem^.symtable; + srsym:=tsym(srsymtable.FindWithHash(hashedid)); + if assigned(srsym) then + begin + if not(srsymtable.symtabletype in [globalsymtable,staticsymtable]) or + not(srsym.owner.symtabletype in [globalsymtable,staticsymtable]) or + (srsym.typ<>procsym) then + internalerror(2009112005); + { check whether this procsym includes a helper for this particular class } + for i:=0 to tprocsym(srsym).procdeflist.count-1 do + begin + { we need to know if a procedure references symbols + in the static symtable, because then it can't be + inlined from outside this unit } + if assigned(current_procinfo) and + (srsym.owner.symtabletype=staticsymtable) then + include(current_procinfo.flags,pi_uses_static_symtable); + { no need to keep looking. There might be other + methods with the same name, but that doesn't matter + as far as the basic procsym is concerned. + } + srsym:=tprocdef(tprocsym(srsym).procdeflist[i]).procsym; + { We need the symtable in which the classhelper-like sym + is located, not the objectdef. The reason is that the + callnode will climb the symtablestack until it encounters + this symtable to start looking for overloads (and it won't + find the objectsymtable in which this method sym is + located + + srsymtable:=srsym.owner; + } + addsymref(srsym); + result:=true; + exit; + end; + end; + stackitem:=stackitem^.next; + end; + srsym:=nil; + srsymtable:=nil; + result:=false; + end; + + + function search_struct_member(pd : tabstractrecorddef;const s : string):tsym; + { searches n in symtable of pd and all anchestors } + var + hashedid : THashedIDString; + srsym : tsym; + orgpd : tabstractrecorddef; + srsymtable : tsymtable; + begin + { in case this is a formal objcclass, first find the real definition } + if (oo_is_formal in pd.objectoptions) then + pd:=find_real_objcclass_definition(tobjectdef(pd),true); + if search_objectpascal_helper(pd, pd, s, result, srsymtable) then + exit; + hashedid.id:=s; + orgpd:=pd; + while assigned(pd) do + begin + srsym:=tsym(pd.symtable.FindWithHash(hashedid)); + if assigned(srsym) then + begin + search_struct_member:=srsym; + exit; + end; + if pd.typ=objectdef then + pd:=tobjectdef(pd).childof + else + pd:=nil; + end; + + { not found, now look for class helpers } + if is_objcclass(pd) then + search_objc_helper(tobjectdef(orgpd),s,result,srsymtable) + else + result:=nil; + end; + + + function search_macro(const s : string):tsym; + var + stackitem : psymtablestackitem; + hashedid : THashedIDString; + srsym : tsym; + begin + hashedid.id:=s; + + { First search the localmacrosymtable before searching the + global macrosymtables from the units } + if assigned(current_module) then + begin + srsym:=tsym(current_module.localmacrosymtable.FindWithHash(hashedid)); + if assigned(srsym) then + begin + result:= srsym; + exit; + end; + end; + + stackitem:=macrosymtablestack.stack; + while assigned(stackitem) do + begin + srsym:=tsym(stackitem^.symtable.FindWithHash(hashedid)); + if assigned(srsym) then + begin + result:= srsym; + exit; + end; + stackitem:=stackitem^.next; + end; + result:= nil; + end; + + + function defined_macro(const s : string):boolean; + var + mac: tmacro; + begin + mac:=tmacro(search_macro(s)); + if assigned(mac) then + begin + mac.is_used:=true; + defined_macro:=mac.defined; + end + else + defined_macro:=false; + end; + + +{**************************************************************************** + Object Helpers +****************************************************************************} + + function search_default_property(pd : tabstractrecorddef) : tpropertysym; + { returns the default property of a class, searches also anchestors } + var + _defaultprop : tpropertysym; + helperpd : tobjectdef; + begin + _defaultprop:=nil; + { first search in helper's hierarchy } + if search_last_objectpascal_helper(pd,nil,helperpd) then + while assigned(helperpd) do + begin + helperpd.symtable.SymList.ForEachCall(@tstoredsymtable(helperpd.symtable).testfordefaultproperty,@_defaultprop); + if assigned(_defaultprop) then + break; + helperpd:=helperpd.childof; + end; + if assigned(_defaultprop) then + begin + search_default_property:=_defaultprop; + exit; + end; + { now search in the type's hierarchy itself } + while assigned(pd) do + begin + pd.symtable.SymList.ForEachCall(@tstoredsymtable(pd.symtable).testfordefaultproperty,@_defaultprop); + if assigned(_defaultprop) then + break; + if (pd.typ=objectdef) then + pd:=tobjectdef(pd).childof + else + break; + end; + search_default_property:=_defaultprop; + end; + + +{**************************************************************************** + Macro Helpers +****************************************************************************} + + procedure def_system_macro(const name : string); + var + mac : tmacro; + s: string; + begin + if name = '' then + internalerror(2004121202); + s:= upper(name); + mac:=tmacro(search_macro(s)); + if not assigned(mac) then + begin + mac:=tmacro.create(s); + if assigned(current_module) then + current_module.localmacrosymtable.insert(mac) + else + initialmacrosymtable.insert(mac); + end; + Message1(parser_c_macro_defined,mac.name); + mac.defined:=true; + end; + + + procedure set_system_macro(const name, value : string); + var + mac : tmacro; + s: string; + begin + if name = '' then + internalerror(2004121203); + s:= upper(name); + mac:=tmacro(search_macro(s)); + if not assigned(mac) then + begin + mac:=tmacro.create(s); + if assigned(current_module) then + current_module.localmacrosymtable.insert(mac) + else + initialmacrosymtable.insert(mac); + end + else + begin + mac.is_compiler_var:=false; + if assigned(mac.buftext) then + freemem(mac.buftext,mac.buflen); + end; + Message2(parser_c_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; + end; + + + procedure set_system_compvar(const name, value : string); + var + mac : tmacro; + s: string; + begin + if name = '' then + internalerror(2004121204); + s:= upper(name); + mac:=tmacro(search_macro(s)); + if not assigned(mac) then + begin + mac:=tmacro.create(s); + mac.is_compiler_var:=true; + if assigned(current_module) then + current_module.localmacrosymtable.insert(mac) + else + initialmacrosymtable.insert(mac); + end + else + begin + mac.is_compiler_var:=true; + if assigned(mac.buftext) then + freemem(mac.buftext,mac.buflen); + end; + Message2(parser_c_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; + end; + + + procedure undef_system_macro(const name : string); + var + mac : tmacro; + s: string; + begin + if name = '' then + internalerror(2004121205); + s:= upper(name); + mac:=tmacro(search_macro(s)); + if not assigned(mac) then + {If not found, then it's already undefined.} + else + begin + Message1(parser_c_macro_undefined,mac.name); + mac.defined:=false; + mac.is_compiler_var:=false; + { delete old definition } + if assigned(mac.buftext) then + begin + freemem(mac.buftext,mac.buflen); + mac.buftext:=nil; + end; + end; + end; + + +{$ifdef UNITALIASES} +{**************************************************************************** + TUNIT_ALIAS + ****************************************************************************} + + constructor tunit_alias.create(const n:string); + var + i : longint; + begin + i:=pos('=',n); + if i=0 then + fail; + inherited createname(Copy(n,1,i-1)); + newname:=stringdup(Copy(n,i+1,255)); + end; + + + destructor tunit_alias.destroy; + begin + stringdispose(newname); + inherited destroy; + end; + + + procedure addunitalias(const n:string); + begin + unitaliases^.insert(tunit_alias,init(Upper(n)))); + end; + + + function getunitalias(const n:string):string; + var + p : punit_alias; + begin + p:=punit_alias(unitaliases^.Find(Upper(n))); + if assigned(p) then + getunitalias:=punit_alias(p).newname^ + else + getunitalias:=n; + end; +{$endif UNITALIASES} + + +{**************************************************************************** + Init/Done Symtable +****************************************************************************} + + procedure InitSymtable; + begin + { Reset symbolstack } + symtablestack:=nil; + systemunit:=nil; + { create error syms and def } + generrorsym:=terrorsym.create; + generrordef:=terrordef.create; + { macros } + initialmacrosymtable:=tmacrosymtable.create(false); + macrosymtablestack:=TSymtablestack.create; + macrosymtablestack.push(initialmacrosymtable); +{$ifdef UNITALIASES} + { unit aliases } + unitaliases:=TFPHashObjectList.create; +{$endif} + { set some global vars to nil, might be important for the ide } + class_tobject:=nil; + interface_iunknown:=nil; + interface_idispatch:=nil; + rec_tguid:=nil; + dupnr:=0; + end; + + + procedure DoneSymtable; + begin + generrorsym.owner:=nil; + generrorsym.free; + generrordef.owner:=nil; + generrordef.free; + initialmacrosymtable.free; + macrosymtablestack.free; +{$ifdef UNITALIASES} + unitaliases.free; +{$endif} + end; + +end. |