Reintegration of Custom Attributes branch by Joost van der Sluis with patches reworked by Svetozar Belic [patch 1/3]

Implemented attributes for class types and properties (based on work by Joost van der Sluis). Added TCustomAttribute - a base class for attributes. Added TUnitInfo record to RTTI. It contains the unit name and unit options (for now only a flag which specifies if the unit contains attributes). Added several tests for attributes.

git-svn-id: trunk@42356 -
This commit is contained in:
svenbarth 2019-07-12 22:04:48 +00:00
parent 28e07f77e4
commit b2932393df
31 changed files with 1126 additions and 72 deletions

10
.gitattributes vendored
View File

@ -13139,6 +13139,16 @@ tests/test/tclass6.pp svneol=native#text/plain
tests/test/tclass7.pp svneol=native#text/plain tests/test/tclass7.pp svneol=native#text/plain
tests/test/tclass8.pp svneol=native#text/plain tests/test/tclass8.pp svneol=native#text/plain
tests/test/tclass9.pp svneol=native#text/pascal tests/test/tclass9.pp svneol=native#text/pascal
tests/test/tclassattribute1.pp svneol=native#text/pascal
tests/test/tclassattribute10.pp svneol=native#text/pascal
tests/test/tclassattribute2.pp svneol=native#text/pascal
tests/test/tclassattribute3.pp svneol=native#text/pascal
tests/test/tclassattribute4.pp svneol=native#text/pascal
tests/test/tclassattribute5.pp svneol=native#text/pascal
tests/test/tclassattribute6.pp svneol=native#text/pascal
tests/test/tclassattribute7.pp svneol=native#text/pascal
tests/test/tclassattribute8.pp svneol=native#text/pascal
tests/test/tclassattribute9.pp svneol=native#text/pascal
tests/test/tclassinfo1.pp svneol=native#text/pascal tests/test/tclassinfo1.pp svneol=native#text/pascal
tests/test/tclrprop.pp svneol=native#text/plain tests/test/tclrprop.pp svneol=native#text/plain
tests/test/tcmov1.pp svneol=native#text/plain tests/test/tcmov1.pp svneol=native#text/plain

View File

@ -44,7 +44,7 @@ interface
uses uses
cutils,cclasses,cfileutl, cutils,cclasses,cfileutl,
globtype,finput,ogbase,fpkg, globtype,finput,ogbase,fpkg,
symbase,symsym, symbase,symsym,symtype,
wpobase, wpobase,
aasmbase,aasmdata; aasmbase,aasmdata;
@ -68,6 +68,9 @@ interface
); );
tmoduleoptions = set of tmoduleoption; tmoduleoptions = set of tmoduleoption;
trtti_moduleoption = (rmo_hasattributes);
trtti_moduleoptions = set of trtti_moduleoption;
tlinkcontaineritem=class(tlinkedlistitem) tlinkcontaineritem=class(tlinkedlistitem)
public public
data : TPathStr; data : TPathStr;
@ -195,6 +198,11 @@ interface
moduleoptions: tmoduleoptions; moduleoptions: tmoduleoptions;
deprecatedmsg: pshortstring; deprecatedmsg: pshortstring;
{ contains a reference to the TUnitInfo rtti information for this module }
rttiunitinfo : TAsmSymbol;
rttiunitinfodef : tdef;
rtti_options : trtti_moduleoptions;
{ contains a list of types that are extended by helper types; the key is { contains a list of types that are extended by helper types; the key is
the full name of the type and the data is a TFPObjectList of the full name of the type and the data is a TFPObjectList of
tobjectdef instances (the helper defs) } tobjectdef instances (the helper defs) }
@ -634,6 +642,8 @@ implementation
deprecatedmsg:=nil; deprecatedmsg:=nil;
namespace:=nil; namespace:=nil;
tcinitcode:=nil; tcinitcode:=nil;
rttiunitinfo:=nil;
rttiunitinfodef:=nil;
_exports:=TLinkedList.Create; _exports:=TLinkedList.Create;
dllscannerinputlist:=TFPHashList.Create; dllscannerinputlist:=TFPHashList.Create;
asmdata:=casmdata.create(modulename); asmdata:=casmdata.create(modulename);

View File

@ -55,7 +55,7 @@ interface
m_pointer_2_procedure,m_autoderef,m_tp_procvar,m_initfinal,m_default_ansistring, m_pointer_2_procedure,m_autoderef,m_tp_procvar,m_initfinal,m_default_ansistring,
m_out,m_default_para,m_duplicate_names,m_hintdirective, m_out,m_default_para,m_duplicate_names,m_hintdirective,
m_property,m_default_inline,m_except,m_advanced_records, m_property,m_default_inline,m_except,m_advanced_records,
m_array_operators]; m_array_operators,m_prefixed_attributes];
delphiunicodemodeswitches = delphimodeswitches + [m_systemcodepage,m_default_unicodestring]; delphiunicodemodeswitches = delphimodeswitches + [m_systemcodepage,m_default_unicodestring];
fpcmodeswitches = fpcmodeswitches =
[m_fpc,m_string_pchar,m_nested_comment,m_repeat_forward, [m_fpc,m_string_pchar,m_nested_comment,m_repeat_forward,

View File

@ -490,7 +490,8 @@ interface
m_isolike_mod, { mod operation as it is required by an iso compatible compiler } m_isolike_mod, { mod operation as it is required by an iso compatible compiler }
m_array_operators, { use Delphi compatible array operators instead of custom ones ("+") } m_array_operators, { use Delphi compatible array operators instead of custom ones ("+") }
m_multi_helpers, { helpers can appear in multiple scopes simultaneously } m_multi_helpers, { helpers can appear in multiple scopes simultaneously }
m_array2dynarray { regular arrays can be implicitly converted to dynamic arrays } m_array2dynarray, { regular arrays can be implicitly converted to dynamic arrays }
m_prefixed_attributes { enable attributes that are defined before the type they belong to }
); );
tmodeswitches = set of tmodeswitch; tmodeswitches = set of tmodeswitch;
@ -681,7 +682,8 @@ interface
'ISOMOD', 'ISOMOD',
'ARRAYOPERATORS', 'ARRAYOPERATORS',
'MULTIHELPERS', 'MULTIHELPERS',
'ARRAYTODYNARRAY' 'ARRAYTODYNARRAY',
'PREFIXEDATTRIBUTES'
); );

View File

@ -146,7 +146,7 @@ general_t_unitscope=01027_T_Using unit scope: $1
# #
# Scanner # Scanner
# #
# 02105 is the last used one # 02106 is the last used one
# #
% \section{Scanner messages.} % \section{Scanner messages.}
% This section lists the messages that the scanner emits. The scanner takes % This section lists the messages that the scanner emits. The scanner takes
@ -432,6 +432,8 @@ scan_w_setpeosversion_not_support=02103_W_SETPEOSVERSION is not supported by the
scan_w_setpesubsysversion_not_support=02104_W_SETPESUBSYSVERSION is not supported by the target OS scan_w_setpesubsysversion_not_support=02104_W_SETPESUBSYSVERSION is not supported by the target OS
% The \var{\{\$SETPESUBSYSVERSION\}} directive is not supported by the target OS. % The \var{\{\$SETPESUBSYSVERSION\}} directive is not supported by the target OS.
scan_n_changecputype=02105_N_Changed CPU type to be consistent with specified controller scan_n_changecputype=02105_N_Changed CPU type to be consistent with specified controller
scan_e_unresolved_attribute=02106_E_Unresolved custom attribute: "$1".
% A custom attribute is defined, but there is no identifier to bind it to.
% \end{description} % \end{description}
# #
# Parser # Parser

View File

