Feature announcement: Interface RTTI

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Feature announcement: Interface RTTI

Sven Barth-2
Hello together!

I'm pleased to finally announce the addition of Interface RTTI to Free
Pascal.

Interface RTTI essentially provides a list of all methods available in
an interface if it's declared is parsed with $M+ or has such an
interface as parent.

For now however this only applies to COM style interfaces. CORBA/Raw
style interfaces don't respect $M+ yet, so they don't have that RTTI.

Also note that while this RTTI provides the same content as Delphi it is
*not* Delphi compatible. In fact since FPC's TypInfo unit never has been
fully compatible with Delphi it's save to come out and say that the
TypInfo unit is considered a known incompatibility to Delphi and always
will be. For compatibility with newer versions of Delphi it's suggested
to use the RTTI unit (patches to improve/extend its functionality are
welcome).

To access the methods it's best to use the new types provided by the
TypInfo unit with the starting point being TInterfaceData.
The list of methods is available in the property MethodTable of type
TIntfMethodTable. This contains two count fields, namely Count and
RTTICount. The former *always* contains the number of methods contained
in *this* interface, the latter is either $FFFF if $M- or the same as
Count if $M+. Directly after that follow the message information in the
form of TIntfMethodEntry and can be easily accessed using the Method[]
property of TIntfMethodTable.

Each method consists of its name, calling convention, method kind,
return type (if any), needed stack size for the parameters, any
parameters (which can be accessed using the Param[] property) and if the
return type is not Nil then also the location of the return value.

A parameter consists of its name, the parameter type (Note: open array
parameters have their element type as type!), parameter flags and the
location the parameter needs to reside in for invoking the method. These
locations aren't restricted to single locations however as for example
on 32-bit platforms 64-bit values might be passed using two registers.
The parameters also contain hidden parameters not really visible in the
methods declarations like the Self argument, an eventual Result
parameter (for example AnsiString or UnicodeString is passed this way on
some platforms) or the high parameter for open arrays.

=== example begin ===

program tintfrtti;

{$mode objfpc}{$H+}

uses
  typinfo;

type
  {$push}
  {$M+}
  ITest = interface
    procedure Test;
    function Test2(aArg1: LongInt): Int64;
    function Test3(aArg1: array of String): String;
  end;
  {$pop}

var
  id: PInterfaceData;
  imt: PIntfMethodTable;
  ime: PIntfMethodEntry;
  vmp: PVmtMethodParam;
  i, j: LongInt;
