Abstract methods (or “why not”)

I always considered the implementation of abstract methods in Delphi to be a hack, mostly because there is no way (or is there?) to treat this: “[DCC Warning] W1020 Constructing instance of a class containing abstract method.” warning as a compile error. We all know  that a slot is reserved in the abstract class for each virtual (and abstract) method it holds. Delphi by default initializes those slots with addresses to _AbstractError routine in System unit.

This is how it’s defined in Delphi 2009:

procedure _AbstractError;
begin
  if Assigned(AbstractErrorProc) then
    AbstractErrorProc;
  _RunError(210);  // loses return address
end;

AbstractErrorProc is being set up by SysUtils (in initialization section) if you include it in your application to be handled like this:

procedure AbstractErrorHandler;
begin
  raise EAbstractError.CreateRes(@SAbstractError);
end;

Unfortunately we cannot plug-in our custom handler and hope to just bypass all abstract errors because a _RunError(210); call will be performed in any case by _AbstractError function — which would kill the application anyway.

But if you still want to hack your way through this default behavior, there are 2 ways to do it. Note that those will only work for abstract functions with no parameters — otherwise the stack will become corrupted.

Direct memory manipulation method:

type
  TAbstractClass = class
    procedure AbstractMethod; virtual; abstract;
  end;

var
  PtrToAbErr : ^Pointer;
  C3 : ^Byte;

begin
  { Get the pointer to the function in slot 0 of VPTR }
  PtrToAbErr := Pointer(TAbstractClass);

  { Point to the first byte of _AbstractError routine }
  C3 := PtrToAbErr^;

  { Overwrite the first byte with a RET instruction}
  C3^ := $C3;

  WriteLn('We''ve got here! Hooray!');
  ReadLn;
end.

This method will not work if the memory in which _AbstractError resides is write-protected. You would have to manipulate the permissions first.

The second method will work but depends on the RTL’s implementation of _AbstractError:

type
  TAbstractClass = class
    procedure AbstractMethod; virtual; abstract;
  end;

procedure MyHndlr();
asm
  ADD ESP, 4
  RET
end;

var
  O : TAbstractClass;

begin
  AbstractErrorProc := MyHndlr;

  O := TAbstractClass.Create();
  O.AbstractMethod;

  WriteLn('We''ve got here! Hooray!');
  ReadLn;
end.

This method will plug in our own AbstractErrorProc handler which will “remove” the return address from the stack (in our case the address back to _AbstractError) and then simply returns to the code that originated the call to the abstract method.

I know it’s not very useful … but it’s fun 🙂