Delphi interface mechanism--compiler implementation process of interface operation (2)

Source: Internet
Author: User

Memory space for Interface objects


Suppose we define the following two interfaces Iintfa and IINTFB, where Proca and PROCB will be implemented as static methods, and Virta and VIRTB will be implemented as virtual methods:

[Delphi]View Plaincopyprint?
    1. IINTFA = Interface
    2. procedure Proca;
    3. procedure Virta;
    4. end;
    5. IINTFB = Interface
    6. procedure PROCB;
    7. procedure VIRTB;
    8. end;


Then we define a Tmyobject class, which inherits from Tinterfacedobject and implements IINTFA and IINTFB two interfaces:

[Delphi]View Plaincopyprint?
    1. Tmyobject = Class (Tinterfacedobject, IINTFA, IINTFB)
    2. Ffielda:integer;
    3. Ffieldb:integer;
    4. procedure Proca;
    5. procedure Virta; virtual;
    6. procedure PROCB;
    7. procedure VIRTB; virtual;
    8. end;

Then we execute the following code:

[Delphi]View Plaincopyprint?
  1. Var
  2. Myobject:tmyobject;
  3. Myintf:iinterface;
  4. MYINTFA:IINTFA;
  5. MYINTFB:IINTFB;
  6. begin
  7. MyObject: = Tmyobject.  Create; //Create Tmyobject object
  8. myintf: = MyObject; //To point the interface to the MyObject object
  9. MYINTFA: = MyObject;
  10. MYINTFB: = MyObject;
  11. end;



During the execution of the above code, the compiler implements the memory space diagram as follows:


Look at the leftmost column first. MyObject is an object pointer to a 0 offset (virtual method table pointer) in the object's data space. You can see that MYINTF/MYINTFA/MYINTFB three interfaces are implemented as pointers, and these three pointers point to an area of 4 bytes in MyObject object data space, respectively.
The middle column is the object memory space. As you can see, there are three additional fields in the object memory space of Tmyobject compared to objects that do not support interfaces: IINTERFACE/IINTFB/IINTFA. These fields are also pointers, pointing to the memory address of the interface jump table. Note that the order of MYINTFA/MYINTFB is the opposite of the order of Tmyobject class declarations, why?
The third column is the virtual method table for the class, consistent with the generic class (the class that does not support the interface).
-----------
Interface Jump Table
-----------
An interface jump table is a row of function pointers to the function addresses that implement the current interface, arranged in the order in which they are declared in the interface. Now let's look at what the so-called "interface Jump Table" is for.
We know that when an object calls a member function of a class, such as executing Myobject.proca, it implicitly passes a self pointer to the member function: Myobject.proca (self). Self is the address of the object data space. So how does the compiler know the self pointer? The original object pointer MyObject point to the address is self, the compiler directly remove the myobject^ can be used as self.
When invoking member functions as an interface, such as Myintfa.proca, the compiler does not know what type of object MYINTFA is pointing to (class), and does not know the distance between MYINTFA and self (in fact, in the example above, Delphi The compiler knows the distance between MYINTFA and self, just to be compatible with COM's binary format, so that other languages can also invoke interface member functions using interface pointers, which must be corrected with the later self pointer, and the compiler directly sets the address that MYINTFA points to self. As you can see, MYINTFA points to the $18 offset address in the MyObject object space. The self pointer is, of course, wrong, and the compiler cannot call Tmyobject.proca directly, but instead calls Proca in IINTFA's Interface jump table. The content of the Proca in the interface jump table is to fix the self pointer (self-$18) and then call Tmyobject.proca, which is the correct invocation of the object's member function. Because each class implements an interface in a different order, there are different interface jump tables for the same interface to be implemented in separate classes (of course, the editor can intelligently check that some classes have an "interface jump table" offset of the same or shared use).
This is the compiler implementation process, using the "Interface Jump table" is the real reason is that interface must support the COM binary format standard. is a COM binary specifications excerpt from the Learning Notes of 〈com principles and applications:


----------------------------------------
Initialization of interface jump pointers in object memory space
----------------------------------------
Another problem is how the interface jump pointers in the object memory space are initialized. Originally, in Tobject.initinstance, after clearing 0 object memory space with Fillchar, the work is to initialize the object's interface jump pointer:

[Delphi]View Plaincopyprint?
  1. function TObject.  InitInstance (instance:pointer): TObject;
  2. Var
  3. intftable:pinterfacetable;
  4. Classptr:tclass;
  5. I:integer;
  6. begin
  7. Fillchar (instance^, Instancesize, 0);
  8. Pinteger (Instance) ^: = Integer (self);
  9. Classptr: = self;
  10. While classptr <> nil do
  11. begin
  12. Intftable: = Classptr.  getinterfacetable;
  13. if intftable <> nil Then
  14. For I: = 0 to intftable. entrycount-1 do
  15. With intftable. Entries[i] do
  16. begin
  17. if VTable <> nil Then
  18. Pinteger (@PChar (Instance) [Ioffset]) ^: = Integer (VTable);
  19. end;
  20. Classptr: = Classptr.  Classparent;
  21. end;
  22. Result: = Instance;
  23. end;

----------------------
The realization of implements
----------------------
Delphi can use the Implements keyword to delegate an interface method to another interface or object. The following tmyobject as the base class, to examine the implementation of the Implements method.

[Delphi]View Plaincopyprint?
    1. tmyobject = class ( TINTERFACEDOBJECT,&NBSP;IINTFA,&NBSP;IINTFB)   
    2.     FFieldA:  integer;  
    3. &NBSP;&NBSP;&NBSP;&NBSP;FFIELDB:&NBSP;INTEGER;&NBSP;&NBSP;
    4.     procedure proca;  
    5. procedure virta; virtual;  
    6.     procedure procb;  
    7. procedure virtb; virtual;  
    8.     DESTRUCTOR&NBSP;DESTROY;&NBSP;OVERRIDE;&NBSP;&NBSP;
    9. Li class= "alt" >   end;  

(1) Implement implements with interface member variables

[Delphi]View Plaincopyprint?
    1. TMyObject2 = Class (Tinterfacedobject, IINTFA)
    2. FINTFA:IINTFA;
    3. Property INTFA:IINTFA read FINTFA implements IINTFA;
    4. End

At this point the implementation of the compiler is very simple, because FINTFA is the interface pointer, if you use the interface assignment MYINTFA: = MyObject2 Such a statement invocation, MYINTFA directly points to MYOBJECT2.FINTFA.

(2) Implement implements with object member variables

In the following example, if an interface class TMyObject3 implements implements as an object (which is usually the case), its object memory space is arranged in almost the same way as the Tmyobject memory space:

[Delphi]View Plaincopyprint?
    1. TMYOBJECT3 = Class (Tinterfacedobject, IINTFA, IINTFB)
    2. Fmyobject:tmyobject;
    3. function Getmyobject:tmyobject;
    4. Property Myobject:tmyobject Read Getmyobject implements IINTFA, IINTFB;
    5. end;



The difference is that the content of the TMyObject3 "interface Jump table" has changed. Since TMYOBJECT3 does not implement IINTFA and IINTFB on its own, it is the Fmyobject object that implements both interfaces. At this point, the method called in the interface jump table must be changed to the method that calls the Fmyobject object. For example, the following code:

[Delphi]View Plaincopyprint?
  1. Var
  2. MYOBJECT3:TMYOBJECT3;
  3. MYINTFA:IINTFA;
  4. begin
  5. myobject3:= TMyObject3.  Create;
  6. MyObject3. Fmyobject: = Tmyobject.  Create;
  7. MYINTFA: = MyObject3;
  8. MYINTFA. _addref;
  9. MYINTFA.  Proca;
  10. MYINTFA. _release;
  11. end;


When executing the MYINTFA._ADDREF statement, the compiler generates an "interface Jump" code:

[Delphi]View Plaincopyprint?
  1. {myintfa._addref;}
  2. mov eax,[ebp-$0c] //eax = myintfa^
  3. push eax //myintfa^ set to self
  4. MOV Eax,[eax] //eax = interface Jump table address pointer
  5. Call DWORD ptr [eax+$04] //Go to Interface jump table
  6. {The code in the "Interface Jump segment"}
  7. mov eax,[esp+$04] //[esp+$04] is the interface pointer content (myintfa^)
  8. Add eax,-$14 //fix eax = self (MyObject2)
  9. Call TMyObject2. Getmyobject
  10. mov [esp+$04],eax //Get Fmyobject object, note [esp+$04]
  11. JMP tinterfacedobject. _addref //Call Fmyobject._addref

[Esp+$04] is a notable place. Only one parameter, self, is fixed in the interface jump table, and the other invocation parameters, if any, are set by the compiler before the execution enters the interface jump table. Here _addref is using the stdcall calling convention, so esp+$04 is self. As mentioned earlier, the compiler directly takes the contents of the interface pointer as the self parameter, and then goes to the "interface jump table" to fix the self, before invoking the object method. The assembly code above is the method of correcting self to fmyobject and calling Fmyobject.
You can see that the Fmyobject._addref method increases the reference count of the Fmyobject object, and it seems that the implementation of implements simply transmits the interface to the object execution, and other methods must be used to implement the COM component aggregation.

http://blog.csdn.net/tht2009/article/details/6768032

Delphi interface mechanism--compiler implementation process of interface operation (2)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.