begin
  id := PInterfaceData(GetTypeData(TypeInfo(ITest)));
  imt := id^.MethodTable;
  Writeln('Methods: ', imt^.Count, ' ', imt^.RTTICount);
  for i := 0 to imt^.Count - 1 do begin
    ime := imt^.Method[i];
    Writeln('Method ', ime^.Name);
    Writeln(#9'CC: ', ime^.CC);
    Writeln(#9'Kind: ', ime^.Kind);
    Writeln(#9'StackSize: ', ime^.StackSize);
    if Assigned(ime^.ResultType) then begin
      Writeln(#9'Result Type: ', ime^.ResultType^^.Name);
      Writeln(#9'Result Locations: ', ime^.ResultLocs^.Count);
    end else begin
      Writeln(#9'Return Type: <none>');
      Writeln(#9'Result Locations: <none>');
    end;
    Writeln(#9'Params: ', ime^.ParamCount);
    for j := 0 to ime^.ParamCount - 1 do begin
      vmp := ime^.Param[j];
      Writeln(#9'Param ', vmp^.Name);
      Writeln(#9#9'Type: ', vmp^.ParamType^^.Name);
      Writeln(#9#9'Flags: ', HexStr(Word(vmp^.Flags), 4));
      Writeln(#9#9'Locations: ', vmp^.ParaLocs^.Count);
    end;
  end;
end.

=== example end ===

On a x86_64-linux it will print the following:

=== output begin ===

Methods: 3 3
Method Test
        CC: ccReg
        Kind: mkProcedure
        StackSize: 0
        Return Type: <none>
        Result Locations: <none>
        Params: 1
        Param $self
                Type: ITest
                Flags: 0288
                Locations: 1
Method Test2
        CC: ccReg
        Kind: mkFunction
        StackSize: 0
        Result Type: Int64
        Result Locations: 1
        Params: 2
        Param $self
                Type: ITest
                Flags: 0288
                Locations: 1
        Param aArg1
                Type: LongInt
                Flags: 0000
                Locations: 1
Method Test3
        CC: ccReg
        Kind: mkFunction
        StackSize: 0
        Result Type: AnsiString
        Result Locations: 1
        Params: 4
        Param $self
                Type: ITest
                Flags: 0288
                Locations: 1
        Param $result
                Type: AnsiString
                Flags: 0881
                Locations: 1
        Param aArg1
                Type: AnsiString
                Flags: 0014
                Locations: 1
        Param $highAARG1
                Type: Int64
                Flags: 0182
                Locations: 1

=== output end ===

A note regarding performance: The indexing properties of except the one
in TParameterLocations have a complexity of O(n) as they always need to
iterate from the 0th element. So if you have code that relies on
performance it might be better to iterate them like this:

=== code begin ===

i := 0;
ime := imt^.Method[0];
while i < imt^.Count do begin
  { do something with ime }
  ime := ime^.Next;
  Inc(i);
end;

=== code end ===

The Next property correctly handles alignment on targets that requires
them, so they are the recommended, platform independent way of accessing
the following entry. Please note however that Next will *not* return Nil
once the end is reached (because it has no knowledge about this; it
simply returns a pointer to the location after itself).

Maybe in the future for-in-iterators might be added.

Similar functionality has been added to TPropData which can also be
accessed from TInterfaceData.PropertyTable and
TInterfaceRawData.PropertyTable.
Further utility records to simplify navigation of the raw RTTI are
planned to be added.
Also the Delphi compatible RTTI unit will be extended accordingly.

Regards,
Sven
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Feature announcement: Interface RTTI

Michael Van Canneyt


On Sat, 28 Jan 2017, Sven Barth wrote:

> Hello together!
>
> I'm pleased to finally announce the addition of Interface RTTI to Free
> Pascal.
>
> Interface RTTI essentially provides a list of all methods available in
> an interface if it's declared is parsed with $M+ or has such an
> interface as parent.

Nice job. Does this mean we can now implement Invoke() ?

Michael.
_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Feature announcement: Interface RTTI

Sven Barth-2
On 28.01.2017 15:25, Michael Van Canneyt wrote:

>
>
> On Sat, 28 Jan 2017, Sven Barth wrote:
>
>> Hello together!
>>
>> I'm pleased to finally announce the addition of Interface RTTI to Free
>> Pascal.
>>
>> Interface RTTI essentially provides a list of all methods available in
>> an interface if it's declared is parsed with $M+ or has such an
>> interface as parent.
>
> Nice job. Does this mean we can now implement Invoke() ?

We can start to, yes. Though there will probably be two different
flavors of that: the Delphi compatible one that merely takes the
address, the calling convention and the parameter values and an extended
one that can also handle location information.

Regards,
Sven

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Feature announcement: Interface RTTI

silvioprog
In reply to this post by Sven Barth-2
On Sat, Jan 28, 2017 at 9:29 AM, Sven Barth <[hidden email]> wrote:
Hello together!

I'm pleased to finally announce the addition of Interface RTTI to Free
Pascal.

Interface RTTI essentially provides a list of all methods available in
an interface if it's declared is parsed with $M+ or has such an
interface as parent.

Great news. Finally I'm going to make some my RTTI layer written in Delphi compatible with Free Pascal. Thank you for making it even better!

--
Silvio Cl├ęcio

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Reply | Threaded
Open this post in threaded view
|

Re: Feature announcement: Interface RTTI

Anthony Walter-3
In reply to this post by Sven Barth-2
This is fantastic news. Thank you for all you work Sven. This has been committed to the main branch? I'll be testing shortly.

_______________________________________________
fpc-pascal maillist  -  [hidden email]
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal