On 10/20/25 3:29 PM, Martin Frb via fpc-devel wrote:
On 17/10/2025 21:15, Sven Barth via fpc-devel wrote:
What is the best way to check if a virtual function in a base class
has been overridden by a class that inherits the base class?
In LCL TPrinter.pas the following check is used:
procedure TPrinter.NewPage;
begin
Inc(fPageNumber);
if TMethod(@Self.DoNewPage).Code = Pointer(@TPrinter.DoNewPage) then
begin
..
end
..
end
We have run across an optimization problem with the TCocoaPrinter
class and that TPrinter code.
TCocoaPrinter = class(TPrinter)
with no override of DeNewPage
David's problem is *not* due to a corruction or a bug, but due to an
optimization that FPC performs that leads to different behavior,
namely changing virtual methods that are empty to EmptyMethod to
reduce the number of duplicate (empty) methods in the binary.
I am trying to figure out under which circumstances this would happen?
TPrinter = class...
procedure DoNewPage; virtual;
And the check really only makes sense for virtual methods. Well (in
generics that differs, but for non virtual, both classes are known at
compiletime).
So if it is a virtual method:
If the compiler optimizes @TPrinter.DoNewPage to EmptyMethod, then it
would put that into the class definition?
So that is into the VMT?
And there is only one VMT?
And self is a pointer (to pointer) to that one VMT?
So then how can one be replaced, but the other not?
He says "with no override of DeNewPage". So he expects them both to be
the same.
I could see the opposite fail. If there was on overwritten method, and
that was also empty, then the different overwritten empty method would
(falsely) report as the same...
But then reporting as different?
Or does the compiler only update the VMT, but does report
@TPrinter.DoNewPage to the original code?
If that was the case, would/should that not be considered a bug. If the
compiler replaced it, then it should report it for the original too.
If @TPrinter.DoNewPage still reports the original code (and NOT
emptyproc) then that forces the original code to be included too (even
though the whole optimization was to NOT include it).
Because you could then do:
MyMeth := @TPrinter.DoNewPage;
MyMeth();
and call the original code.
_______________________________________________
fpc-devel maillist - [email protected]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel
To me it looks like TPrinter.DoNewPage is not updated to
System.EmptyMethod as part of the optimization. Which is why the check
fails.
My local fix for now is to use
if (TMethod(@Self.DoNewPage).Code = Pointer(@TPrinter.DoNewPage)) or
(TMethod(@Self.DoNewPage).Code = System.EmptyMethod) then
begin
....
end;
David
_______________________________________________
fpc-devel maillist - [email protected]
https://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-devel