On 11-4-2023 12:11, Hairy Pixels via fpc-devel wrote:
Btw, I was curious because I haven’t done this in so many years but is this 
basically how a VTable looks in procedural code?

Every class has a reference to class type information stored at negative offsets. The class type info contains VMT, IVMT, the class name etc.  The table is compile time (iow a structured constant in procedural pascal) and the layout of that table is roughly  described in objpash.inc:

 const
       vmtInstanceSize         = 0;
       vmtParent               = sizeof(SizeInt)*2;
       { These were negative value's, but are now positive, else classes
         couldn't be used with shared linking which copies only all data from          the .global directive and not the data before the directive (PFV) }
       vmtClassName            = vmtParent+sizeof(pointer);
       vmtDynamicTable         = vmtParent+sizeof(pointer)*2;
       vmtMethodTable          = vmtParent+sizeof(pointer)*3;
       vmtFieldTable           = vmtParent+sizeof(pointer)*4;
       vmtTypeInfo             = vmtParent+sizeof(pointer)*5;
       vmtInitTable            = vmtParent+sizeof(pointer)*6;
       vmtAutoTable            = vmtParent+sizeof(pointer)*7;
       vmtIntfTable            = vmtParent+sizeof(pointer)*8;
       vmtMsgStrPtr            = vmtParent+sizeof(pointer)*9;
       { methods }
       vmtMethodStart          = vmtParent+sizeof(pointer)*10;
       vmtDestroy              = vmtMethodStart;
       vmtNewInstance          = vmtMethodStart+sizeof(codepointer);
       vmtFreeInstance         = vmtMethodStart+sizeof(codepointer)*2;
       vmtSafeCallException    = vmtMethodStart+sizeof(codepointer)*3;
       vmtDefaultHandler       = vmtMethodStart+sizeof(codepointer)*4;
       vmtAfterConstruction    = vmtMethodStart+sizeof(codepointer)*5;
       vmtBeforeDestruction    = vmtMethodStart+sizeof(codepointer)*6;
       vmtDefaultHandlerStr    = vmtMethodStart+sizeof(codepointer)*7;
       vmtDispatch             = vmtMethodStart+sizeof(codepointer)*8;
       vmtDispatchStr          = vmtMethodStart+sizeof(codepointer)*9;
       vmtEquals               = vmtMethodStart+sizeof(codepointer)*10;
       vmtGetHashCode          = vmtMethodStart+sizeof(codepointer)*11;
       vmtToString             = vmtMethodStart+sizeof(codepointer)*12;

The VMT has one pointer for each virtual method in a class. In e.g. TAnimal version of the table those pointers would point to the TAnimal copy of the virtual methods or a method that raises an "abstract" exception if the method is abstract.  The VMTs are linked (the TDog table points to TAnimal in its parent pointer, which in turn points to its parent) for the IS and AS functionality, but not for VMTs

The TDog version of the table they point to the TDog version.

So basically the tdog.create would only allocate memory, and initialize the first "field" with a pointer to the tdog class structured constants, and advance the pointer to the next field and return that as the instance pointer.

const ddogclassinfo : array [0..3]of pointer = (@parent,@vmtmethod1,@vmtmethod2,@vmtmethod3);

If the class (TDog) overrides the method, it points to the tdog reference, if not, this table still lists the TAnimal version of the method.


constructor TDog.Create;

begin

  result:=NewInstance; // allocate memory for the instance

  ppointer(result)^:=@dogclassinfo;

  inc(result,sizeof(pointer));

end;


Dispatch then becomes in simplified Pascal  (assume that "instance" is a initialized class)

var vmt : pointer;

vmt:=ppointer(instance)^;

tthemethod(ppointer(vmt)[n]).themethod(parameter);

... with n the number of the respective virtual method, and TTheMethod its signature.  Handling all those indexes, signatures and generating the parameter loading into registers, stack is the core work of the compiler.

Note that Delphi 1 had a different way of dispatch, called "dynamic" (the 16-bit compiler had to be much more careful with memory), at the expense of performance. IIRC that version kind of worked like your scheme, where every class only had a table for the methods it really had, leaving the rest up to a function in the parent. I don't recall the exact details (I suggest an old Delphi manual or mastering Delphi for that).  Free Pascal ignores this directive, and doesn't implement it.



_______________________________________________
fpc-devel maillist  -  fpc-devel@lists.freepascal.org
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel

Reply via email to