@ -57,6 +57,8 @@ interface
function ref_rtti(def:tdef;rt:trttitype;indirect:boolean;suffix:tsymstr):tasmsymbol; function ref_rtti(def:tdef;rt:trttitype;indirect:boolean;suffix:tsymstr):tasmsymbol;
procedure write_rtti_name(tcb: ttai_typedconstbuilder; def: tdef); procedure write_rtti_name(tcb: ttai_typedconstbuilder; def: tdef);
procedure write_rtti_data(tcb: ttai_typedconstbuilder; def:tdef; rt: trttitype); procedure write_rtti_data(tcb: ttai_typedconstbuilder; def:tdef; rt: trttitype);
procedure write_attribute_data(tcb: ttai_typedconstbuilder; def:tdef);
procedure write_unit_info_reference(tcb: ttai_typedconstbuilder);
procedure write_child_rtti_data(def:tdef;rt:trttitype); procedure write_child_rtti_data(def:tdef;rt:trttitype);
procedure write_rtti_reference(tcb: ttai_typedconstbuilder; def: tdef; rt: trttitype); procedure write_rtti_reference(tcb: ttai_typedconstbuilder; def: tdef; rt: trttitype);
procedure write_methods(tcb:ttai_typedconstbuilder;st:tsymtable;visibilities:tvisibilities); procedure write_methods(tcb:ttai_typedconstbuilder;st:tsymtable;visibilities:tvisibilities);
@ -72,6 +74,8 @@ interface
function get_rtti_label(def:tdef;rt:trttitype;indirect:boolean):tasmsymbol; inline; function get_rtti_label(def:tdef;rt:trttitype;indirect:boolean):tasmsymbol; inline;
function get_rtti_label_ord2str(def:tdef;rt:trttitype;indirect:boolean):tasmsymbol; inline; function get_rtti_label_ord2str(def:tdef;rt:trttitype;indirect:boolean):tasmsymbol; inline;
function get_rtti_label_str2ord(def:tdef;rt:trttitype;indirect:boolean):tasmsymbol; inline; function get_rtti_label_str2ord(def:tdef;rt:trttitype;indirect:boolean):tasmsymbol; inline;
procedure start_write_unit_info;
procedure after_write_unit_info(st: TSymtable);
end; end;
{ generate RTTI and init tables } { generate RTTI and init tables }
@ -116,6 +120,8 @@ implementation
{ no Delphi-style RTTI for managed platforms } { no Delphi-style RTTI for managed platforms }
if target_info.system in systems_managed_vm then if target_info.system in systems_managed_vm then
exit; exit;
if current_module.rttiunitinfo=nil then
RTTIWriter.start_write_unit_info;
for i:=0 to st.DefList.Count-1 do for i:=0 to st.DefList.Count-1 do
begin begin
def:=tdef(st.DefList[i]); def:=tdef(st.DefList[i]);
@ -170,6 +176,8 @@ implementation
(ds_rtti_table_used in def.defstates) then (ds_rtti_table_used in def.defstates) then
RTTIWriter.write_rtti(def,fullrtti); RTTIWriter.write_rtti(def,fullrtti);
end; end;
if st.symtabletype = staticsymtable then
RTTIWriter.after_write_unit_info(st);
end; end;
@ -753,6 +761,9 @@ implementation
proctypesinfo : byte; proctypesinfo : byte;
propnameitem : tpropnamelistitem; propnameitem : tpropnamelistitem;
propdefname : string; propdefname : string;
attridx: ShortInt;
attrcount: byte;
attr: trtti_attribute;
procedure writeaccessproc(pap:tpropaccesslisttypes; shiftvalue : byte; unsetvalue: byte); procedure writeaccessproc(pap:tpropaccesslisttypes; shiftvalue : byte; unsetvalue: byte);
var var
@ -897,7 +908,23 @@ implementation
internalerror(200512201); internalerror(200512201);
tcb.emit_ord_const(propnameitem.propindex,u16inttype); tcb.emit_ord_const(propnameitem.propindex,u16inttype);
tcb.emit_ord_const(proctypesinfo,u8inttype); tcb.emit_ord_const(proctypesinfo,u8inttype);
{ Write property attribute count }
if assigned(tpropertysym(sym).rtti_attributesdef) then
attrcount:=tpropertysym(sym).rtti_attributesdef.get_attribute_count
else
attrcount:=0;
tcb.emit_ord_const(attrcount,u8inttype);
{ Write property name }
tcb.emit_shortstring_const(tpropertysym(sym).realname); tcb.emit_shortstring_const(tpropertysym(sym).realname);
{ Write property attributes }
for attridx := 0 to attrcount-1 do
begin
attr := trtti_attribute(tpropertysym(sym).rtti_attributesdef.rtti_attributes[attridx]);
tcb.emit_tai(Tai_const.Createname(attr.symbolname,AT_DATA_FORCEINDIRECT,0), cpointerdef.getreusable(ttypesym(attr.typesym).typedef));
end;
tcb.end_anonymous_record; tcb.end_anonymous_record;
end; end;
end; end;
@ -1551,8 +1578,12 @@ implementation
{ total number of unique properties } { total number of unique properties }
tcb.emit_ord_const(propnamelist.count,u16inttype); tcb.emit_ord_const(propnamelist.count,u16inttype);
{ write unit name } { reference to unitinfo with unit-name }
tcb.emit_shortstring_const(current_module.realmodulename^); write_unit_info_reference(tcb);
{ TAttributeData }
if rmo_hasattributes in current_module.rtti_options then
write_attribute_data(tcb, def);
{ write published properties for this object } { write published properties for this object }
published_properties_write_rtti_data(tcb,propnamelist,def.symtable); published_properties_write_rtti_data(tcb,propnamelist,def.symtable);
@ -1715,6 +1746,31 @@ implementation
end; end;
end; end;
procedure TRTTIWriter.write_attribute_data(tcb: ttai_typedconstbuilder; def: tdef);
var
count: word;
idx: byte;
attr: trtti_attribute;
begin
if (def.typ = objectdef) and (assigned(tobjectdef(def).rtti_attributesdef)) then
count:=tobjectdef(def).rtti_attributesdef.get_attribute_count
else
count:=0;
tcb.emit_ord_const(count,u16inttype);
if count>0 then
for idx:=0 to count-1 do
begin
attr := trtti_attribute(tobjectdef(def).rtti_attributesdef.rtti_attributes[idx]);
tcb.emit_tai(Tai_const.Createname(attr.symbolname,AT_DATA_FORCEINDIRECT,0), cpointerdef.getreusable(ttypesym(attr.typesym).typedef));
end;
end;
procedure TRTTIWriter.write_unit_info_reference(tcb: ttai_typedconstbuilder);
begin
tcb.emit_tai(Tai_const.Create_sym(current_module.rttiunitinfo), current_module.rttiunitinfodef);
end;
function enumsym_compare_name(item1, item2: pointer): Integer; function enumsym_compare_name(item1, item2: pointer): Integer;
var var
@ -2098,5 +2154,37 @@ implementation
end; end;
end; end;
procedure TRTTIWriter.start_write_unit_info;
var
s : string;
tcb: ttai_typedconstbuilder;
begin
tcb:=ctai_typedconstbuilder.create([tcalo_make_dead_strippable,tcalo_new_section]);
tcb.begin_anonymous_record(make_mangledname('RTTIU',current_module.localsymtable,''), 1, sizeof(pint), 1, 1);
{ write the TRTTIUnitOptions }
tcb.emit_ord_const(byte(longint(current_module.rtti_options)),u8inttype);
{ Write the unit-name }
s := current_module.realmodulename^;
tcb.emit_shortstring_const(current_module.realmodulename^);
current_module.rttiunitinfodef := tcb.end_anonymous_record;
current_module.rttiunitinfo := current_asmdata.DefineAsmSymbol(make_mangledname('RTTIU_',current_module.localsymtable,''),AB_GLOBAL,AT_DATA, current_module.rttiunitinfodef);
current_asmdata.AsmLists[al_rtti].concatList(
tcb.get_final_asmlist(current_module.rttiunitinfo,current_module.rttiunitinfodef,sec_rodata,current_module.rttiunitinfo.name,const_align(sizeof(pint))));
tcb.free;
end;
procedure TRTTIWriter.after_write_unit_info(st: TSymtable);
begin
if current_module.rttiunitinfo<>nil then
begin
{ Write a trailing 255 to mark the end of the symbols-list }
current_asmdata.asmlists[al_rtti].concat(cai_align.Create(sizeof(TConstPtrUInt)));
current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(255));
end;
end;
end. end.

View File

@ -43,6 +43,8 @@ interface
end; end;
pinitfinalentry = ^tinitfinalentry; pinitfinalentry = ^tinitfinalentry;
{ tnodeutils }
tnodeutils = class tnodeutils = class
class function call_fail_node:tnode; virtual; class function call_fail_node:tnode; virtual;
class function initialize_data_node(p:tnode; force: boolean):tnode; virtual; class function initialize_data_node(p:tnode; force: boolean):tnode; virtual;
@ -113,6 +115,7 @@ interface
class function create_main_procdef(const name: string; potype:tproctypeoption; ps: tprocsym):tdef; virtual; class function create_main_procdef(const name: string; potype:tproctypeoption; ps: tprocsym):tdef; virtual;
class procedure InsertInitFinalTable; class procedure InsertInitFinalTable;
class procedure InsertRTTIUnitList; virtual;
protected protected
class procedure InsertRuntimeInits(const prefix:string;list:TLinkedList;unitflag:tmoduleflag); virtual; class procedure InsertRuntimeInits(const prefix:string;list:TLinkedList;unitflag:tmoduleflag); virtual;
class procedure InsertRuntimeInitsTablesTable(const prefix,tablename:string;unitflag:tmoduleflag); virtual; class procedure InsertRuntimeInitsTablesTable(const prefix,tablename:string;unitflag:tmoduleflag); virtual;
@ -1031,6 +1034,32 @@ implementation
release_init_final_list(entries); release_init_final_list(entries);
end; end;
class procedure tnodeutils.InsertRTTIUnitList;
var
hp : tused_unit;
unitinits : TAsmList;
count : longint;
begin
unitinits:=TAsmList.Create;
count:=0;
hp:=tused_unit(usedunits.first);
while assigned(hp) do
begin
unitinits.concat(Tai_const.Createname(make_mangledname('RTTIU_',hp.u.globalsymtable,''),0));
inc(count);
hp:=tused_unit(hp.next);
end;
{ Insert TableCount,InitCount at start }
unitinits.insert(Tai_const.Create_32bit(count));
{ Add to data segment }
maybe_new_object_file(current_asmdata.asmlists[al_globals]);
new_section(current_asmdata.asmlists[al_globals],sec_data,'RTTIUNITLIST',sizeof(pint));
current_asmdata.asmlists[al_globals].concat(Tai_symbol.Createname_global('RTTIUNITLIST',AT_DATA,0, carraydef.getreusable(cansichartype,length('RTTIUNITLIST'))));
current_asmdata.asmlists[al_globals].concatlist(unitinits);
current_asmdata.asmlists[al_globals].concat(Tai_symbol_end.Createname('RTTIUNITLIST'));
unitinits.free;
end;
class procedure tnodeutils.insert_init_final_table(entries:tfplist); class procedure tnodeutils.insert_init_final_table(entries:tfplist);
var var

View File

