|
Reported by Antoine Leca; checked by Duncan Murdoch, Reinier Sterkenburg
Delphi 2/3/4 do not select the correct method when calling
a virtual method via a polymorphed pointer with old-style objects
created as typed consts. It fall short by 2, i.e. it selects the
method two previous from the correct one; in fact, it does as if
the VMT for "objects" would have the same format as a one for
"classes" (which is wrong).
These objects are not required (see the BP 7 manual) to have a
constructor. It's a rule of the language that a constructor must
be called to initialize any object with virtual methods.
However, the compiler is forgiving about this in BP 7 and allows
constant object declarations to avoid the constructor call
(this is documented behaviour); but the Delphi 32 bits compilers
aren't so forgiving.
This is a bug of the compiler, because, while undocumented, all
the stuff for handling old-style objects is present. Unfortunately,
the compiler initialises the VMT field of the typed-const object
with a wrong value.
Below is a source file to see the problem. It works OK under BP7
and Delphi 1.02, where it outputs:
TObj.c elem=1
TObj.c elem=1
but fails under Delphi 2.01, where it outputs:
TObj.c elem=1
TObj2.a elem=1
Under Delphi 3.01, 4.02, 5.01 and 6.02 it produces an Access Violation
on execution of the second line.
Under Kylix (1.0) it gives: "Program Test416 received signal SIGSEGV(11). Process stopped. Use Step or Run to continue."
The source:
{$ifdef WINDOWS}
uses WinCRT;
{$endif}
type TObj=object
elem:Byte;
procedure a;virtual;
procedure b;virtual;
procedure c;virtual;
end;
type
PObj=^TObj;
type
TObj2=object(TObj)
procedure a; virtual;
end;
procedure TObj.a;
begin
WriteLn('TObj.a elem=',elem);
end;
procedure TObj.b;
begin
WriteLn('TObj.b elem=',elem);
end;
procedure TObj.c;
begin
WriteLn('TObj.c elem=',elem);
end;
procedure TObj2.a;
begin
WriteLn('TObj2.a elem=',elem);
end;
const
Obj:TObj2 = (elem:1);
const
Ptr:PObj = @Obj;
begin
Obj.c;
Ptr^.c;
end. |