The magic Delphi ReturnAddress intrinsic
Posted by jpluimers on 2020/02/05
I could not find any official place where ReturnAddress
is documented even though it is used at quite a few places in the RTL, VCL, FMX and 3rd party libraries like DUnitX, Spring4D, JCL, ReportBuilder, TeeChart.
I tried searching it in the contexts of Delphi 2007, Delphi 2009, but there is only a [Archive.is] different System.ObjAuto.TParameters.ReturnAddress
documented in XE2 and higher.
procedure Abort; begin raise EAbort.CreateRes(@SOperationAborted) at ReturnAddress; end;
There is a (usually broken*) ReturnAddr
function used in various places of the RTL/VCL/FMX and (therefore wrongfully copied) in many other peoples code.
function ReturnAddr: Pointer; // From classes.pas asm MOV EAX,[EBP+4] // sysutils.pas says [EBP-4], but this works ! end;
- See the above link; I think this was fixed in Delphi XE, but the issue is still open.
Related to the above is the documented ExceptAddr
.
I’ve used this in my ExceptionHelperUnit
to build a GetStackTrace
function in the example gist below.
I found these posts documenting the behaviour of the above intrinsic functions and more:
- [WayBack] Undocumented Delphi routines – Chee Wee’s blog: IT solutions for Singapore and companies worldwide
- [WayBack] delphi – Undocumented intrinsic routines – Stack Overflow
- [WayBack] exception – What does `at ReturnAddress` mean in Delphi? – Stack Overflow
–jeroen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
unit ExceptionHelperUnit; | |
interface | |
uses | |
System.SysUtils; | |
type | |
ExceptionHelper = class helper for Exception | |
public | |
function Describe: string; | |
class procedure RaiseNotImplementedException(const aClass: TClass; const aMethodName: string); | |
class function GetStackTrace: string; | |
end; | |
implementation | |
uses | |
System.RTLConsts, | |
System.SysConst; | |
type | |
EStackTraceException = class(Exception); // EProgrammerNotFound to make it really clear this is only to be used in very limited places ?? | |
{ ExceptionHelper } | |
function ExceptionHelper.Describe: string; | |
var | |
lStackTrace: string; | |
begin | |
Result := inherited ToString(); | |
if Self is EInOutError then | |
if Result = System.RTLConsts.SInvalidFileName then | |
Result := System.SysConst.SInvalidFileName; | |
if Assigned(StackInfo) then | |
lStackTrace := StackTrace | |
else | |
lStackTrace := 'empty'; | |
Result := Format('Exception'#13#10'%s at $%p: %s'#13#10'with StackTrace'#13#10'%s', [ClassName, ExceptAddr, Result, lStackTrace]); | |
end; | |
class function ExceptionHelper.GetStackTrace: string; | |
begin | |
try | |
Result := 'Get StackTrace via Exception.'; | |
raise EStackTraceException.Create(Result) at ReturnAddress; | |
except | |
on E: EStackTraceException do | |
Result := E.StackTrace; | |
end; | |
end; | |
class procedure ExceptionHelper.RaiseNotImplementedException(const aClass: TClass; const aMethodName: string); | |
begin | |
raise ENotImplemented.CreateFmt('Method %s.%s is not implemented.', [aClass.ClassName, aMethodName]); | |
end; | |
end. |
Leave a Reply