@ -41,24 +41,27 @@ interface
procedure consts_dec(in_structure, allow_typed_const: boolean;out had_generic:boolean); procedure consts_dec(in_structure, allow_typed_const: boolean;out had_generic:boolean);
procedure label_dec; procedure label_dec;
procedure type_dec(out had_generic:boolean); procedure type_dec(out had_generic:boolean);
procedure types_dec(in_structure: boolean;out had_generic:boolean); procedure types_dec(in_structure: boolean;out had_generic:boolean;var rtti_attrs_def: trtti_attributesdef);
procedure var_dec(out had_generic:boolean); procedure var_dec(out had_generic:boolean);
procedure threadvar_dec(out had_generic:boolean); procedure threadvar_dec(out had_generic:boolean);
procedure property_dec; procedure property_dec;
procedure resourcestring_dec(out had_generic:boolean); procedure resourcestring_dec(out had_generic:boolean);
procedure parse_rttiattributes(var rtti_attrs_def: trtti_attributesdef);
procedure add_synthetic_rtti_funtion_declarations(rtti_attrs_def: trtti_attributesdef; name: shortstring);
implementation implementation
uses uses
SysUtils,
{ common } { common }
cutils, cutils,
{ global } { global }
globals,tokens,verbose,widestr,constexp, globals,tokens,verbose,widestr,constexp,
systems,aasmdata,fmodule,compinnr, systems,aasmdata,fmodule,compinnr,
{ symtable } { symtable }
symconst,symbase,symtype,symcpu,symcreat,defutil, symconst,symbase,symtype,symcpu,symcreat,defutil,defcmp,symtable,
{ pass 1 } { pass 1 }
ninl,ncon,nobj,ngenutil, ninl,ncon,nobj,ngenutil,nld,nmem,ncal,
{ parser } { parser }
scanner, scanner,
pbase,pexpr,ptype,ptconst,pdecsub,pdecvar,pdecobj,pgenutil,pparautl, pbase,pexpr,ptype,ptconst,pdecsub,pdecvar,pdecobj,pgenutil,pparautl,
@ -69,6 +72,39 @@ implementation
cpuinfo cpuinfo
; ;
var
system_custom_attribute_def: tobjectdef = nil;
function is_system_custom_attribute_descendant(def:tdef): Boolean;
begin
if system_custom_attribute_def=nil then
system_custom_attribute_def := tobjectdef(search_system_type('TCUSTOMATTRIBUTE').typedef);
Result := def_is_related(def, system_custom_attribute_def);
end;
procedure create_renamed_attr_type_if_needed(hdef: tobjectdef);
const
attrconst = 'attribute';
var
newname : TIDString;
newtypeattr : ttypesym;
i: integer;
begin
if not is_system_custom_attribute_descendant(hdef) then
Exit;
{ Check if the name ends with 'attribute'. }
i := Pos(attrconst, lower(hdef.typename), max(0, length(hdef.typename) - length(attrconst)));
newname:=Copy(hdef.typename, 0, i-1);
if (i > 0) and (length(newname) > 0) then
begin
{ Create a new typesym with 'attribute' removed. }
newtypeattr:=ctypesym.create(newname,hdef,true);
newtypeattr.visibility:=symtablestack.top.currentvisibility;
include(newtypeattr.symoptions,sp_implicitrename);
symtablestack.top.insert(newtypeattr);
end;
end;
function readconstant(const orgname:string;const filepos:tfileposinfo; out nodetype: tnodetype):tconstsym; function readconstant(const orgname:string;const filepos:tfileposinfo; out nodetype: tnodetype):tconstsym;
var var
@ -386,7 +422,100 @@ implementation
consume(_SEMICOLON); consume(_SEMICOLON);
end; end;
procedure types_dec(in_structure: boolean;out had_generic:boolean); function find_create_constructor(objdef: tobjectdef): tsymentry;
begin
while assigned(objdef) do
begin
result:=objdef.symtable.Find('CREATE');
if assigned(result) then
exit;
objdef:=objdef.childof;
end;
// A class without a constructor called 'create'?!?
internalerror(2012111101);
end;
procedure parse_rttiattributes(var rtti_attrs_def: trtti_attributesdef);
var
p, p1: tnode;
again: boolean;
od: tobjectdef;
constrSym: tsymentry;
constrProcDef: tprocdef;
typeSym: ttypesym;
oldblock_type: tblock_type;
begin
consume(_LECKKLAMMER);
{ Parse attribute type }
p := factor(false,[ef_type_only]);
if p.nodetype<> errorn then
begin
typeSym := ttypesym(ttypenode(p).typesym);
od := tobjectdef(ttypenode(p).typedef);
if Assigned(od) then
begin
{ Check if the attribute class is related to TCustomAttribute }
if not is_system_custom_attribute_descendant(od) then
incompatibletypes(od, system_custom_attribute_def);
{ Search the tprocdef of the constructor which has to be called. }
constrSym := find_create_constructor(od);
if constrSym.typ<>procsym then
internalerror(2018102301);
constrProcDef:=tprocsym(constrSym).find_procdef_bytype(potype_constructor);
{ Parse the attribute-parameters as if it is a list of parameters from
a call to the constrProcDef constructor in an execution-block. }
p1 := cloadvmtaddrnode.create(ctypenode.create(od));
again:=true;
oldblock_type := block_type;
block_type := bt_body;
do_member_read(od,false,constrProcDef.procsym,p1,again,[], nil);
{ Check the number of parameters }
if (tcallnode(p1).para_count < constrProcDef.minparacount) then
CGMessagePos1(p.fileinfo, parser_e_wrong_parameter_size, od.typename + '.' + constrProcDef.procsym.prettyname);
block_type:=oldblock_type;
{ Add attribute to attribute list which will be added
to the property which is defined next. }
if not assigned(rtti_attrs_def) then
rtti_attrs_def := trtti_attributesdef.create;
rtti_attrs_def.addattribute(typeSym,p1);
Include(current_module.rtti_options, rmo_hasattributes);
end;
end;
p.free;
consume(_RECKKLAMMER);
end;
procedure add_synthetic_rtti_funtion_declarations(rtti_attrs_def: trtti_attributesdef; name: shortstring);
var
i: Integer;
sstate: tscannerstate;
attribute: trtti_attribute;
pd: tprocdef;
begin
name := StringReplace(name, '.', '_', [rfReplaceAll]);
for i := 0 to rtti_attrs_def.get_attribute_count-1 do
begin
attribute := trtti_attribute(rtti_attrs_def.rtti_attributes[i]);
replace_scanner('rtti_class_attributes',sstate);
if str_parse_method_dec('function rtti_'+name+'_'+IntToStr(i)+':'+ attribute.typesym.Name +';',potype_function,false,tabstractrecorddef(ttypesym(attribute.typesym).typedef),pd) then
pd.synthetickind:=tsk_get_rttiattribute
else
internalerror(2012052601);
pd.skpara:=attribute;
attribute.symbolname:=pd.mangledname;
restore_scanner(sstate);
end;
end;
procedure types_dec(in_structure: boolean;out had_generic:boolean;var rtti_attrs_def: trtti_attributesdef);
function determine_generic_def(name:tidstring):tstoreddef; function determine_generic_def(name:tidstring):tstoreddef;
var var
@ -484,6 +613,11 @@ implementation
generictypelist:=nil; generictypelist:=nil;
generictokenbuf:=nil; generictokenbuf:=nil;
{ class attribute definitions? }
if m_prefixed_attributes in current_settings.modeswitches then
while token=_LECKKLAMMER do
parse_rttiattributes(rtti_attrs_def);
{ fpc generic declaration? } { fpc generic declaration? }
if first then if first then
had_generic:=not(m_delphi in current_settings.modeswitches) and try_to_consume(_GENERIC); had_generic:=not(m_delphi in current_settings.modeswitches) and try_to_consume(_GENERIC);
@ -888,6 +1022,15 @@ implementation
vmtbuilder.free; vmtbuilder.free;
end; end;
{ If there are attribute-properties available, bind them to
this object }
if assigned(rtti_attrs_def) then
begin
add_synthetic_rtti_funtion_declarations(rtti_attrs_def,hdef.typesym.Name);
tobjectdef(hdef).rtti_attributesdef:=rtti_attrs_def;
rtti_attrs_def := nil;
end;
{ In case of an objcclass, verify that all methods have a message { In case of an objcclass, verify that all methods have a message
name set. We only check this now, because message names can be set name set. We only check this now, because message names can be set
during the protocol (interface) mapping. At the same time, set the during the protocol (interface) mapping. At the same time, set the
@ -903,6 +1046,9 @@ implementation
if is_cppclass(hdef) then if is_cppclass(hdef) then
tobjectdef(hdef).finish_cpp_data; tobjectdef(hdef).finish_cpp_data;
if (m_prefixed_attributes in current_settings.modeswitches) then
create_renamed_attr_type_if_needed(tobjectdef(hdef));
end; end;
recorddef : recorddef :
begin begin
@ -942,7 +1088,10 @@ implementation
else else
had_generic:=false; had_generic:=false;
first:=false; first:=false;
until (token<>_ID) or if assigned(rtti_attrs_def) and (rtti_attrs_def.get_attribute_count>0) then
Message1(scan_e_unresolved_attribute,trtti_attribute(rtti_attrs_def.rtti_attributes[0]).typesym.prettyname);
until ((token<>_ID) and (token<>_LECKKLAMMER)) or
(in_structure and (in_structure and
((idtoken in [_PRIVATE,_PROTECTED,_PUBLIC,_PUBLISHED,_STRICT]) or ((idtoken in [_PRIVATE,_PROTECTED,_PUBLIC,_PUBLISHED,_STRICT]) or
((m_final_fields in current_settings.modeswitches) and ((m_final_fields in current_settings.modeswitches) and
@ -958,9 +1107,12 @@ implementation
{ reads a type declaration to the symbol table } { reads a type declaration to the symbol table }
procedure type_dec(out had_generic:boolean); procedure type_dec(out had_generic:boolean);
var
rtti_attrs_def: trtti_attributesdef;
begin begin
consume(_TYPE); consume(_TYPE);
types_dec(false,had_generic); rtti_attrs_def := nil;
types_dec(false,had_generic,rtti_attrs_def);
end; end;

View File

@ -39,7 +39,7 @@ interface
function class_destructor_head(astruct: tabstractrecorddef):tprocdef; function class_destructor_head(astruct: tabstractrecorddef):tprocdef;
function constructor_head:tprocdef; function constructor_head:tprocdef;
function destructor_head:tprocdef; function destructor_head:tprocdef;
procedure struct_property_dec(is_classproperty:boolean); procedure struct_property_dec(is_classproperty:boolean;var rtti_attrs_def: trtti_attributesdef);
implementation implementation
@ -162,7 +162,7 @@ implementation
end; end;
procedure struct_property_dec(is_classproperty:boolean); procedure struct_property_dec(is_classproperty:boolean;var rtti_attrs_def: trtti_attributesdef);
var var
p : tpropertysym; p : tpropertysym;
begin begin
@ -214,6 +214,13 @@ implementation
Message(parser_e_enumerator_identifier_required); Message(parser_e_enumerator_identifier_required);
consume(_SEMICOLON); consume(_SEMICOLON);
end; end;
if assigned(rtti_attrs_def) then
begin
add_synthetic_rtti_funtion_declarations(rtti_attrs_def,current_structdef.RttiName+'_'+p.RealName);
p.rtti_attributesdef := rtti_attrs_def;
rtti_attrs_def:=nil;
end;
{ hint directives, these can be separated by semicolons here, { hint directives, these can be separated by semicolons here,
that needs to be handled here with a loop (PFV) } that needs to be handled here with a loop (PFV) }
while try_consume_hintdirective(p.symoptions,p.deprecatedmsg) do while try_consume_hintdirective(p.symoptions,p.deprecatedmsg) do
@ -1056,6 +1063,7 @@ implementation
threadvar_fields : boolean; threadvar_fields : boolean;
vdoptions: tvar_dec_options; vdoptions: tvar_dec_options;
fieldlist: tfpobjectlist; fieldlist: tfpobjectlist;
rtti_attrs_def: trtti_attributesdef;
procedure parse_const; procedure parse_const;
@ -1153,6 +1161,7 @@ implementation
class_fields:=false; class_fields:=false;
is_final:=false; is_final:=false;
final_fields:=false; final_fields:=false;
rtti_attrs_def:=nil;
hadgeneric:=false; hadgeneric:=false;
threadvar_fields:=false; threadvar_fields:=false;
object_member_blocktype:=bt_general; object_member_blocktype:=bt_general;
@ -1168,10 +1177,12 @@ implementation
end; end;
_VAR : _VAR :
begin begin
rtti_attrs_def := nil;
parse_var(false); parse_var(false);
end; end;
_CONST: _CONST:
begin begin
rtti_attrs_def := nil;
parse_const parse_const
end; end;
_THREADVAR : _THREADVAR :
@ -1266,6 +1277,7 @@ implementation
begin begin
if object_member_blocktype=bt_general then if object_member_blocktype=bt_general then
begin begin
rtti_attrs_def := nil;
if (idtoken=_GENERIC) and if (idtoken=_GENERIC) and
not (m_delphi in current_settings.modeswitches) and not (m_delphi in current_settings.modeswitches) and
( (
@ -1313,7 +1325,7 @@ implementation
end; end;
end end
else if object_member_blocktype=bt_type then else if object_member_blocktype=bt_type then
types_dec(true,hadgeneric) types_dec(true,hadgeneric, rtti_attrs_def)
else if object_member_blocktype=bt_const then else if object_member_blocktype=bt_const then
begin begin
typedconstswritable:=false; typedconstswritable:=false;
@ -1336,7 +1348,7 @@ implementation
end; end;
_PROPERTY : _PROPERTY :
begin begin
struct_property_dec(is_classdef); struct_property_dec(is_classdef, rtti_attrs_def);
fields_allowed:=false; fields_allowed:=false;
is_classdef:=false; is_classdef:=false;
end; end;
@ -1349,13 +1361,23 @@ implementation
_CONSTRUCTOR, _CONSTRUCTOR,
_DESTRUCTOR : _DESTRUCTOR :
begin begin
rtti_attrs_def := nil;
method_dec(current_structdef,is_classdef,hadgeneric); method_dec(current_structdef,is_classdef,hadgeneric);
fields_allowed:=false; fields_allowed:=false;
is_classdef:=false; is_classdef:=false;
hadgeneric:=false; hadgeneric:=false;
end; end;
_LECKKLAMMER:
begin
if m_prefixed_attributes in current_settings.modeswitches then
parse_rttiattributes(rtti_attrs_def)
else
consume(_ID);
end;
_END : _END :
begin begin
if assigned(rtti_attrs_def) and (rtti_attrs_def.get_attribute_count>0) then
Message1(scan_e_unresolved_attribute,trtti_attribute(rtti_attrs_def.rtti_attributes[0]).typesym.prettyname);
consume(_END); consume(_END);
break; break;
end; end;

View File

@ -2407,6 +2407,7 @@ type
cnodeutils.InsertWideInitsTablesTable; cnodeutils.InsertWideInitsTablesTable;
cnodeutils.InsertResStrTablesTable; cnodeutils.InsertResStrTablesTable;
cnodeutils.InsertMemorySizes; cnodeutils.InsertMemorySizes;
cnodeutils.InsertRTTIUnitList;
{ Insert symbol to resource info } { Insert symbol to resource info }
cnodeutils.InsertResourceInfo(resources_used); cnodeutils.InsertResourceInfo(resources_used);

View File

@ -33,6 +33,8 @@ interface
symdef,procinfo,optdfa; symdef,procinfo,optdfa;
type type
tcggetcodeblockfunc = function(pd: tprocdef) : tnode;
tcgprocinfo = class(tprocinfo) tcgprocinfo = class(tprocinfo)
private private
procedure CreateInlineInfo; procedure CreateInlineInfo;
@ -64,7 +66,7 @@ interface
procedure resetprocdef; procedure resetprocdef;
procedure add_to_symtablestack; procedure add_to_symtablestack;
procedure remove_from_symtablestack; procedure remove_from_symtablestack;
procedure parse_body; procedure parse_body(get_code_block_func: tcggetcodeblockfunc=nil);
function has_assembler_child : boolean; function has_assembler_child : boolean;
procedure set_eh_info; override; procedure set_eh_info; override;
@ -89,7 +91,7 @@ interface
{ reads any routine in the implementation, or a non-method routine { reads any routine in the implementation, or a non-method routine
declaration in the interface (depending on whether or not parse_only is declaration in the interface (depending on whether or not parse_only is
true) } true) }
procedure read_proc(isclassmethod:boolean; usefwpd: tprocdef;isgeneric:boolean); procedure read_proc(isclassmethod:boolean; usefwpd: tprocdef; isgeneric:boolean; get_code_block_func: tcggetcodeblockfunc = nil);
{ parses only the body of a non nested routine; needs a correctly setup pd } { parses only the body of a non nested routine; needs a correctly setup pd }
procedure read_proc_body(pd:tprocdef); procedure read_proc_body(pd:tprocdef);
@ -325,10 +327,44 @@ implementation
end; end;
end; end;
procedure init_main_block_syms(block: tnode);
var
oldfilepos: tfileposinfo;
begin
{ initialized variables }
if current_procinfo.procdef.localst.symtabletype=localsymtable then
begin
{ initialization of local variables with their initial
values: part of function entry }
oldfilepos:=current_filepos;
current_filepos:=current_procinfo.entrypos;
current_procinfo.procdef.localst.SymList.ForEachCall(@initializevars,block);
current_filepos:=oldfilepos;
end
else if current_procinfo.procdef.localst.symtabletype=staticsymtable then
begin
{ for program and unit initialization code we also need to
initialize the local variables used of Default() }
oldfilepos:=current_filepos;
current_filepos:=current_procinfo.entrypos;
current_procinfo.procdef.localst.SymList.ForEachCall(@initializedefaultvars,block);
current_filepos:=oldfilepos;
end;
if assigned(current_procinfo.procdef.parentfpstruct) then
begin
{ we only do this after the code has been parsed because
otherwise for-loop counters moved to the struct cause
errors; we still do it nevertheless to prevent false
"unused" symbols warnings and to assist debug info
generation }
redirect_parentfpstruct_local_syms(current_procinfo.procdef);
{ finish the parentfpstruct (add padding, ...) }
finish_parentfpstruct(current_procinfo.procdef);
end;
end;
function block(islibrary : boolean) : tnode; function block(islibrary : boolean) : tnode;
var
oldfilepos: tfileposinfo;
begin begin
{ parse const,types and vars } { parse const,types and vars }
read_declarations(islibrary); read_declarations(islibrary);
@ -388,37 +424,7 @@ implementation
begin begin
{ parse routine body } { parse routine body }
block:=statement_block(_BEGIN); block:=statement_block(_BEGIN);
{ initialized variables } init_main_block_syms(block);
if current_procinfo.procdef.localst.symtabletype=localsymtable then
begin
{ initialization of local variables with their initial
values: part of function entry }
oldfilepos:=current_filepos;
current_filepos:=current_procinfo.entrypos;
current_procinfo.procdef.localst.SymList.ForEachCall(@initializevars,block);
current_filepos:=oldfilepos;
end
else if current_procinfo.procdef.localst.symtabletype=staticsymtable then
begin
{ for program and unit initialization code we also need to
initialize the local variables used of Default() }
oldfilepos:=current_filepos;
current_filepos:=current_procinfo.entrypos;
current_procinfo.procdef.localst.SymList.ForEachCall(@initializedefaultvars,block);
current_filepos:=oldfilepos;
end;
if assigned(current_procinfo.procdef.parentfpstruct) then
begin
{ we only do this after the code has been parsed because
otherwise for-loop counters moved to the struct cause
errors; we still do it nevertheless to prevent false
"unused" symbols warnings and to assist debug info
generation }
redirect_parentfpstruct_local_syms(current_procinfo.procdef);
{ finish the parentfpstruct (add padding, ...) }
finish_parentfpstruct(current_procinfo.procdef);
end;
end; end;
end; end;
@ -2065,7 +2071,7 @@ implementation
end; end;
procedure tcgprocinfo.parse_body; procedure tcgprocinfo.parse_body(get_code_block_func: tcggetcodeblockfunc);
var var
old_current_procinfo : tprocinfo; old_current_procinfo : tprocinfo;
old_block_type : tblock_type; old_block_type : tblock_type;
@ -2149,8 +2155,17 @@ implementation
current_scanner.startrecordtokens(procdef.generictokenbuf); current_scanner.startrecordtokens(procdef.generictokenbuf);
end; end;
{ parse the code ... } if assigned(get_code_block_func) then
code:=block(current_module.islibrary); begin
{ generate the code-nodes }
code:=get_code_block_func(procdef);
init_main_block_syms(code);
end
else
begin
{ parse the code ... }
code:=block(current_module.islibrary);
end;
if recordtokens then if recordtokens then
begin begin
@ -2262,7 +2277,7 @@ implementation
end; end;
procedure read_proc_body(old_current_procinfo:tprocinfo;pd:tprocdef); procedure read_proc_body(old_current_procinfo:tprocinfo;pd:tprocdef;get_code_block_func: tcggetcodeblockfunc=nil);
{ {
Parses the procedure directives, then parses the procedure body, then Parses the procedure directives, then parses the procedure body, then
generates the code for it generates the code for it
@ -2308,7 +2323,7 @@ implementation
tokeninfo^[_FAIL].keyword:=alllanguagemodes; tokeninfo^[_FAIL].keyword:=alllanguagemodes;
end; end;
tcgprocinfo(current_procinfo).parse_body; tcgprocinfo(current_procinfo).parse_body(get_code_block_func);
{ reset _FAIL as _SELF normal } { reset _FAIL as _SELF normal }
if (pd.proctypeoption=potype_constructor) then if (pd.proctypeoption=potype_constructor) then
@ -2344,7 +2359,7 @@ implementation
assigned(current_procinfo.procdef.owner) and assigned(current_procinfo.procdef.owner) and
(current_procinfo.procdef.owner.defowner=current_procinfo.procdef.struct) (current_procinfo.procdef.owner.defowner=current_procinfo.procdef.struct)
) )
) then ) and not(assigned(get_code_block_func)) then
consume(_SEMICOLON); consume(_SEMICOLON);
if not isnestedproc then if not isnestedproc then
@ -2368,7 +2383,7 @@ implementation
end; end;
procedure read_proc(isclassmethod:boolean; usefwpd: tprocdef;isgeneric:boolean); procedure read_proc(isclassmethod:boolean; usefwpd: tprocdef; isgeneric:boolean; get_code_block_func: tcggetcodeblockfunc);
{ {
Parses the procedure directives, then parses the procedure body, then Parses the procedure directives, then parses the procedure body, then
generates the code for it generates the code for it
@ -2527,7 +2542,7 @@ implementation
{ compile procedure when a body is needed } { compile procedure when a body is needed }
if (pd_body in pdflags) then if (pd_body in pdflags) then
begin begin
read_proc_body(old_current_procinfo,pd); read_proc_body(old_current_procinfo,pd, get_code_block_func);
end end
else else
begin begin

View File

@ -677,6 +677,7 @@ implementation
hadgeneric, hadgeneric,
fields_allowed, is_classdef, classfields, threadvarfields: boolean; fields_allowed, is_classdef, classfields, threadvarfields: boolean;
vdoptions: tvar_dec_options; vdoptions: tvar_dec_options;
rtti_attrs_def: trtti_attributesdef;
begin begin
{ empty record declaration ? } { empty record declaration ? }
if (token=_SEMICOLON) then if (token=_SEMICOLON) then
@ -697,6 +698,7 @@ implementation
classfields:=false; classfields:=false;
threadvarfields:=false; threadvarfields:=false;
member_blocktype:=bt_general; member_blocktype:=bt_general;
rtti_attrs_def := nil;
repeat repeat
case token of case token of
_TYPE : _TYPE :
@ -857,7 +859,7 @@ implementation
end; end;
end end
else if member_blocktype=bt_type then else if member_blocktype=bt_type then
types_dec(true,hadgeneric) types_dec(true,hadgeneric, rtti_attrs_def)
else if member_blocktype=bt_const then else if member_blocktype=bt_const then
consts_dec(true,true,hadgeneric) consts_dec(true,true,hadgeneric)
else else
@ -869,7 +871,7 @@ implementation
begin begin
if IsAnonOrLocal then if IsAnonOrLocal then
Message(parser_e_no_properties_in_local_anonymous_records); Message(parser_e_no_properties_in_local_anonymous_records);
struct_property_dec(is_classdef); struct_property_dec(is_classdef, rtti_attrs_def);
fields_allowed:=false; fields_allowed:=false;
is_classdef:=false; is_classdef:=false;
end; end;

View File

@ -465,7 +465,8 @@ type
tsk_field_setter, // Setter for a field (callthrough property is passed in skpara) tsk_field_setter, // Setter for a field (callthrough property is passed in skpara)
tsk_block_invoke_procvar, // Call a procvar to invoke inside a block tsk_block_invoke_procvar, // Call a procvar to invoke inside a block
tsk_interface_wrapper, // Call through to a method from an interface wrapper tsk_interface_wrapper, // Call through to a method from an interface wrapper
tsk_call_no_parameters // Call skpara procedure without passing any parameters nor returning a result tsk_call_no_parameters, // Call skpara procedure without passing any parameters nor returning a result
tsk_get_rttiattribute // Create and return a TCustomAttribute instance
); );
{ synthetic procdef supplementary information (tprocdef.skpara) } { synthetic procdef supplementary information (tprocdef.skpara) }

View File

@ -1024,6 +1024,33 @@ implementation
setverbosity('W+'); setverbosity('W+');
end; end;
function get_attribute_code_block(pd: tprocdef) : tnode;
var
attribute: trtti_attribute;
load: tloadnode;
statement: tstatementnode;
assignment: tassignmentnode;
begin
attribute:=trtti_attribute(pd.skpara);
load := cloadnode.create(pd.funcretsym,pd.funcretsym.Owner);
assignment := cassignmentnode.create(load,Attribute.constructorcall);
assignment.resultdef := voidtype;
statement := cstatementnode.Create(assignment,nil);
result := cblocknode.create(statement);
result.resultdef := voidtype;
end;
procedure implement_get_attribute(pd: tprocdef);
var
old_parse_only: boolean;
begin
old_parse_only:=parse_only;
parse_only:=false;
read_proc(po_classmethod in pd.procoptions,pd,false,@get_attribute_code_block);
parse_only:=old_parse_only;
end;
procedure add_synthetic_method_implementations_for_st(st: tsymtable); procedure add_synthetic_method_implementations_for_st(st: tsymtable);
var var
@ -1115,6 +1142,8 @@ implementation
implement_interface_wrapper(pd); implement_interface_wrapper(pd);
tsk_call_no_parameters: tsk_call_no_parameters:
implement_call_no_parameters(pd); implement_call_no_parameters(pd);
tsk_get_rttiattribute:
implement_get_attribute(pd);
end; end;
end; end;
end; end;

View File

@ -392,6 +392,21 @@ interface
end; end;
pvmtentry = ^tvmtentry; pvmtentry = ^tvmtentry;
{ trtti_attributesdef }
trtti_attribute = class
typesym : tsym;
constructorcall : tnode;
symbolname : string;
end;
trtti_attributesdef = class
rtti_attributes : TFPObjectList;
procedure addattribute(atypesym: tsym; constructorcall: tnode);
destructor destroy; override;
function get_attribute_count: longint;
end;
{ tobjectdef } { tobjectdef }
tvmcallstatic = (vmcs_default, vmcs_yes, vmcs_no, vmcs_unreachable); tvmcallstatic = (vmcs_default, vmcs_yes, vmcs_no, vmcs_unreachable);
@ -438,6 +453,7 @@ interface
} }
classref_created_in_current_module : boolean; classref_created_in_current_module : boolean;
objecttype : tobjecttyp; objecttype : tobjecttyp;
rtti_attributesdef : trtti_attributesdef;
constructor create(ot:tobjecttyp;const n:string;c:tobjectdef;doregister:boolean);virtual; constructor create(ot:tobjecttyp;const n:string;c:tobjectdef;doregister:boolean);virtual;
constructor ppuload(ppufile:tcompilerppufile); constructor ppuload(ppufile:tcompilerppufile);
destructor destroy;override; destructor destroy;override;
@ -2867,6 +2883,36 @@ implementation
GetTypeName:='<enumeration type>'; GetTypeName:='<enumeration type>';
end; end;
{****************************************************************************
TRTTI_ATTRIBUTESDEF
****************************************************************************}
procedure trtti_attributesdef.addattribute(atypesym: tsym; constructorcall: tnode);
var
newattribute: trtti_attribute;
begin
if not assigned(rtti_attributes) then
rtti_attributes := TFPObjectList.Create(True);
newattribute := trtti_attribute.Create;
newattribute.typesym := atypesym;
newattribute.constructorcall:=constructorcall;
rtti_attributes.Add(newattribute);
end;
destructor trtti_attributesdef.destroy;
begin
rtti_attributes.Free;
inherited destroy;
end;
function trtti_attributesdef.get_attribute_count: longint;
begin
if assigned(rtti_attributes) then
result := rtti_attributes.Count
else
result := 0;
end;
{**************************************************************************** {****************************************************************************
TORDDEF TORDDEF
@ -6979,6 +7025,11 @@ implementation
freemem(vmcallstaticinfo); freemem(vmcallstaticinfo);
vmcallstaticinfo:=nil; vmcallstaticinfo:=nil;
end; end;
if assigned(rtti_attributesdef) then
begin
rtti_attributesdef.Free;
rtti_attributesdef:=nil;
end;
inherited destroy; inherited destroy;
end; end;

View File

@ -352,6 +352,7 @@ interface
dispid : longint; dispid : longint;
propaccesslist: array[tpropaccesslisttypes] of tpropaccesslist; propaccesslist: array[tpropaccesslisttypes] of tpropaccesslist;
parast : tsymtable; parast : tsymtable;
rtti_attributesdef : trtti_attributesdef;
constructor create(const n : string);virtual; constructor create(const n : string);virtual;
destructor destroy;override; destructor destroy;override;
constructor ppuload(ppufile:tcompilerppufile); constructor ppuload(ppufile:tcompilerppufile);
@ -1375,6 +1376,7 @@ implementation
for pap:=low(tpropaccesslisttypes) to high(tpropaccesslisttypes) do for pap:=low(tpropaccesslisttypes) to high(tpropaccesslisttypes) do
propaccesslist[pap].free; propaccesslist[pap].free;
parast.free; parast.free;
rtti_attributesdef.free;
inherited destroy; inherited destroy;
end; end;

View File

@ -955,10 +955,12 @@ implementation
if (vo_is_funcret in tabstractvarsym(sym).varoptions) then if (vo_is_funcret in tabstractvarsym(sym).varoptions) then
begin begin
{ don't warn about the result of constructors } { don't warn about the result of constructors }
{ or the synthetic helper functions for class-attributes }
if ((tsym(sym).owner.symtabletype<>localsymtable) or if ((tsym(sym).owner.symtabletype<>localsymtable) or
(tprocdef(tsym(sym).owner.defowner).proctypeoption<>potype_constructor)) and (tprocdef(tsym(sym).owner.defowner).proctypeoption<>potype_constructor)) and
not (po_noreturn in tprocdef(tsym(sym).owner.defowner).procoptions) and not (po_noreturn in tprocdef(tsym(sym).owner.defowner).procoptions) and
not(cs_opt_nodedfa in current_settings.optimizerswitches) then not(cs_opt_nodedfa in current_settings.optimizerswitches) and
(tprocdef(tsym(sym).owner.defowner).synthetickind <> tsk_get_rttiattribute) then
MessagePos(tsym(sym).fileinfo,sym_w_function_result_not_set) MessagePos(tsym(sym).fileinfo,sym_w_function_result_not_set)
end end
else if (tsym(sym).owner.symtabletype=parasymtable) then else if (tsym(sym).owner.symtabletype=parasymtable) then

View File

@ -191,6 +191,7 @@ type
protected protected
function GetHandle: Pointer; virtual; abstract; function GetHandle: Pointer; virtual; abstract;
public public
function GetAttributes: specialize TArray<TCustomAttribute>; virtual; abstract;
property Handle: Pointer read GetHandle; property Handle: Pointer read GetHandle;
end; end;
@ -208,6 +209,8 @@ type
TRttiType = class(TRttiNamedObject) TRttiType = class(TRttiNamedObject)
private private
FTypeInfo: PTypeInfo; FTypeInfo: PTypeInfo;
FAttributesResolved: boolean;
FAttributes: specialize TArray<TCustomAttribute>;
FMethods: specialize TArray<TRttiMethod>; FMethods: specialize TArray<TRttiMethod>;
function GetAsInstance: TRttiInstanceType; function GetAsInstance: TRttiInstanceType;
protected protected
@ -224,6 +227,7 @@ type
function GetBaseType: TRttiType; virtual; function GetBaseType: TRttiType; virtual;
public public
constructor Create(ATypeInfo : PTypeInfo); constructor Create(ATypeInfo : PTypeInfo);
function GetAttributes: specialize TArray<TCustomAttribute>; override;
function GetProperties: specialize TArray<TRttiProperty>; virtual; function GetProperties: specialize TArray<TRttiProperty>; virtual;
function GetProperty(const AName: string): TRttiProperty; virtual; function GetProperty(const AName: string): TRttiProperty; virtual;
function GetMethods: specialize TArray<TRttiMethod>; virtual; function GetMethods: specialize TArray<TRttiMethod>; virtual;
@ -288,6 +292,8 @@ type
TRttiProperty = class(TRttiMember) TRttiProperty = class(TRttiMember)
private private
FPropInfo: PPropInfo; FPropInfo: PPropInfo;
FAttributesResolved: boolean;
FAttributes: specialize TArray<TCustomAttribute>;
function GetPropertyType: TRttiType; function GetPropertyType: TRttiType;
function GetIsWritable: boolean; function GetIsWritable: boolean;
function GetIsReadable: boolean; function GetIsReadable: boolean;
@ -297,6 +303,7 @@ type
function GetHandle: Pointer; override; function GetHandle: Pointer; override;
public public
constructor Create(AParent: TRttiType; APropInfo: PPropInfo); constructor Create(AParent: TRttiType; APropInfo: PPropInfo);
function GetAttributes: specialize TArray<TCustomAttribute>; override;
function GetValue(Instance: pointer): TValue; function GetValue(Instance: pointer): TValue;
procedure SetValue(Instance: pointer; const AValue: TValue); procedure SetValue(Instance: pointer; const AValue: TValue);
property PropertyType: TRttiType read GetPropertyType; property PropertyType: TRttiType read GetPropertyType;
@ -3388,6 +3395,22 @@ begin
FPropInfo := APropInfo; FPropInfo := APropInfo;
end; end;
function TRttiProperty.GetAttributes: specialize TArray<TCustomAttribute>;
var
i: Integer;
begin
if not FAttributesResolved then
begin
setlength(FAttributes,FPropInfo^.AttributeCount);
for i := 0 to FPropInfo^.AttributeCount-1 do
begin
FAttributes[i]:=TCustomAttribute(GetPropAttribute(FPropInfo,i));
end;
FAttributesResolved:=true;
end;
result := FAttributes;
end;
function TRttiProperty.GetValue(Instance: pointer): TValue; function TRttiProperty.GetValue(Instance: pointer): TValue;
procedure ValueFromBool(value: Int64); procedure ValueFromBool(value: Int64);
@ -3600,6 +3623,22 @@ begin
FTypeData:=GetTypeData(ATypeInfo); FTypeData:=GetTypeData(ATypeInfo);
end; end;
function TRttiType.GetAttributes: specialize TArray<TCustomAttribute>;
var
i: Integer;
ad: PAttributeData;
begin
if not FAttributesResolved then
begin
ad := GetAttributeData(FTypeInfo);
setlength(FAttributes,ad^.AttributeCount);
for i := 0 to ad^.AttributeCount-1 do
FAttributes[i]:=GetAttribute(ad,i);
FAttributesResolved:=true;
end;
result := FAttributes;
end;
function TRttiType.GetProperties: specialize TArray<TRttiProperty>; function TRttiType.GetProperties: specialize TArray<TRttiProperty>;
begin begin
Result := Nil; Result := Nil;

View File

@ -979,11 +979,15 @@
class function TObject.UnitName : {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS}; class function TObject.UnitName : {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS};
type type
// from the typinfo unit // from the typinfo unit
TUnitInfo = packed record
UnitOptions: byte;
UnitName: shortstring;
end;
TClassTypeInfo = {$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record TClassTypeInfo = {$ifndef FPC_REQUIRES_PROPER_ALIGNMENT}packed{$endif}record
ClassType: TClass; ClassType: TClass;
ParentInfo: Pointer; ParentInfo: Pointer;
PropCount: SmallInt; PropCount: SmallInt;
UnitName: ShortString; UnitInfo: ^TUnitInfo;
end; end;
PClassTypeInfo = ^TClassTypeInfo; PClassTypeInfo = ^TClassTypeInfo;
var var
@ -997,7 +1001,7 @@
{$ifdef FPC_REQUIRES_PROPER_ALIGNMENT} {$ifdef FPC_REQUIRES_PROPER_ALIGNMENT}
classtypeinfo:=aligntoqword(classtypeinfo); classtypeinfo:=aligntoqword(classtypeinfo);
{$endif} {$endif}
result:=classtypeinfo^.UnitName; result:=classtypeinfo^.UnitInfo^.UnitName;
end end
else else
result:=''; result:='';

View File

@ -428,6 +428,9 @@
{$endif FPC_USE_PSABIEH} {$endif FPC_USE_PSABIEH}
end; end;
TCustomAttribute = class(TObject)
end;
Const Const
ExceptProc : TExceptProc = Nil; ExceptProc : TExceptProc = Nil;
RaiseProc : TExceptProc = Nil; RaiseProc : TExceptProc = Nil;

View File

@ -226,7 +226,16 @@ unit TypInfo;
property Field[aIndex: Word]: PVmtFieldEntry read GetField; property Field[aIndex: Word]: PVmtFieldEntry read GetField;
end; end;
TRTTIUnitOption = (rmoHasAttributes);
TRTTIUnitOptions = set of TRTTIUnitOption;
{$PACKRECORDS 1} {$PACKRECORDS 1}
PUnitInfo = ^TUnitInfo;
TUnitInfo = packed record
UnitOptions: TRTTIUnitOptions;
UnitName: shortstring;
end;
TTypeInfo = record TTypeInfo = record
Kind : TTypeKind; Kind : TTypeKind;
Name : ShortString; Name : ShortString;
@ -562,6 +571,7 @@ unit TypInfo;
{ tkPointer } { tkPointer }
property RefType: PTypeInfo read GetRefType; property RefType: PTypeInfo read GetRefType;
public public
function UnitName: string;
case TTypeKind of case TTypeKind of
tkUnKnown,tkLString,tkWString,tkVariant,tkUString: tkUnKnown,tkLString,tkWString,tkVariant,tkUString:
(); ();
@ -608,7 +618,8 @@ unit TypInfo;
(ClassType : TClass; (ClassType : TClass;
ParentInfoRef : TypeInfoPtr; ParentInfoRef : TypeInfoPtr;
PropCount : SmallInt; PropCount : SmallInt;
UnitName : ShortString UnitInfo : PUnitInfo
// AttributeData: TAttributeData;
// here the properties follow as array of TPropInfo // here the properties follow as array of TPropInfo
); );
tkRecord: tkRecord:
@ -726,6 +737,7 @@ unit TypInfo;
// 6 : true, constant index property // 6 : true, constant index property
PropProcs : Byte; PropProcs : Byte;
AttributeCount : Byte;
Name : ShortString; Name : ShortString;
property PropType: PTypeInfo read GetPropType; property PropType: PTypeInfo read GetPropType;
property Tail: Pointer read GetTail; property Tail: Pointer read GetTail;
@ -734,9 +746,25 @@ unit TypInfo;
TProcInfoProc = Procedure(PropInfo : PPropInfo) of object; TProcInfoProc = Procedure(PropInfo : PPropInfo) of object;
TAttributeProc = function : TCustomAttribute;
PAttributeProcList = ^TAttributeProcList;
TAttributeProcList = array[0..$ffff] of TAttributeProc;
PPropList = ^TPropList; PPropList = ^TPropList;
TPropList = array[0..{$ifdef cpu16}(32768 div sizeof(PPropInfo))-2{$else}65535{$endif}] of PPropInfo; TPropList = array[0..{$ifdef cpu16}(32768 div sizeof(PPropInfo))-2{$else}65535{$endif}] of PPropInfo;
TAttributeData = record
AttributeCount: word;
AttributesList: TAttributeProcList;
end;
PAttributeData = ^TAttributeData;
PUnitInfoList = ^TUnitInfoList;
TUnitInfoList = record
UnitCount: IntPtr;
Units: array[0..65535] of PUnitInfo;
end;
const const
tkString = tkSString; tkString = tkSString;
tkProcedure = tkProcVar; // for compatibility with Delphi tkProcedure = tkProcVar; // for compatibility with Delphi
@ -873,6 +901,18 @@ function GetDynArrayProp(Instance: TObject; PropInfo: PPropInfo): Pointer;
procedure SetDynArrayProp(Instance: TObject; const PropName: string; const Value: Pointer); procedure SetDynArrayProp(Instance: TObject; const PropName: string; const Value: Pointer);
procedure SetDynArrayProp(Instance: TObject; PropInfo: PPropInfo; const Value: Pointer); procedure SetDynArrayProp(Instance: TObject; PropInfo: PPropInfo; const Value: Pointer);
// Extended RTTI
function GetUnitList: PUnitInfoList;
function GetFirstTypeinfoFromUnit(AUnitInfo: PUnitInfo): PTypeInfo;
function GetNextTypeInfo(ATypeInfo: PTypeInfo): PTypeInfo;
function GetAttributeData(TypeInfo: PTypeInfo): PAttributeData;
function GetPropAttributeProclist(PropInfo: PPropInfo): PAttributeProcList;
function GetPropAttribute(PropInfo: PPropInfo; AttributeNr: byte): TObject;
function GetAttribute(AttributeData: PAttributeData; AttributeNr: byte): TCustomAttribute;
// Auxiliary routines, which may be useful // Auxiliary routines, which may be useful
Function GetEnumName(TypeInfo : PTypeInfo;Value : Integer) : string; Function GetEnumName(TypeInfo : PTypeInfo;Value : Integer) : string;
Function GetEnumValue(TypeInfo : PTypeInfo;const Name : string) : Integer; Function GetEnumValue(TypeInfo : PTypeInfo;const Name : string) : Integer;
@ -920,6 +960,15 @@ uses rtlconsts;
type type
PMethod = ^TMethod; PMethod = ^TMethod;
{ ---------------------------------------------------------------------
TTypeData methods
---------------------------------------------------------------------}
function TTypeData.UnitName: string;
begin
Result := UnitInfo^.UnitName
end;
{ --------------------------------------------------------------------- { ---------------------------------------------------------------------
Auxiliary methods Auxiliary methods
---------------------------------------------------------------------} ---------------------------------------------------------------------}
@ -950,6 +999,187 @@ begin
{$endif} {$endif}
end; end;
{$ifdef FPC_HAS_UNIT_RTTI}
var
UnitList: TUnitInfoList; external name 'RTTIUNITLIST';
{$endif FPC_HAS_UNIT_RTTI}
function GetUnitList: PUnitInfoList;
begin
{$ifdef FPC_HAS_UNIT_RTTI}
result := @UnitList;
{$else FPC_HAS_UNIT_RTTI}
result := nil;
{$endif FPC_HAS_UNIT_RTTI}
end;
function GetAttributeData(TypeInfo: PTypeInfo): PAttributeData;
var
TD: PTypeData;
begin
if TypeInfo^.Kind<>tkClass then
result := nil
else
begin
TD := GetTypeData(TypeInfo);
if (rmoHasAttributes in td^.UnitInfo^.UnitOptions) then
Result:=PAttributeData(aligntoptr(pointer(@TD^.UnitInfo)+sizeof(TD^.UnitInfo)))
else
result := nil;
end;
end;
function GetPropData(TypeInfo : PTypeInfo; TypeData: PTypeData) : PPropData;
var
AD: PAttributeData;
begin
if rmoHasAttributes in TypeData^.UnitInfo^.UnitOptions then
begin
AD := GetAttributeData(TypeInfo);
result := PPropData(pointer(AD)+SizeOf(AD^.AttributeCount)+(AD^.AttributeCount*SizeOf(TAttributeProc)));
end
else
result := aligntoptr(pointer(@TypeData^.UnitInfo)+sizeof(TypeData^.UnitInfo));
end;
function GetFirstTypeinfoFromUnit(AUnitInfo: PUnitInfo): PTypeInfo;
begin
result := align(pointer(@AUnitInfo^.UnitName)+1+byte(AUnitInfo^.UnitName[0]), sizeof(Pointer));
end;
function GetNextTypeInfo(ATypeInfo: PTypeInfo): PTypeInfo;
type
TEnumTableMode=(lookup,search);
var
p: pointer;
td: PTypeData;
pd: ppropdata;
i: longint;
fc: longint;
minv,maxv: longint;
EnumTableMode: TEnumTableMode;
count: pword;
begin
td := GetTypeData(ATypeInfo);
p := GetTypeData(ATypeInfo);
case ATypeInfo^.Kind of
tkEnumeration:
begin
p := aligntoptr(p + 1); { OrdType }
minv := PLongInt(p)^;
p := p + SizeOf(LongInt); { MinValue }
maxv := PLongInt(p)^;
p := p + SizeOf(LongInt); { MaxValue }
p := p + SizeOf(PTypeInfo); { basetype }
for i := minv to maxv do
p := p + 1 + pbyte(p)^; { NameList: shortstring length + length of string }
p := p + 1 + pbyte(p)^; { UnitName: shortstring length + length of string }
p := p + 1; { trailing zero }
end;
tkInteger,
tkChar,
tkWChar,
tkBool : begin
p := aligntoptr(p + 1); { OrdType }
p := p + SizeOf(LongInt) + SizeOf(LongInt); { MinValue + MaxValue }
end;
tkSet : begin
p := aligntoptr(p + 1); { OrdType }
p := p + sizeof(PTypeInfo); { CompType }
end;
tkQWord : p := p + SizeOf(QWord) + SizeOf(QWord); { MinQWordValue, MaxQWordValue }
tkInt64 : p := p + SizeOf(Int64) + SizeOf(Int64); { MinInt64Value, MaxInt64Value }
tkSString: p := P + SizeOf(Byte); { MaxLength }
tkArray : begin
p := p + sizeof(Ptrint); { Element size }
p := p + sizeof(PtrInt); { Element count }
p := p + sizeof(pointer); { Element type }
p := p + sizeof(longint); { Variant type }
end;
tkDynArray:begin
p := p + sizeof(Ptrint); { Element size }
p := p + sizeof(PtrInt); { Element type 2 }
p := p + sizeof(longint); { Variant type }
p := p + sizeof(pointer); { Element type }
p := p + 1 + pbyte(p)^; { unitname: shortstring length + length of string }
end;
tkFloat : begin
p := p + sizeof(TFloatType); { Float type }
end;
tkObject,
tkRecord : begin
p := p + 4; { Size }
fc := plongint(p)^;
p := p + 4; { Fieldcount }
p := p + (fc * (sizeof(pointer) + 4)); { Fieldcount * (element type + field offset) }
end;
tkClass : begin
pd := GetPropData(ATypeInfo,td);
p:=@pd^.PropList;
for i:=1 to pd^.PropCount do
p:=aligntoptr(pointer(@ppropinfo(p)^.Name)+byte(ppropinfo(p)^.Name[0])+(ppropinfo(p)^.AttributeCount*SizeOf(TAttributeProc))+1);
end;
tkInterface :
begin
p := aligntoptr(pointer(@td^.IntfUnit)+byte(td^.IntfUnit[0])+1);
p := p+pbyte(p)^+1; { IIDStr }
end;
tkMethod : begin
p := @td^.ParamList[0];
for i := 0 to td^.ParamCount-1 do
begin
p := aligntoptr(p + sizeof(TParamFlags)); { TParamFlags }
p := aligntoptr(p +pbyte(p)^+1); { paramname }
p := aligntoptr(p +pbyte(p)^+1); { typename }
end;
if td^.MethodKind in [mkFunction, mkClassFunction] then
begin
p := aligntoptr(p +pbyte(p)^+1); { resulttype }
p := p + sizeof(PPTypeInfo); { resulttyperef }
end;
p := aligntoptr(p + sizeof(TCallConv)); { cc }
p := p + (td^.ParamCount * sizeof(PPTypeInfo));
end;
end;
result := PTypeInfo(align(p,sizeof(p)));
if PByte(result)^=255 then
result := nil;
end;
function GetPropAttributeProclist(PropInfo: PPropInfo): PAttributeProcList;
begin
if PropInfo^.AttributeCount=0 then
result := nil
else
begin
Result:=PAttributeProcList(aligntoptr(pointer(@PropInfo^.Name)+byte(PropInfo^.Name[0])+1));
end;
end;
function GetPropAttribute(PropInfo: PPropInfo; AttributeNr: byte): TObject;
var
AttributeProcList: PAttributeProcList;
begin
if AttributeNr>=PropInfo^.AttributeCount then
result := nil
else
begin
AttributeProcList := GetPropAttributeProclist(PropInfo);
result := AttributeProcList^[AttributeNr]();
end;
end;
function GetAttribute(AttributeData: PAttributeData; AttributeNr: byte): TCustomAttribute;
var
AttributeProcList: TAttributeProcList;
begin
if (AttributeData=nil) or (AttributeNr>=AttributeData^.AttributeCount) then
result := nil
else
begin
result := AttributeData^.AttributesList[AttributeNr]();
end;
end;
Function GetEnumName(TypeInfo : PTypeInfo;Value : Integer) : string; Function GetEnumName(TypeInfo : PTypeInfo;Value : Integer) : string;
@ -1241,7 +1471,7 @@ var
hp : PTypeData; hp : PTypeData;
i : longint; i : longint;
p : shortstring; p : shortstring;
pd : ^TPropData; pd : PPropData;
begin begin
P:=PropName; // avoid Ansi<->short conversion in a loop P:=PropName; // avoid Ansi<->short conversion in a loop
while Assigned(TypeInfo) do while Assigned(TypeInfo) do
@ -1249,7 +1479,7 @@ begin
// skip the name // skip the name
hp:=GetTypeData(Typeinfo); hp:=GetTypeData(Typeinfo);
// the class info rtti the property rtti follows immediatly // the class info rtti the property rtti follows immediatly
pd:=aligntoptr(pointer(pointer(@hp^.UnitName)+Length(hp^.UnitName)+1)); pd := GetPropData(TypeInfo,hp);
Result:=PPropInfo(@pd^.PropList); Result:=PPropInfo(@pd^.PropList);
for i:=1 to pd^.PropCount do for i:=1 to pd^.PropCount do
begin begin
@ -1257,7 +1487,7 @@ begin
if ShortCompareText(Result^.Name, P) = 0 then if ShortCompareText(Result^.Name, P) = 0 then
exit; exit;
// skip to next property // skip to next property
Result:=PPropInfo(aligntoptr(pointer(@Result^.Name)+byte(Result^.Name[0])+1)); Result:=PPropInfo(aligntoptr(pointer(@Result^.Name)+byte(Result^.Name[0])+(result^.AttributeCount*SizeOf(TAttributeProc))+1));
end; end;
// parent class // parent class
Typeinfo:=hp^.ParentInfo; Typeinfo:=hp^.ParentInfo;
@ -1408,7 +1638,7 @@ begin
repeat repeat
TD:=GetTypeData(TypeInfo); TD:=GetTypeData(TypeInfo);
// published properties count for this object // published properties count for this object
TP:=aligntoptr(PPropInfo(aligntoptr((Pointer(@TD^.UnitName)+Length(TD^.UnitName)+1)))); TP:=PPropInfo(GetPropData(TypeInfo, TD));
Count:=PWord(TP)^; Count:=PWord(TP)^;
// Now point TP to first propinfo record. // Now point TP to first propinfo record.
Inc(Pointer(TP),SizeOF(Word)); Inc(Pointer(TP),SizeOF(Word));
@ -1420,7 +1650,7 @@ begin
PropList^[TP^.NameIndex]:=TP; PropList^[TP^.NameIndex]:=TP;
// Point to TP next propinfo record. // Point to TP next propinfo record.
// Located at Name[Length(Name)+1] ! // Located at Name[Length(Name)+1] !
TP:=aligntoptr(PPropInfo(pointer(@TP^.Name)+PByte(@TP^.Name)^+1)); TP:=aligntoptr(PPropInfo(pointer(@TP^.Name)+PByte(@TP^.Name)^+(TP^.AttributeCount*SizeOf(TAttributeProc))+1));
Dec(Count); Dec(Count);
end; end;
TypeInfo:=TD^.Parentinfo; TypeInfo:=TD^.Parentinfo;

View File

@ -0,0 +1,43 @@
program tclassattribute1;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
uses
typinfo;
type
{ tmyt }
tmyt = class(TCustomAttribute)
constructor create;
end;
type
[Tmyt]
TMyObject = class(TObject)
end;
var
ad: PAttributeData;
AClassAttribute: TCustomAttribute;
{ tmyt }
constructor tmyt.create;
begin
//
end;
begin
ad := GetAttributeData(TMyObject.ClassInfo);
if ad^.AttributeCount<>1 then
halt(1);
AClassAttribute := GetAttribute(ad,0);
if AClassAttribute = nil then
halt(2);
writeln('ok');
end.

View File

@ -0,0 +1,34 @@
program tclassattribute10;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
uses
typinfo;
type
{ TMyAttr }
TMyAttrAttribute = class(TCustomAttribute)
end;
type
// The attribute should be also accessable without the Attribute suffix.
[TMyAttr]
TMyObject = class(TObject)
end;
var
ad: PAttributeData;
AClassAttribute: TCustomAttribute;
begin
ad := GetAttributeData(TMyObject.ClassInfo);
if ad^.AttributeCount<>1 then
halt(1);
AClassAttribute := GetAttribute(ad,0);
if AClassAttribute = nil then
halt(2);
writeln('ok');
end.

View File

@ -0,0 +1,16 @@
{ %fail }
program tclassattribute2;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
type
// Delphi XE does compile attributes that are not defined, but ignores them.
// That's clearly a Delphi-bug, so fpc should fail on the following:
[TMyAttributeDoesNotExist]
TMyObject = class(TObject)
end;
begin
end.

View File

@ -0,0 +1,22 @@
{ %fail }
program tclassattribute3;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
type
{ tmyt }
tmyt = class
constructor create;
end;
// tmyt is not a TCustomAttribute, so this should fail.
[tmyt]
TMyObject = class(TObject)
end;
begin
end.

View File

@ -0,0 +1,55 @@
program tclassattribute4;
{$mode delphi}
uses
typinfo;
type
{ tmyt }
tmyt = class(TCustomAttribute)
private
FID: integer;
public
constructor create(Id: integer);
end;
type
[Tmyt(924)]
[Tmyt(1425)]
TMyObject = class(TObject)
end;
var
rtd: PAttributeData;
AClassAttribute: tmyt;
{ tmyt }
constructor tmyt.create(Id: integer);
begin
Fid := Id;
end;
begin
rtd := GetAttributeData(TMyObject.ClassInfo);
if rtd^.AttributeCount<>2 then
halt(1);
AClassAttribute := GetAttribute(rtd,1) as tmyt;
if AClassAttribute = nil then
halt(2);
if AClassAttribute.FID<>1425 then
halt(3);
AClassAttribute := GetAttribute(rtd,0) as tmyt;
if AClassAttribute = nil then
halt(2);
if AClassAttribute.FID<>924 then
halt(3);
writeln('ok');
end.

View File

@ -0,0 +1,30 @@
{ %fail }
program tclassattribute5;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
uses
typinfo;
type
{ tmyt }
tmyt = class(TCustomAttribute)
private
FID: integer;
public
constructor create(Id: integer);
end;
type
// Delphi XE does compile attributes with invalid parameters.
// That's clearly a Delphi-bug, so fpc should fail on the following:
[Tmyt(924,32)]
TMyObject = class(TObject)
end;
begin
end.

View File

@ -0,0 +1,51 @@
program tclassattribute6;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
uses
typinfo;
type
{ tmyt }
TMyt = class(TCustomAttribute)
constructor create;
end;
type
{ TMyObject }
TMyObject = class(TObject)
private
FInt: integer;
published
[TMyt]
property PublicInt: integer read FInt;
end;
constructor TMyt.create;
begin
end;
var
pi: PPropInfo;
AClassAttribute: TCustomAttribute;
begin
pi := GetPropInfo(TMyObject.ClassInfo,'PublicInt');
if pi^.AttributeCount<>1 then
halt(1);
AClassAttribute := GetPropAttribute(pi,0) as TCustomAttribute;
if AClassAttribute = nil then
halt(2);
writeln('ok');
end.

View File

@ -0,0 +1,39 @@
{ %fail }
program tclassattribute7;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
uses
typinfo;
type
{ tmyt }
TMyt = class(TCustomAttribute)
constructor create;
end;
type
{ TMyObject }
TMyObject = class(TObject)
private
FInt: integer;
published
// Should fail because there is nothing to bind the custom attribute to.
[TMyt]
end;
constructor TMyt.create;
begin
//
end;
begin
//
end.

View File

@ -0,0 +1,37 @@
{ %fail }
program tclassattribute8;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
uses
typinfo;
type
{ tmyt }
TMyt = class(TCustomAttribute)
constructor create;
end;
type
{ TMyObject }
[TMyt]
TMyObject = class(TObject)
end;
// Attributes for integers are not allowed, so the following should fail, since
// there is nothing to bind the attribute to.
[TMyt]
int = integer;
constructor TMyt.create;
begin
end;
begin
end.

View File

@ -0,0 +1,33 @@
program tclassattribute9;
{$mode objfpc}{$H+}
{$modeswitch prefixedattributes}
uses
typinfo;
type
{ tmyt }
// TCustomAttribute without constructor
tmyt = class(TCustomAttribute);
type
[Tmyt]
TMyObject = class(TObject)
end;
var
ad: PAttributeData;
AClassAttribute: TCustomAttribute;
begin
ad := GetAttributeData(TMyObject.ClassInfo);
if ad^.AttributeCount<>1 then
halt(1);
AClassAttribute := GetAttribute(ad,0);
if AClassAttribute = nil then
halt(2);
writeln('ok');
end.