{ Copyright (c) 1998-2002 by Florian Klaempfl Parses variable declarations. Used for var statement and record definitions 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 pdecvar; {$i fpcdefs.inc} interface uses cclasses, symtable,symsym,symdef; type tvar_dec_option=(vd_record,vd_object,vd_threadvar,vd_class,vd_final,vd_canreorder); tvar_dec_options=set of tvar_dec_option; function read_property_dec(is_classproperty:boolean;astruct:tabstractrecorddef):tpropertysym; procedure read_var_decls(options:Tvar_dec_options); procedure read_record_fields(options:Tvar_dec_options; reorderlist: TFPObjectList; variantdesc: ppvariantrecdesc); procedure read_public_and_external(vs: tabstractvarsym); procedure try_consume_sectiondirective(var asection: ansistring); implementation uses SysUtils, { common } cutils, { global } globtype,globals,tokens,verbose,constexp, systems, { symtable } symconst,symbase,symtype,defutil,defcmp,symcreat, {$ifdef jvm} jvmdef, {$endif} fmodule,htypechk, { pass 1 } node,pass_1,aasmdata, ncon,nmat,nadd,ncal,nset,ncnv,ninl,nld,nflw,nmem,nutils, { codegen } ncgutil,ngenutil, { parser } scanner, pbase,pexpr,ptype,ptconst,pdecsub, {$ifdef jvm} pjvm, {$endif} { link } import ; function read_property_dec(is_classproperty:boolean;astruct:tabstractrecorddef):tpropertysym; { convert a node tree to symlist and return the last symbol } function parse_symlist(pl:tpropaccesslist;var def:tdef):boolean; var idx : longint; sym : tsym; srsymtable : TSymtable; st : TSymtable; p : tnode; begin result:=true; def:=nil; if token=_ID then begin if assigned(astruct) then sym:=search_struct_member(astruct,pattern) else searchsym(pattern,sym,srsymtable); if assigned(sym) then begin if assigned(astruct) and not is_visible_for_object(sym,astruct) then Message(parser_e_cant_access_private_member); case sym.typ of fieldvarsym : begin addsymref(sym); pl.addsym(sl_load,sym); def:=tfieldvarsym(sym).vardef; end; procsym : begin addsymref(sym); pl.addsym(sl_call,sym); end; else begin Message1(parser_e_illegal_field_or_method,orgpattern); def:=generrordef; result:=false; end; end; end else begin Message1(parser_e_illegal_field_or_method,orgpattern); def:=generrordef; result:=false; end; consume(_ID); repeat case token of _ID, _SEMICOLON : begin break; end; _POINT : begin consume(_POINT); if assigned(def) then begin st:=def.GetSymtable(gs_record); if assigned(st) then begin sym:=tsym(st.Find(pattern)); if not(assigned(sym)) and is_object(def) then sym:=search_struct_member(tobjectdef(def),pattern); if assigned(sym) then begin pl.addsym(sl_subscript,sym); case sym.typ of fieldvarsym : def:=tfieldvarsym(sym).vardef; else begin Message1(sym_e_illegal_field,orgpattern); result:=false; end; end; end else begin Message1(sym_e_illegal_field,orgpattern); result:=false; end; end else begin Message(parser_e_invalid_qualifier); result:=false; end; end else begin Message(parser_e_invalid_qualifier); result:=false; end; consume(_ID); end; _LECKKLAMMER : begin consume(_LECKKLAMMER); repeat if def.typ=arraydef then begin idx:=0; p:=comp_expr(true,false); if (not codegenerror) then begin if (p.nodetype=ordconstn) then begin { type/range checking } inserttypeconv(p,tarraydef(def).rangedef); if (Tordconstnode(p).valueint64(high(longint))) then message(parser_e_array_range_out_of_bounds) else idx:=Tordconstnode(p).value.svalue end else Message(type_e_ordinal_expr_expected) end; pl.addconst(sl_vec,idx,p.resultdef); p.free; def:=tarraydef(def).elementdef; end else begin Message(parser_e_invalid_qualifier); result:=false; end; until not try_to_consume(_COMMA); consume(_RECKKLAMMER); end; else begin Message(parser_e_ill_property_access_sym); result:=false; break; end; end; until false; end else begin Message(parser_e_ill_property_access_sym); result:=false; end; end; function allow_default_property(p : tpropertysym) : boolean; begin allow_default_property:= (is_ordinal(p.propdef) or {$ifndef cpu64bitaddr} is_64bitint(p.propdef) or {$endif cpu64bitaddr} is_class(p.propdef) or is_single(p.propdef) or (p.propdef.typ in [classrefdef,pointerdef]) or is_smallset(p.propdef) ) and not ( (p.propdef.typ=arraydef) and (ppo_indexed in p.propoptions) ) and not (ppo_hasparameters in p.propoptions); end; procedure create_accessor_procsym(p: tpropertysym; pd: tprocdef; const prefix: string; accesstype: tpropaccesslisttypes); var sym: tprocsym; begin handle_calling_convention(pd); sym:=tprocsym.create(prefix+lower(p.realname)); symtablestack.top.insert(sym); pd.procsym:=sym; include(pd.procoptions,po_dispid); include(pd.procoptions,po_global); pd.visibility:=vis_private; proc_add_definition(pd); p.propaccesslist[accesstype].addsym(sl_call,sym); p.propaccesslist[accesstype].procdef:=pd; end; procedure parse_dispinterface(p : tpropertysym; readpd,writepd: tprocdef; var paranr: word); var hasread, haswrite: boolean; pt: tnode; hdispid: longint; hparavs: tparavarsym; begin p.propaccesslist[palt_read].clear; p.propaccesslist[palt_write].clear; hasread:=true; haswrite:=true; if try_to_consume(_READONLY) then haswrite:=false else if try_to_consume(_WRITEONLY) then hasread:=false; if try_to_consume(_DISPID) then begin pt:=comp_expr(true,false); if is_constintnode(pt) then if (Tordconstnode(pt).valueint64(high(longint))) then message3(type_e_range_check_error_bounds,tostr(Tordconstnode(pt).value),tostr(low(longint)),tostr(high(longint))) else hdispid:=Tordconstnode(pt).value.svalue else Message(parser_e_dispid_must_be_ord_const); pt.free; end else hdispid:=tobjectdef(astruct).get_next_dispid; { COM property is simply a pair of methods, tagged with 'propertyget' and 'propertyset' flags (or a single method if access is restricted). Creating these implicit accessor methods also allows the rest of compiler to handle dispinterface properties the same way as regular ones. } if hasread then begin readpd.returndef:=p.propdef; readpd.dispid:=hdispid; readpd.proctypeoption:=potype_propgetter; create_accessor_procsym(p,readpd,'get$',palt_read); end; if haswrite then begin { add an extra parameter, a placeholder of the value to set } inc(paranr); hparavs:=tparavarsym.create('$value',10*paranr,vs_value,p.propdef,[]); writepd.parast.insert(hparavs); writepd.proctypeoption:=potype_propsetter; writepd.dispid:=hdispid; create_accessor_procsym(p,writepd,'put$',palt_write); end; end; var sym : tsym; srsymtable: tsymtable; p : tpropertysym; overridden : tsym; varspez : tvarspez; hdef : tdef; arraytype : tdef; def : tdef; pt : tnode; sc : TFPObjectList; paranr : word; i : longint; ImplIntf : TImplementedInterface; found : boolean; hreadparavs, hparavs : tparavarsym; storedprocdef: tprocvardef; readprocdef, writeprocdef : tprocdef; {$ifdef jvm} orgaccesspd : tprocdef; wrongvisibility : boolean; {$endif} begin { Generate temp procdefs to search for matching read/write procedures. the readprocdef will store all definitions } paranr:=0; readprocdef:=tprocdef.create(normal_function_level); writeprocdef:=tprocdef.create(normal_function_level); readprocdef.struct:=astruct; writeprocdef.struct:=astruct; if assigned(astruct) and is_classproperty then begin readprocdef.procoptions:=[po_staticmethod,po_classmethod]; writeprocdef.procoptions:=[po_staticmethod,po_classmethod]; end; if token<>_ID then begin consume(_ID); consume(_SEMICOLON); exit; end; { Generate propertysym and insert in symtablestack } p:=tpropertysym.create(orgpattern); p.visibility:=symtablestack.top.currentvisibility; p.default:=longint($80000000); if is_classproperty then include(p.symoptions, sp_static); symtablestack.top.insert(p); consume(_ID); { property parameters ? } if try_to_consume(_LECKKLAMMER) then begin if (p.visibility=vis_published) and not (m_delphi in current_settings.modeswitches) then Message(parser_e_cant_publish_that_property); { create a list of the parameters } p.parast:=tparasymtable.create(nil,0); symtablestack.push(p.parast); sc:=TFPObjectList.create(false); repeat if try_to_consume(_VAR) then varspez:=vs_var else if try_to_consume(_CONST) then varspez:=vs_const else if try_to_consume(_CONSTREF) then varspez:=vs_constref else if (m_out in current_settings.modeswitches) and try_to_consume(_OUT) then varspez:=vs_out else varspez:=vs_value; sc.clear; repeat inc(paranr); hreadparavs:=tparavarsym.create(orgpattern,10*paranr,varspez,generrordef,[]); p.parast.insert(hreadparavs); sc.add(hreadparavs); consume(_ID); until not try_to_consume(_COMMA); if try_to_consume(_COLON) then begin if try_to_consume(_ARRAY) then begin consume(_OF); { define range and type of range } hdef:=tarraydef.create(0,-1,s32inttype); { define field type } single_type(arraytype,[]); tarraydef(hdef).elementdef:=arraytype; end else single_type(hdef,[]); end else hdef:=cformaltype; for i:=0 to sc.count-1 do tparavarsym(sc[i]).vardef:=hdef; until not try_to_consume(_SEMICOLON); sc.free; symtablestack.pop(p.parast); consume(_RECKKLAMMER); { the parser need to know if a property has parameters, the index parameter doesn't count (PFV) } if paranr>0 then begin p.add_accessor_parameters(readprocdef,writeprocdef); include(p.propoptions,ppo_hasparameters); end; end; { overridden property ? } { force property interface there is a property parameter a global property } if (token=_COLON) or (paranr>0) or (astruct=nil) then begin consume(_COLON); single_type(p.propdef,[stoAllowSpecialization]); if is_dispinterface(astruct) and not is_automatable(p.propdef) then Message1(type_e_not_automatable,p.propdef.typename); if (idtoken=_INDEX) then begin consume(_INDEX); pt:=comp_expr(true,false); { Only allow enum and integer indexes. Convert all integer values to s32int to be compatible with delphi, because the procedure matching requires equal parameters } if is_constnode(pt) and is_ordinal(pt.resultdef) {$ifndef cpu64bitaddr} and (not is_64bitint(pt.resultdef)) {$endif cpu64bitaddr} then begin if is_integer(pt.resultdef) then inserttypeconv_internal(pt,s32inttype); p.index:=tordconstnode(pt).value.svalue; end else begin Message(parser_e_invalid_property_index_value); p.index:=0; end; p.indexdef:=pt.resultdef; include(p.propoptions,ppo_indexed); { concat a longint to the para templates } p.add_index_parameter(paranr,readprocdef,writeprocdef); pt.free; end; end else begin { do an property override } if (astruct.typ=objectdef) then overridden:=search_struct_member(tobjectdef(astruct).childof,p.name) else overridden:=nil; if assigned(overridden) and (overridden.typ=propertysym) and not(is_dispinterface(astruct)) then begin tpropertysym(overridden).makeduplicate(p,readprocdef,writeprocdef,paranr); p.overriddenpropsym:=tpropertysym(overridden); include(p.propoptions,ppo_overrides); end else begin p.propdef:=generrordef; message(parser_e_no_property_found_to_override); end; end; if ((p.visibility=vis_published) or is_dispinterface(astruct)) and (not(p.propdef.is_publishable) or (sp_static in p.symoptions)) then begin Message(parser_e_cant_publish_that_property); p.visibility:=vis_public; end; if not(is_dispinterface(astruct)) then begin if try_to_consume(_READ) then begin p.propaccesslist[palt_read].clear; if parse_symlist(p.propaccesslist[palt_read],def) then begin sym:=p.propaccesslist[palt_read].firstsym^.sym; case sym.typ of procsym : begin { read is function returning the type of the property } readprocdef.returndef:=p.propdef; { Insert hidden parameters } handle_calling_convention(readprocdef); { search procdefs matching readprocdef } { we ignore hidden stuff here because the property access symbol might have non default calling conventions which might change the hidden stuff; see tw3216.pp (FK) } p.propaccesslist[palt_read].procdef:=Tprocsym(sym).Find_procdef_bypara(readprocdef.paras,p.propdef,[cpo_allowdefaults,cpo_ignorehidden]); if not assigned(p.propaccesslist[palt_read].procdef) or { because of cpo_ignorehidden we need to compare if it is a static class method and we have a class property } ((sp_static in p.symoptions) <> tprocdef(p.propaccesslist[palt_read].procdef).no_self_node) then Message(parser_e_ill_property_access_sym) else begin {$ifdef jvm} orgaccesspd:=tprocdef(p.propaccesslist[palt_read].procdef); wrongvisibility:=tprocdef(p.propaccesslist[palt_read].procdef).visibility'') and (wrongvisibility or (p.propaccesslist[palt_read].firstsym^.sym.RealName<>prop_auto_getter_prefix+p.RealName)) then jvm_create_getter_for_property(p,orgaccesspd) { if the visibility of the getter is lower than the visibility of the property, wrap it so that we can call it from all contexts in which the property is visible } else if wrongvisibility then begin p.propaccesslist[palt_read].procdef:=jvm_wrap_method_with_vis(tprocdef(p.propaccesslist[palt_read].procdef),p.visibility); p.propaccesslist[palt_read].firstsym^.sym:=tprocdef(p.propaccesslist[palt_read].procdef).procsym; end; {$endif jvm} end; end; fieldvarsym : begin if not assigned(def) then internalerror(200310071); if compare_defs(def,p.propdef,nothingn)>=te_equal then begin { property parameters are allowed if this is an indexed property, because the index is then the parameter. Note: In the help of Kylix it is written that it isn't allowed, but the compiler accepts it (PFV) } if (ppo_hasparameters in p.propoptions) or ((sp_static in p.symoptions) <> (sp_static in sym.symoptions)) then Message(parser_e_ill_property_access_sym); {$ifdef jvm} { if the visibility of the field is lower than the visibility of the property, wrap it in a getter so that we can access it from all contexts in which the property is visibile } if (prop_auto_getter_prefix<>'') or (tfieldvarsym(sym).visibility