Discussion:
[fpc-pascal] access violation?
Ryan Joseph
2018-07-27 02:44:23 UTC
Permalink
I was doing some testing today (Mac) and discovered that calling Free on a nil object didn’t cause a EAccessViolation exception. Why doesn’t that crash?

var
c: TObject;
begin
c := nil;
c.Free;



Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://list
Michael Van Canneyt
2018-07-27 05:36:57 UTC
Permalink
I was doing some testing today (Mac) and discovered that calling Free on a nil object didn’t cause a EAccessViolation exception. Why doesn’t that crash?
It is by design.

And - OMG !! - it is even documented:

https://www.freepascal.org/docs-html/current/rtl/system/tobject.free.html

Michael.
Victor Campillo
2018-07-27 06:00:37 UTC
Permalink
Post by Michael Van Canneyt
It is by design.
https://www.freepascal.org/docs-html/current/rtl/system/tobject.free.html
There is no need to search the docs, it is even more quick just jump to
the implementation of free and see what it does :)
--
Victor Campillo

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepa
Ryan Joseph
2018-07-27 15:15:45 UTC
Permalink
Post by Michael Van Canneyt
It is by design.
I never use Free directly so I had no idea. How is that even accomplished in the language? I thought it was just a method and behaved accordingly.

If it is magic then how do we call other methods on nil objects? There are times that would be handy if I could control it.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freep
Dmitry Boyarintsev
2018-07-27 16:16:54 UTC
Permalink
Post by Ryan Joseph
I never use Free directly so I had no idea. How is that even accomplished
in the language? I thought it was just a method and behaved accordingly.
If it is magic then how do we call other methods on nil objects? There are
times that would be handy if I could control it.
It's not a magic, it's explicit check for the value of "Self" within a
method.
Self would point to an actual instance of an object, if it's nil. Free
method won't do anything.

type
TRobust = class(TObject)
public
v : Integer;
procedure RobustMethod;
end;

procedure TRobust.RobustMethod;
begin
if Self = nil then Exit; // remove or comment out this line to start
getting AVs
v := 5;
end;

var
r : TRobust;
begin
r := nil;
r.RobustMethod;
end.

You could consider this a sanity check, similar to checking any other input
parameters.

thanks,
Dmitry
Ryan Joseph
2018-07-27 17:06:11 UTC
Permalink
Post by Dmitry Boyarintsev
type
TRobust = class(TObject)
public
v : Integer;
procedure RobustMethod;
end;
procedure TRobust.RobustMethod;
begin
if Self = nil then Exit; // remove or comment out this line to start getting AVs
v := 5;
end;
var
r : TRobust;
begin
r := nil;
r.RobustMethod;
end.
I had no idea you could do that!

I’m totally confused now. Since when was it ok to call methods on nil objects? According to this test not only can you call methods on nil objects but it calls the method statically (like a class method), i.e the test prints “DoThis”. How is that possible?

type
TMyClass = class
procedure DoThis;
end;

procedure TMyClass.DoThis;
begin
writeln('DoThis');
end;

var
obj: TMyClass;
begin
obj := nil;
obj.DoThis;


Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.f
Mattias Gaertner
2018-07-27 17:24:39 UTC
Permalink
On Fri, 27 Jul 2018 11:06:11 -0600
Post by Ryan Joseph
[...]
I’m totally confused now. Since when was it ok to call methods on nil objects?
You can't if you compile with -CR.
The RTL is not compiled with objectchecks, so it works there.
Post by Ryan Joseph
According to this test not only can you call methods on nil objects but
it calls the method statically (like a class method), i.e the test
prints “DoThis”. How is that possible?
Self on x86_64 target is just a hidden argument.
It won't work with virtual methods.

Mattias

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepasca
Ryan Joseph
2018-07-27 17:41:51 UTC
Permalink
Post by Mattias Gaertner
You can't if you compile with -CR.
The RTL is not compiled with objectchecks, so it works there.
Post by Ryan Joseph
According to this test not only can you call methods on nil objects but
it calls the method statically (like a class method), i.e the test
prints “DoThis”. How is that possible?
Self on x86_64 target is just a hidden argument.
It won't work with virtual methods.
This is all news to me. I had no idea I was possibly compiling programs where I could call nil objects.

How do I disable this? I just ran this test program with -CR and I still calls the method statically.

