Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2018-07

access violation?

18 messages · 6 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Ryan Joseph

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

Ryan Joseph
to Michael Van Canneyt: It is by design. And - OMG !! - it is even documented:…
Show quoted text 5 lines
On Jul 26, 2018, at 11:36 PM, Michael Van Canneyt wrote: It is by design. And - OMG !! - it is even documented:

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

Dmitry Boyarintsev
to Ryan Joseph
Ryan Joseph wrote 5 lines
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
to Dmitry Boyarintsev
Show quoted text 22 lines
On Jul 27, 2018, at 10:16 AM, Dmitry Boyarintsev wrote: 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

Mattias Gaertner
to 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.

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

Ryan Joseph
to Mattias Gaertner
Show quoted text 11 lines
On Jul 27, 2018, at 11:24 AM, Mattias Gaertner wrote: You can't if you compile with -CR. The RTL is not compiled with objectchecks, so it works there. > 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

Dmitry Boyarintsev
to Ryan Joseph: I had no idea you could do that! I’m totally confused now. Since when was it ok to call…
Ryan Joseph wrote
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

Show quoted text 6 lines
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
to Dmitry Boyarintsev
Dmitry Boyarintsev wrote 6 lines
> 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.

Dmitry Boyarintsev
to Michael Van Canneyt

On Fri, Jul 27, 2018 at 3:22 PM, Michael Van Canneyt <

Show quoted text 12 lines
wrote: 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 ? :)

Permission granted.
I thought all (COM) interfaces have been resolved, back in 2.6?

Ryan Joseph
to Mattias Gaertner
Show quoted text 6 lines
On Jul 27, 2018, at 2:17 PM, Mattias Gaertner wrote: 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

Sven Barth
to Ryan Joseph: This is all news to me. I had no idea I was possibly compiling programs where I could…
Ryan Joseph wrote 12 lines
> On Jul 27, 2018, at 11:24 AM, Mattias Gaertner wrote: > > You can't if you compile with -CR. > The RTL is not compiled with objectchecks, so it works there. > >> 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.

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

Ryan Joseph
to Sven Barth
On Jul 28, 2018, at 2:03 AM, Sven Barth via fpc-pascal wrote: 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

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Mo., 30. Juli 2018,
18:05:

Show quoted text 21 lines
> On Jul 28, 2018, at 2:03 AM, Sven Barth via fpc-pascal < > wrote: > > 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
to Sven Barth
On Jul 30, 2018, at 11:08 PM, Sven Barth via fpc-pascal wrote: 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

Sven Barth
to Ryan Joseph

Ryan Joseph schrieb am Di., 31. Juli 2018,
18:15:

Show quoted text 11 lines
> On Jul 30, 2018, at 11:08 PM, Sven Barth via fpc-pascal < > wrote: > > 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