On 17 May 2012, at 23:32, Joost van der Sluis wrote:

>> I'm trying to implement extended RTTI property attributes as
>> supported by recent Delphi versions.

>>Please add this functionality first to a separate branch for easier
>>reviewing.

I could, but I attached a patch first. Before I commit this to a branch
I want to know that I'm on the right track. Could you and/or others have
a look at the patch?

I'm especially interested if things are done in the right units, and if
the code in ptypes.pas is acceptable.

Attached are the patch and a simple test-program. 

First an example what class-attributes are:

type
  TMyObject = class
  published
    [TAttributeClassName('param1','param2')]
    property AProperty;
  end;

When you request the class-property that belongs to the property
AProperty, then you get an instance of the TAttributeClassName class
which is constructed by calling it's constructor with the supplied
parameters. (In this case 'param1' and 'param2')

How I implemented this:
For each attribute a function is generated that returns a class-instance
of the attribute. This function is generated in ptypes by creating a
tcgprocinfo. All the necessary nodes are added to it and then
tcgprocinfo.generate_code is executed.

In the rtti-info (ppropinfo) the address of this function is stored so
that it is possible to read those attributes.

If I'm on the right track I'll add a branch with this patch and work
from there. Some issues:

- In Delphi an attribute-class has to be defined as a TCustomAttribute.
That way it's also clear which constructor has to be called. For now, it
calls the first constructor it can find.

- The rtti-info is now stored inside PPropInfo, I don't think that this
is how Delphi does it, also because in Delphi you need the 'rtti' unit
to get this information. I've added it to typinfo and later on a 'rtti'
unit could be written as a wrapper around typinfo.

- When we add more rtti-information to types we need a way to specify
what to generate and what not and where to store it. Especially because
the rtti-info will grow. 

- No error is generated when you define an attribute for something else
then a property.

- The attribute-definition is stored in the .ppu, but that's not really
needed. Since at compile-time this information is never used by another
unit. I used it for debugging though (with ppudump) A side-effect is
that when an attribute is changed, a recompilation of the unit is
forced. Do we want that? (For now only the attribute-type is stored in
the ppu, not the parameters)

Joost.







-- 
Met vriendelijke groeten,

  Joost van der Sluis
  CNOC Informatiesystemen en Netwerken
  http://www.cnoc.nl
Index: compiler/ptype.pas
===================================================================
--- compiler/ptype.pas	(revision 21375)
+++ compiler/ptype.pas	(working copy)
@@ -60,11 +60,14 @@
     { parse hint directives (platform, deprecated, ...) for a procdef }
     procedure maybe_parse_hint_directives(pd:tprocdef);
 
+    { generate hidden RTTI attribute functions which return the attribute classes }
+    procedure generate_rttiattribute_procs;
+
 implementation
 
     uses
        { common }
-       cutils,
+       cutils,sysutils,pparautl,
        { global }
        globals,tokens,verbose,constexp,
        systems,
@@ -80,10 +83,11 @@
        fmodule,
        { pass 1 }
        node,ncgrtti,nobj,
-       nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,
+       nmat,nadd,ncal,nset,ncnv,ninl,ncon,nld,nflw,nmem,nbas,
+       ncgutil,
        { parser }
        scanner,
-       pbase,pexpr,pdecsub,pdecvar,pdecobj,pdecl,pgenutil
+       pbase,pexpr,pdecsub,pdecvar,pdecobj,pdecl,pgenutil,psub
 {$ifdef jvm}
        ,pjvm
 {$endif}
@@ -1618,7 +1622,138 @@
         read_named_type(def,nil,nil,nil,parseprocvardir);
       end;
 
+    procedure generate_rttiattribute_proc(APropertySym: tpropertysym; AnAttribute: trtti_attribute; attribute_index: longint);
+    var
+      pd: tprocdef;
+      aprocsym: tprocsym;
+      st:TSymtable;
+      checkstack : psymtablestackitem;
+      old_current_procinfo : tprocinfo;
+      old_current_structdef: tabstractrecorddef;
+      old_current_genericdef,
+      old_current_specializedef: tstoreddef;
+      block: tblocknode;
+      load: tloadnode;
+      statement: tstatementnode;
+      call: tcallnode;
+      assignment: tassignmentnode;
+      constrdef: tprocdef;
+      vmtaddrnode: tloadvmtaddrnode;
+    begin
+      aprocsym:=tprocsym.create(make_mangledname('_RTTI_ATTRIBUTE_',symtablestack.top,APropertySym.Name+'_'+IntToStr(attribute_index)));
+      symtablestack.top.insert(aprocsym);
+      aprocsym.visibility:=vis_hidden;
 
+      st:=nil;
+      checkstack:=symtablestack.stack;
+      while assigned(checkstack) do
+        begin
+          st:=checkstack^.symtable;
+          if st.symtabletype in [staticsymtable,globalsymtable,localsymtable] then
+            break;
+          checkstack:=checkstack^.next;
+        end;
+      pd:=tprocdef.create(st.symtablelevel+1);
+      pd.procsym:=aprocsym;
+      pd.proctypeoption:=potype_procedure;
+      pd.visibility:=vis_hidden;
+      pd.fileinfo:=current_tokenpos;
+
+      pd.calcparas;
+
+      { save old state }
+      old_current_procinfo:=current_procinfo;
+      old_current_structdef:=current_structdef;
+      old_current_genericdef:=current_genericdef;
+      old_current_specializedef:=current_specializedef;
+
+      { reset current_procinfo.procdef to nil to be sure that nothing is writing
+        to another procdef }
+      current_procinfo:=nil;
+      current_structdef:=nil;
+      current_genericdef:=nil;
+      current_specializedef:=nil;
+
+      proc_set_mangledname(pd);
+
+      { store the mangledname so that the rtti-info can reference to it }
+      AnAttribute.symbolname:=pd.mangledname;
+
+      { allocate the symbol for this procedure }
+      alloc_proc_symbol(pd);
+
+      current_procinfo:=cprocinfo.create(old_current_procinfo);
+      current_procinfo.procdef:=pd;
+
+      { Insert mangledname }
+      pd.aliasnames.insert(pd.mangledname);
+
+      pd.returndef := ttypesym(AnAttribute.typesym).typedef;
+      insert_funcret_local(pd);
+
+      { Let the proc call the first constructor of the class that can be found }
+      constrdef := tobjectdef(pd.returndef).find_procdef_bytype(potype_constructor);
+
+      load := cloadnode.create(pd.funcretsym,pd.funcretsym.Owner);
+      vmtaddrnode := cloadvmtaddrnode.create(ctypenode.create(pd.returndef));
+      call := ccallnode.create(tcallparanode(AnAttribute.paras),tprocsym(constrdef.procsym), tprocsym(constrdef.procsym).Owner,vmtaddrnode,[]);
+      assignment := cassignmentnode.create(load,call);
+      assignment.resultdef := voidtype;
+
+      statement := cstatementnode.Create(assignment,nil);
+      block := cblocknode.create(statement);
+      block.resultdef := voidtype;
+      tcgprocinfo(current_procinfo).code := block;
+
+      if paraprintnodetree=1 then
+        tcgprocinfo(current_procinfo).printproc( 'after parsing');
+
+      tcgprocinfo(current_procinfo).generate_code;
+
+      current_procinfo.free;
+
+      current_structdef:=old_current_structdef;
+      current_genericdef:=old_current_genericdef;
+      current_specializedef:=old_current_specializedef;
+      current_procinfo:=old_current_procinfo;
+    end;
+
+    procedure generate_rttiattribute_procs_properties(p: TObject; Arg: pointer);
+      var
+        rtti_attributesdef: trtti_attributesdef;
+        i: longint;
+      begin
+         if not((tsym(p).typ=propertysym)
+               ) then
+           exit;
+
+         rtti_attributesdef := tpropertysym(p).rtti_attributesdef;
+         if assigned(rtti_attributesdef) then
+           begin
+             for i := 0 to rtti_attributesdef.get_attribute_count-1 do
+               begin
+                 generate_rttiattribute_proc(tpropertysym(p), trtti_attribute(rtti_attributesdef.rtti_attributes[i]),i);
+               end;
+           end;
+      end;
+
+    procedure generate_rttiattribute_procs_classes(p: TObject; Arg: pointer);
+      begin
+        if not((tsym(p).typ=typesym) and
+               (ttypesym(p).typedef.typ in [objectdef])
+              ) then
+          exit;
+        tobjectdef(ttypesym(p).typedef).symtable.SymList.ForEachCall(@generate_rttiattribute_procs_properties,nil);
+      end;
+
+    procedure generate_rttiattribute_procs;
+      begin
+        if assigned(current_module.globalsymtable) then
+          current_module.globalsymtable.SymList.ForEachCall(@generate_rttiattribute_procs_classes,nil);
+        if assigned(current_module.localsymtable) then
+          current_module.localsymtable.SymList.ForEachCall(@generate_rttiattribute_procs_classes,nil);
+      end;
+
     procedure write_persistent_type_info(st:tsymtable;is_global:boolean);
       var
         i : longint;
Index: compiler/ppu.pas
===================================================================
--- compiler/ppu.pas	(revision 21375)
+++ compiler/ppu.pas	(working copy)
@@ -122,6 +122,7 @@
   ibvariantdef     = 57;
   ibundefineddef   = 58;
   ibunicodestringdef = 59;
+  ibattributesdef  = 60;
   {implementation/ObjData}
   ibnodetree       = 80;
   ibasmsymbols     = 81;
Index: compiler/pmodules.pas
===================================================================
--- compiler/pmodules.pas	(revision 21375)
+++ compiler/pmodules.pas	(working copy)
@@ -962,6 +962,8 @@
              current_module.mainfilepos:=init_procinfo.entrypos;
            end;
 
+         generate_rttiattribute_procs;
+
          { Generate specializations of objectdefs methods }
          generate_specialization_procs;
 
Index: compiler/pdecobj.pas
===================================================================
--- compiler/pdecobj.pas	(revision 21375)
+++ compiler/pdecobj.pas	(working copy)
@@ -64,6 +64,7 @@
 
     var
       current_objectdef : tobjectdef absolute current_structdef;
+      current_rttiattributesdef : trtti_attributesdef;
 
 
     procedure constr_destr_finish_head(pd: tprocdef; const astruct: tabstractrecorddef);
@@ -201,6 +202,11 @@
               Message(parser_e_enumerator_identifier_required);
             consume(_SEMICOLON);
           end;
+        if assigned(current_rttiattributesdef) then
+          begin
+            p.rtti_attributesdef := current_rttiattributesdef;
+            current_rttiattributesdef:=nil;
+          end;
         { hint directives, these can be separated by semicolons here,
           that needs to be handled here with a loop (PFV) }
         while try_consume_hintdirective(p.symoptions,p.deprecatedmsg) do
@@ -955,6 +961,35 @@
       end;
 
 
+    procedure parse_rttiattributes(var rtti_attributes: trtti_attributesdef);
+      var
+        p: tnode;
+        paras: tnode;
+      begin
+        consume(_LECKKLAMMER);
+        { Parse attribute type }
+        p := factor(false,true);
+
+        if try_to_consume(_LKLAMMER) then
+          begin
+            paras:=parse_paras(false,false,_RKLAMMER);
+            consume(_RKLAMMER);
+          end
+        else
+          paras := nil;
+
+        // Add attribute to attribute list which will be added
+        // to the property which is defined next.
+        if not assigned(rtti_attributes) then
+          rtti_attributes := trtti_attributesdef.create(attributedef);
+        rtti_attributes.addattribute(ttypesym(ttypenode(p).typesym),ttypenode(p).typesymderef,paras);
+
+        p.free;
+
+        consume(_RECKKLAMMER);
+      end;
+
+
     procedure parse_object_members;
 
       var
@@ -1054,6 +1089,7 @@
         class_fields:=false;
         is_final:=false;
         final_fields:=false;
+        current_rttiattributesdef:=nil;
         object_member_blocktype:=bt_general;
         repeat
           case token of
@@ -1213,6 +1249,10 @@
                 fields_allowed:=false;
                 is_classdef:=false;
               end;
+            _LECKKLAMMER:
+              begin
+                parse_rttiattributes(current_rttiattributesdef);
+              end;
             _END :
               begin
                 consume(_END);
Index: compiler/symdef.pas
===================================================================
--- compiler/symdef.pas	(revision 21375)
+++ compiler/symdef.pas	(working copy)
@@ -752,6 +752,24 @@
           function getbasedef: tenumdef;
        end;
 
+       trtti_attribute = class
+          typesym       : tsym;
+          typesymderef  : tderef;
+          paras         : tobject;
+          symbolname    : string;
+       end;
+
+       trtti_attributesdef = class(tstoreddef)
+          rtti_attributes    : TFPObjectList;
+          procedure addattribute(atypesym: tsym; atypesymderef: tderef; aparas: tobject);
+          destructor destroy; override;
+          constructor ppuload(ppufile:tcompilerppufile);
+          procedure ppuwrite(ppufile:tcompilerppufile);override;
+          function get_attribute_count: longint;
+          procedure buildderef;override;
+          procedure deref;override;
+       end;
+
        tsetdef = class(tstoreddef)
           elementdef : tdef;
           elementdefderef : tderef;
@@ -2101,7 +2119,84 @@
          GetTypeName:='<enumeration type>';
       end;
 
+{****************************************************************************
+                             TRTTI_ATTRIBUTESDEF
+****************************************************************************}
 
+    procedure trtti_attributesdef.addattribute(atypesym: tsym; atypesymderef: tderef; aparas: tobject);
+      var
+        newattribute: trtti_attribute;
+      begin
+        if not assigned(rtti_attributes) then
+          rtti_attributes := TFPObjectList.Create(True);
+        newattribute := trtti_attribute.Create;
+        newattribute.typesym := atypesym;
+        newattribute.typesymderef := atypesymderef;
+        newattribute.paras:=aparas;
+        rtti_attributes.Add(newattribute);
+      end;
+
+    destructor trtti_attributesdef.destroy;
+      begin
+        rtti_attributes.Free;
+        inherited destroy;
+      end;
+
+    constructor trtti_attributesdef.ppuload(ppufile: tcompilerppufile);
+      var
+        AttrCount: longint;
+        i: integer;
+        atypesymderef : tderef;
+      begin
+        inherited ppuload(attributedef,ppufile);
+        AttrCount := ppufile.getlongint;
+        for i := 0 to AttrCount -1 do
+          begin
+            ppufile.getderef(atypesymderef);
+            addattribute(nil,atypesymderef,nil);
+          end;
+        rtti_attributes := nil;
+      end;
+
+    procedure trtti_attributesdef.ppuwrite(ppufile: tcompilerppufile);
+      var
+        i: longint;
+      begin
+        inherited ppuwrite(ppufile);
+        ppufile.putlongint(get_attribute_count);
+        for i := 0 to get_attribute_count - 1 do
+          begin
+            ppufile.putderef(trtti_attribute(rtti_attributes[i]).typesymderef);
+          end;
+        ppufile.writeentry(ibattributesdef);
+      end;
+
+    function trtti_attributesdef.get_attribute_count: longint;
+      begin
+        if assigned(rtti_attributes) then
+          result := rtti_attributes.Count
+        else
+          result := 0;
+      end;
+
+    procedure trtti_attributesdef.buildderef;
+      var
+        i: longint;
+      begin
+        inherited buildderef;
+        for i := 0 to get_attribute_count -1 do
+          trtti_attribute(rtti_attributes[i]).typesymderef.build(trtti_attribute(rtti_attributes[i]).typesym);
+      end;
+
+    procedure trtti_attributesdef.deref;
+      var
+        i: longint;
+      begin
+        inherited deref;
+        for i := 0 to get_attribute_count -1 do
+          trtti_attribute(rtti_attributes[i]).typesym := ttypesym(trtti_attribute(rtti_attributes[i]).typesymderef.resolve);
+      end;
+
 {****************************************************************************
                                  TORDDEF
 ****************************************************************************}
Index: compiler/ncgrtti.pas
===================================================================
--- compiler/ncgrtti.pas	(revision 21375)
+++ compiler/ncgrtti.pas	(working copy)
@@ -269,6 +269,8 @@
     procedure TRTTIWriter.published_properties_write_rtti_data(propnamelist:TFPHashObjectList;st:tsymtable);
       var
         i : longint;
+        attributeindex: ShortInt;
+        attributecount: byte;
         sym : tsym;
         proctypesinfo : byte;
         propnameitem  : tpropnamelistitem;
@@ -392,7 +394,20 @@
                   internalerror(200512201);
                 current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_16bit(propnameitem.propindex));
                 current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(proctypesinfo));
+
+                if assigned(tpropertysym(sym).rtti_attributesdef) then
+                  attributecount:=tpropertysym(sym).rtti_attributesdef.get_attribute_count
+                else
+                  attributecount:=0;
+
+                current_asmdata.asmlists[al_rtti].concat(Tai_const.Create_8bit(attributecount));
+
                 write_string(tpropertysym(sym).realname);
+
+                for attributeindex:=0 to attributecount-1 do
+                  begin
+                    current_asmdata.asmlists[al_rtti].concat(Tai_const.createname(trtti_attribute(tpropertysym(sym).rtti_attributesdef.rtti_attributes[attributeindex]).symbolname,0));
+                  end;
                 maybe_write_align;
              end;
           end;
Index: compiler/symtable.pas
===================================================================
--- compiler/symtable.pas	(revision 21375)
+++ compiler/symtable.pas	(working copy)
@@ -435,6 +435,7 @@
              ibformaldef : def:=tformaldef.ppuload(ppufile);
              ibvariantdef : def:=tvariantdef.ppuload(ppufile);
              ibundefineddef : def:=tundefineddef.ppuload(ppufile);
+             ibattributesdef : def:=trtti_attributesdef.ppuload(ppufile);
              ibenddefs : break;
              ibend : Message(unit_f_ppu_read_error);
            else
Index: compiler/utils/ppudump.pp
===================================================================
--- compiler/utils/ppudump.pp	(revision 21375)
+++ compiler/utils/ppudump.pp	(working copy)
@@ -2624,6 +2624,19 @@
 
            end;
 
+         ibattributesdef :
+           begin
+             readcommondef('Attributes definition',defoptions);
+             l := getlongint;
+             writeln(space,' Attributes count : ', l);
+             for j := 0 to l-1 do
+               begin
+                 writeln(space,'Attribute ',j);
+                 write(space,'          typedef : ');
+                 readderef('');
+               end;
+           end;
+
          iberror :
            begin
              WriteError('!! Error in PPU');
Index: compiler/symsym.pas
===================================================================
--- compiler/symsym.pas	(revision 21375)
+++ compiler/symsym.pas	(working copy)
@@ -277,6 +277,7 @@
           dispid        : longint;
           propaccesslist: array[tpropaccesslisttypes] of tpropaccesslist;
           parast : tsymtable;
+          rtti_attributesdef : trtti_attributesdef;
           constructor create(const n : string);
           destructor  destroy;override;
           constructor ppuload(ppufile:tcompilerppufile);
Index: rtl/objpas/typinfo.pp
===================================================================
--- rtl/objpas/typinfo.pp	(revision 21375)
+++ rtl/objpas/typinfo.pp	(working copy)
@@ -232,11 +232,16 @@
         //     6 : true, constant index property
         PropProcs : Byte;
 
+        AttributeCount : byte;
         Name : ShortString;
       end;
 
       TProcInfoProc = Procedure(PropInfo : PPropInfo) of object;
 
+      TAttributeProc = function : TObject;
+      PAttributeProcList = ^TAttributeProcList;
+      TAttributeProcList = array[0..255] of TAttributeProc;
+
       PPropList = ^TPropList;
       TPropList = array[0..65535] of PPropInfo;
 