Ryans-MacBook-Pro-2:~ ryanjoseph$ fpc -CR /Users/ryanjoseph/Desktop/macro_test/obj_test.pas
Free Pascal Compiler version 3.0.4 [2017/11/26] for x86_64
Copyright (c) 1993-2017 by Florian Klaempfl and others
Target OS: Darwin for x86_64
Compiling /Users/ryanjoseph/Desktop/macro_test/obj_test.pas
Assembling (pipe) /Users/ryanjoseph/Desktop/macro_test/obj_test.s
Linking /Users/ryanjoseph/Desktop/macro_test/obj_test
19 lines compiled, 0.1 sec
Ryans-MacBook-Pro-2:~ ryanjoseph$ /Users/ryanjoseph/Desktop/macro_test/obj_test
DoThis


{$mode objfpc}

program obj_test;

type
TMyClass = class
procedure DoThis;
end;

procedure TMyClass.DoThis;
begin
writeln('DoThis');
end;

var
obj: TMyClass;
begin
obj := nil;
obj.DoThis;
end.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinf
Mattias Gaertner
2018-07-27 20:17:58 UTC
Permalink
On Fri, 27 Jul 2018 11:41:51 -0600
Post by Ryan Joseph
[...]
How do I disable this? I just ran this test program with -CR and I still calls the method statically.
You are right. I just tested myself.
Either I'm confusing the flag or it has been changed.
Once upon a time Lazarus used the same trick and I had to change the
code as some users wanted use the flag.

Mattias
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pasc
Ryan Joseph
2018-07-27 20:25:41 UTC
Permalink
Post by Mattias Gaertner
You are right. I just tested myself.
Either I'm confusing the flag or it has been changed.
Once upon a time Lazarus used the same trick and I had to change the
code as some users wanted use the flag.
Do you need me to file a bug report? This caused me a big headache this morning that I would like to avoid in the future. Off the top of my head I’m not aware of any time I’d want a call to nil to not fire an exception, except for calling Free perhaps since this is the documented behavior.

Thanks for straitening this out for me, I thought I was losing my mind. :)

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pa
Sven Barth via fpc-pascal
2018-07-28 08:03:26 UTC
Permalink
Post by Ryan Joseph
Post by Mattias Gaertner
You can't if you compile with -CR.
The RTL is not compiled with objectchecks, so it works there.
Post by Ryan Joseph
According to this test not only can you call methods on nil objects but
it calls the method statically (like a class method), i.e the test
prints “DoThis”. How is that possible?
Self on x86_64 target is just a hidden argument.
It won't work with virtual methods.
This is all news to me. I had no idea I was possibly compiling programs where I could call nil objects.
Normal, non-virtual methods are essentially ordinary
functions/procedures. In contrast to static functions/procedures they
have a hidden parameter that contains the value of Self. As Self is
merely a pointer there is no problem if Self should be Nil as long as it
isn't dereferenced (which happens if you access a field or a call a
virtual method).
Calling a virtual method would fail immediately.
Post by Ryan Joseph
How do I disable this? I just ran this test program with -CR and I still calls the method statically.
The method is always called statically, -CR doesn't change anything
there. Calling a virtual method however would fail with EObjectCheck
instead of EAccessViolation.
The method you provided below would not fail anyway, because it doesn't
dereference Self. If you'd however access for example a field of your
class then a check should be inserted inside the method. For some reason
it isn't however and *that* is the bug.

Regards,
Sven
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org
Ryan Joseph
2018-07-30 15:33:13 UTC
Permalink
The method you provided below would not fail anyway, because it doesn't dereference Self. If you'd however access for example a field of your class then a check should be inserted inside the method. For some reason it isn't however and *that* is the bug.
Just getting back to this now.

Thanks for the description, I didn’t know FPC behaved that way but it’s good to know.

