Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does `at ReturnAddress` mean in Delphi?

While browsing System.Zip (Delphi XE2) to see how it works, I found this function:

procedure VerifyWrite(Stream: TStream; var Buffer; Count: Integer);
begin
  if Stream.Write(Buffer, Count) <> Count then
    raise EZipException.CreateRes(@SZipErrorWrite) at ReturnAddress;
end;

It's the at ReturnAddress part that sort of puzzles me.

I didn't know that at was a valid keyword (the syntax highlighter doesn't seem to recognise it either).

According to the IDE it's declared as System.ReturnAddress, but I can only find it declared as a label somewhere in the (asm) code of procedure _HandleAnyException;. The system unit is full of references to it though.

So what I would like to know is this:

  1. What is ReturnAddress?
  2. What exactly does Raise Exception.Create ... at ReturnAddress do?

Bonuspoints if you can give a real-world example of where this would be a useful construct, or if you can advice against using it.

like image 917
Wouter van Nifterick Avatar asked Jan 21 '12 03:01

Wouter van Nifterick


2 Answers

ReturnAddress is the address to which VerifyWrite would have returned when finished.

Raise Exception.Create... at ReturnAddress means that when the exception dialog is displayed, it would indicate the address of the exception as being at ReturnAddress. In other words, the exception message would read Exception <whatever> raised at <ReturnAddress>: <Exception Message>.

Here is an excerpt from the help file for Delphi 7. It's nearly the same as the online version.

To raise an exception object, use an instance of the exception class with a raise statement. For example,

raise EMathError.Create;

In general, the form of a raise statement is

raise object at address

where object and at address are both optional; see Re-raising exceptions. When an address is specified, it can be any expression that evaluates to a pointer type, but is usually a pointer to a procedure or function. For example:

raise Exception.Create('Missing parameter') at @MyFunction;

Use this option to raise the exception from an earlier point in the stack than the one where the error actually occurred.

Note the last sentence in particular. It's pretty specific about the use of at <address>.

like image 136
Ken White Avatar answered Oct 31 '22 01:10

Ken White


ReturnAddr was not a puzzle with previous Delphi versions. Consider next test (Delphi XE):

procedure RaiseTest1;

  procedure RaiseException(ReturnAddr: Pointer);
  begin
    raise Exception.Create('OOPS!') at ReturnAddr;
  end;

asm
      POP    EAX
      JMP    RaiseException
end;

procedure RaiseTest2;
begin
  raise Exception.Create('OOPS!');
end;


procedure TForm1.Button3Click(Sender: TObject);
begin
  RaiseTest1;
end;

procedure TForm1.Button4Click(Sender: TObject);
begin
  RaiseTest2;
end;

if you press Button3 under debugger and press 'Break' in exception messagebox, debugger stops at

procedure TForm1.Button3Click(Sender: TObject);
begin
  RaiseTest1; // <-- here
end;

if you press Button4, debugger stops at

procedure RaiseTest2;
begin
  raise Exception.Create('OOPS!');  // <-- here
end;

As you can see RaiseTest1 modifies default exception stack frame and makes debugging a bit more straightforward since the only purpose of RaiseTest1(2) procedures is to raise an exception.

I guess something changed in XE2 so that ReturnAddr syntax is simplified.

like image 30
kludg Avatar answered Oct 31 '22 00:10

kludg