@@ -266,8 +271,6 @@
 function GetPropList(AClass: TClass; out PropList: PPropList): Integer;
 function GetPropList(Instance: TObject; out PropList: PPropList): Integer;
 
-
-
 // Property information routines.
 Function IsStoredProp(Instance: TObject;PropInfo : PPropInfo) : Boolean;
 Function IsStoredProp(Instance: TObject; const PropName: string): Boolean;
@@ -354,6 +357,9 @@
 procedure SetRawInterfaceProp(Instance: TObject; const PropName: string; const Value: Pointer);
 procedure SetRawInterfaceProp(Instance: TObject; PropInfo: PPropInfo; const Value: Pointer);
 
+function GetAttributeProclist(PropInfo: PPropInfo): PAttributeProcList;
+function GetPropAttribute(PropInfo: PPropInfo; AttributeNr: byte): TObject;
+
 // Auxiliary routines, which may be useful
 Function GetEnumName(TypeInfo : PTypeInfo;Value : Integer) : string;
 Function GetEnumValue(TypeInfo : PTypeInfo;const Name : string) : Integer;
@@ -404,7 +410,29 @@
 {$endif FPC_REQUIRES_PROPER_ALIGNMENT}
    end;
 
+function GetAttributeProclist(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 := GetAttributeProclist(PropInfo);
+      result := AttributeProcList^[AttributeNr]();
+    end;
+end;
+
 Function GetEnumName(TypeInfo : PTypeInfo;Value : Integer) : string;
 
   Var PS : PShortString;
@@ -631,7 +659,7 @@
           if ShortCompareText(Result^.Name, P) = 0 then
             exit;
           // 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;
       // parent class
       Typeinfo:=hp^.ParentInfo;
@@ -765,7 +793,7 @@
           PropList^[TP^.NameIndex]:=TP;
         // Point to TP next propinfo record.
         // 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);
       end;
     TypeInfo:=TD^.Parentinfo;
unit testclassattributesunit;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils;

type

  { TAttribute }

  TAttribute = class
  private
    FValue: string;
  public
    constructor create(AValue: string);
    property Value: string read FValue;
  end;

  { THasAttributes }

  THasAttributes = class
  private
    FTestProp: integer;
  published
    [TAttribute('Value first attribute')]
    [TAttribute('Value second attribute')]
    property TestProp1: integer read FTestProp write FTestProp;
    [TAttribute('Value third attribute')]
    property TestProp2: integer read FTestProp write FTestProp;
    [TList]
    property TestProp3: integer read FTestProp write FTestProp;
  end;

implementation

{ TAttribute }

constructor TAttribute.create(AValue: string);
begin
  FValue := AValue;
end;

end.

program testclassattributes;

{$mode objfpc}{$H+}

uses
  Classes, testclassattributesunit,typinfo;

procedure WritePropInfo(APropInfo: PPropInfo);
var
  i: Integer;
  AnAttr: TObject;
begin
  writeln('Propertyname: ',APropInfo^.Name);
  writeln('Attributecount: ',APropInfo^.AttributeCount);
  for i := 0 to APropInfo^.AttributeCount-1 do
    begin
    AnAttr := GetPropAttribute(APropInfo,i);
    writeln('  attribute:', AnAttr.ClassName);
    if AnAttr is TAttribute then
    writeln('  value:', TAttribute(AnAttr).Value);
    end;
end;

procedure WriteClassPropInfo(AnObject: TObject);
var
  APropList: PPropList;
  propcount: integer;
  i: integer;
begin
  PropCount := GetPropList(AnObject,APropList);
  for i := 0 to propcount-1 do
    begin
    WritePropInfo(APropList^[i]);
    end;
end;

var
  HasAttributes: THasAttributes;

begin
  HasAttributes := THasAttributes.Create;
  WriteClassPropInfo(HasAttributes);
  HasAttributes.Free;
end.

_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-devel

Reply via email to