However, I’ve never had a use for this (outside of calling Free as I learned and the way I’ve always programmed Pascal, accessing nil is an error or undefined behavior so I avoid it meticulously.

How can I disable this so calling a method on nil gives an error?

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-
Sven Barth via fpc-pascal
2018-07-31 05:08:08 UTC
Permalink
On Jul 28, 2018, at 2:03 AM, Sven Barth via fpc-pascal <
The method you provided below would not fail anyway, because it doesn't
dereference Self. If you'd however access for example a field of your class
then a check should be inserted inside the method. For some reason it isn't
however and *that* is the bug.
Just getting back to this now.
Thanks for the description, I didn’t know FPC behaved that way but it’s
good to know.
However, I’ve never had a use for this (outside of calling Free as I
learned and the way I’ve always programmed Pascal, accessing nil is an
error or undefined behavior so I avoid it meticulously.
How can I disable this so calling a method on nil gives an error?
If the method doesn't access Self then there is nothing you can do.

Regards,
Sven
Ryan Joseph
2018-07-31 15:45:20 UTC
Permalink
Post by Sven Barth via fpc-pascal
If the method doesn't access Self then there is nothing you can do.
Is this something the compiler team would be interested in adding? I’d like to get an error when calling methods on nil. It’s seems like pretty basic type safety stuff to me.

Regards,
Ryan Joseph

_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cg
Sven Barth via fpc-pascal
2018-08-01 05:46:03 UTC
Permalink
On Jul 30, 2018, at 11:08 PM, Sven Barth via fpc-pascal <
If the method doesn't access Self then there is nothing you can do.
Is this something the compiler team would be interested in adding? I’d
like to get an error when calling methods on nil. It’s seems like pretty
basic type safety stuff to me.
No, because calling methods like Free would break as well and there is
enough code out there that relies on that behavior. Also for methods that
don't access Self it really is not important whether Self is valid or not.
And for the other cases (which are the majority btw) there is functionality
for that even if it is currently buggy.

Regards,
Sven
Dmitry Boyarintsev
2018-07-27 18:54:08 UTC
Permalink
Post by Ryan Joseph
I had no idea you could do that!
Obviously, it speaks high of your abilities ;)
You've been able to accomplish your tasks without use of hacks (of any
kind), playing strict by the rules.


I’m totally confused now. Since when was it ok to call methods on nil
Post by Ryan Joseph
objects? According to this test not only can you call methods on nil
objects but it calls the method statically (like a class method), i.e the
test prints “DoThis”. How is that possible?
From the low-level (virtual/physical memory, CPU) perspective you can do
that at any time on either nil-ed or uninitialized object as long as the
method doesn't try to access and invalid memory.

The example (compiled for i386 and not using -CR works as expected)

type
TRobust = class(TObject)
public
v : Integer;
function Max(a,b: Integer): Integer;
end;

function TRobust.Max(a,b: Integer): Integer;
begin
if a>b then Result:=a
else Result:=b;
end;

var
r : TRobust;
begin
r := nil;
writeln(r.Max(5,10));
end.

However such behavior, would be RTL specific - it's all about how the
compiler would generate such call. If it would attempt to access any field
(i.e. VMT) related to the instance of the object itself (rather than the
class), there's a high chance it would fail.

And that's why having -CR enabled during development is generally a very
good idea.

From high-level (OOP) such actions are not welcomed (and are enforced using
-CR in case of FPC).

In order to have robust, maintainable and portable (to either other
platform or even language) a developer should respect high-level rules and
never depend on low-level rules to be the same on any platform.

thanks,
Dmitry
Michael Van Canneyt
2018-07-27 19:22:24 UTC
Permalink
Post by Dmitry Boyarintsev
Post by Dmitry Boyarintsev
From high-level (OOP) such actions are not welcomed (and are enforced using
-CR in case of FPC).
In order to have robust, maintainable and portable (to either other
platform or even language) a developer should respect high-level rules and
never depend on low-level rules to be the same on any platform.
Do I have permission to quote you the next time someone starts about how
the lifetimes of interfaces are managed slightly differently in FPC and Delphi ?

:)

Michael.
_______________________________________________
fpc-pascal maillist - fpc-***@lists.freepascal.org
http://lists.freepascal.org/cgi-bin/mailman
Dmitry Boyarintsev
2018-07-27 19:50:27 UTC
Permalink
Post by Dmitry Boyarintsev
In order to have robust, maintainable and portable (to either other
Post by Dmitry Boyarintsev
platform or even language) a developer should respect high-level rules and
never depend on low-level rules to be the same on any platform.
Do I have permission to quote you the next time someone starts about how
the lifetimes of interfaces are managed slightly differently in FPC and Delphi ?
:)
Permission granted.
I thought all (COM) interfaces have been resolved, back in 2.6?
Loading...