Accessing a class method by casting the object reference to nil causes segfault
Original Reporter info from Mantis: Michalis @michaliskambi
-
Reporter name: Michalis Kamburelis
Original Reporter info from Mantis: Michalis @michaliskambi
- Reporter name: Michalis Kamburelis
Description:
Assuming you have these types:
TMyMethod = procedure (A: Integer) of object;
TMyClass = class
class procedure Foo(A: Integer);
end;
When you want to get a pointer to the class procedure/function, without creating the class instance, in the objfpc mode you have to do:
@TMyClass(nil).Foo
This looks ugly, but used to work in FPC versions <= 3.0.0 (at least all stable 2.6.x and 3.0.0). It is also the only way (as far as I know) to get the address of Foo to pass it to other routines (as TMyMethod) without creating the instance of TMyClass. Which seems like a reasonable thing to want (Foo is a class procedure, so it should be able to work just fine without the class instance).
FPC 3.0.0 allows this both in objfpc and delphi modes, and it works fine there. In delphi mode, you need to remove the "@" of course.
In current trunk, this fails. It fails with a segmentation fault, unless compiled with -CR in which case it fails with a clear "EObjectCheck: Object reference is Nil" exception (if you use SysUtils, that converts runtime error code to EObjectCheck).
In delphi mode, the fix is simple: change "TMyClass(nil).Foo" to "TMyClass.Foo". This looks much cleaner, and also works.
In objfpc mode, there's no way out of this. Creating a temporary TMyClass instance just to access it's class procedure would be quite uncomfortable in some of my usecases: it takes time at runtime, and requires additional dummy code, that is conceptually useless: the Foo is a class procedure, so we know it doesn't access the class instance stuff.
There are 2 things to be fixed in FPC, ideally:
-
Enable in objfpc the same construction as in delphi mode: to use "Foo" as class procedure without "(nil)" cast to nil. So "@TMyClass.Foo" should work in objfpc, just like "TMyClass.Foo" works in delphi mode.
-
For backward compatibility it would be nice to keep "@TMyClass(nil).Foo" working. Especially since we need to write code that compiles with both FPC <= 3.0.0 and 3.1.1, so (even if point 1 is done) we cannot just change the code everywhere to use "@TMyClass.Foo", as it will not work in FPC <= 3.0.0.
Attaching a testcase.
$ fpc using_class_function_pointers.lpr
$ ./using_class_function_pointers
An unhandled exception occurred at $0000000000400273:
EAccessViolation: Access violation
$0000000000400273
$ fpc -CR using_class_function_pointers.lpr
$ ./using_class_function_pointers
An unhandled exception occurred at $0000000000400288:
EObjectCheck: Object reference is Nil
$0000000000400288
Mantis conversion info:
- Mantis ID: 30706
- OS: Debian GNU/Linux
- OS Build: (testing)
- Build: 34637
- Platform: x86-64
- Version: 3.1.1
- Fixed in version: 3.1.1
- Fixed in revision: 34641 (#23362215)