except.inc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2000 by Michael Van Canneyt
  4. member of the Free Pascal development team
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {****************************************************************************
  12. Exception support
  13. ****************************************************************************}
  14. Const
  15. { Type of exception. Currently only one. }
  16. FPC_EXCEPTION = 1;
  17. { types of frames for the exception address stack }
  18. cExceptionFrame = 1;
  19. cFinalizeFrame = 2;
  20. Type
  21. PExceptAddr = ^TExceptAddr;
  22. TExceptAddr = record
  23. buf : pjmp_buf;
  24. next : PExceptAddr;
  25. frametype : Longint;
  26. end;
  27. TExceptObjectClass = Class of TObject;
  28. Const
  29. CatchAllExceptions : PtrInt = -1;
  30. {$ifdef SUPPORT_THREADVAR}
  31. ThreadVar
  32. {$else SUPPORT_THREADVAR}
  33. Var
  34. {$endif SUPPORT_THREADVAR}
  35. ExceptAddrStack : PExceptAddr;
  36. ExceptObjectStack : PExceptObject;
  37. {$IFNDEF VIRTUALPASCAL}
  38. Function RaiseList : PExceptObject;
  39. begin
  40. RaiseList:=ExceptObjectStack;
  41. end;
  42. {$ENDIF}
  43. function AcquireExceptionObject: Pointer;
  44. begin
  45. If ExceptObjectStack=nil then begin
  46. AcquireExceptionObject := nil
  47. end else begin
  48. Inc(ExceptObjectStack^.refcount);
  49. AcquireExceptionObject := ExceptObjectStack^.FObject;
  50. end;
  51. end;
  52. procedure ReleaseExceptionObject;
  53. begin
  54. If ExceptObjectStack <> nil then begin
  55. if ExceptObjectStack^.refcount > 0 then begin
  56. Dec(ExceptObjectStack^.refcount);
  57. end;
  58. end;
  59. end;
  60. {$ifndef HAS_ADDR_STACK_ON_STACK}
  61. Function fpc_PushExceptAddr (Ft: Longint): PJmp_buf ;
  62. [Public, Alias : 'FPC_PUSHEXCEPTADDR'];{$ifndef NOSAVEREGISTERS}saveregisters;{$endif}
  63. {$else HAS_ADDR_STACK_ON_STACK}
  64. Function fpc_PushExceptAddr (Ft: Longint;_buf,_newaddr : pointer): PJmp_buf ;
  65. [Public, Alias : 'FPC_PUSHEXCEPTADDR'];{$ifndef NOSAVEREGISTERS}saveregisters;{$endif}{$ifdef hascompilerproc} compilerproc; {$endif}
  66. {$endif HAS_ADDR_STACK_ON_STACK}
  67. var
  68. Buf : PJmp_buf;
  69. NewAddr : PExceptAddr;
  70. begin
  71. {$ifdef excdebug}
  72. writeln ('In PushExceptAddr');
  73. {$endif}
  74. If ExceptAddrstack=Nil then
  75. begin
  76. {$ifndef HAS_ADDR_STACK_ON_STACK}
  77. New(ExceptAddrStack);
  78. {$else HAS_ADDR_STACK_ON_STACK}
  79. ExceptAddrStack:=PExceptAddr(_newaddr);
  80. {$endif HAS_ADDR_STACK_ON_STACK}
  81. ExceptAddrStack^.Next:=Nil;
  82. end
  83. else
  84. begin
  85. {$ifndef HAS_ADDR_STACK_ON_STACK}
  86. New(NewAddr);
  87. {$else HAS_ADDR_STACK_ON_STACK}
  88. NewAddr:=PExceptAddr(_newaddr);
  89. {$endif HAS_ADDR_STACK_ON_STACK}
  90. NewAddr^.Next:=ExceptAddrStack;
  91. ExceptAddrStack:=NewAddr;
  92. end;
  93. {$ifndef HAS_ADDR_STACK_ON_STACK}
  94. new(buf);
  95. {$else HAS_ADDR_STACK_ON_STACK}
  96. buf:=PJmp_Buf(_buf);
  97. {$endif HAS_ADDR_STACK_ON_STACK}
  98. ExceptAddrStack^.Buf:=Buf;
  99. ExceptAddrStack^.FrameType:=ft;
  100. fpc_PushExceptAddr:=Buf;
  101. end;
  102. Procedure fpc_PushExceptObj (Obj : TObject; AnAddr,AFrame : Pointer);
  103. [Public, Alias : 'FPC_PUSHEXCEPTOBJECT'];{$ifndef NOSAVEREGISTERS}saveregisters;{$endif}{$ifdef hascompilerproc} compilerproc; {$endif}
  104. var
  105. Newobj : PExceptObject;
  106. framebufsize,
  107. framecount : longint;
  108. frames : PPointer;
  109. prev_frame,
  110. curr_frame,
  111. caller_frame,
  112. caller_addr : Pointer;
  113. begin
  114. {$ifdef excdebug}
  115. writeln ('In PushExceptObject');
  116. {$endif}
  117. If ExceptObjectStack=Nil then
  118. begin
  119. New(ExceptObjectStack);
  120. ExceptObjectStack^.Next:=Nil;
  121. end
  122. else
  123. begin
  124. New(NewObj);
  125. NewObj^.Next:=ExceptObjectStack;
  126. ExceptObjectStack:=NewObj;
  127. end;
  128. ExceptObjectStack^.FObject:=Obj;
  129. ExceptObjectStack^.Addr:=AnAddr;
  130. ExceptObjectStack^.refcount:=0;
  131. { Backtrace }
  132. curr_frame:=AFrame;
  133. prev_frame:=get_frame;
  134. frames:=nil;
  135. framebufsize:=0;
  136. framecount:=0;
  137. while (framecount<RaiseMaxFrameCount) and (curr_frame > prev_frame) and
  138. (curr_frame<(StackBottom + StackLength)) do
  139. Begin
  140. caller_addr := get_caller_addr(curr_frame);
  141. caller_frame := get_caller_frame(curr_frame);
  142. if (caller_addr=nil) or
  143. (caller_frame=nil) then
  144. break;
  145. if (framecount>=framebufsize) then
  146. begin
  147. inc(framebufsize,16);
  148. reallocmem(frames,framebufsize*sizeof(pointer));
  149. end;
  150. frames[framecount]:=caller_addr;
  151. inc(framecount);
  152. prev_frame:=curr_frame;
  153. curr_frame:=caller_frame;
  154. End;
  155. ExceptObjectStack^.framecount:=framecount;
  156. ExceptObjectStack^.frames:=frames;
  157. end;
  158. {$ifdef hascompilerproc}
  159. { make it avalable for local use }
  160. Procedure fpc_PushExceptObj (Obj : TObject; AnAddr,AFrame : Pointer); [external name 'FPC_PUSHEXCEPTOBJECT'];
  161. {$endif}
  162. Procedure DoUnHandledException;
  163. begin
  164. If (ExceptProc<>Nil) and (ExceptObjectStack<>Nil) then
  165. with ExceptObjectStack^ do
  166. TExceptProc(ExceptProc)(FObject,Addr,FrameCount,Frames);
  167. RunError(217);
  168. end;
  169. Function fpc_Raiseexception (Obj : TObject; AnAddr,AFrame : Pointer) : TObject;[Public, Alias : 'FPC_RAISEEXCEPTION']; {$ifdef hascompilerproc} compilerproc; {$endif}
  170. begin
  171. {$ifdef excdebug}
  172. writeln ('In RaiseException');
  173. {$endif}
  174. fpc_Raiseexception:=nil;
  175. fpc_PushExceptObj(Obj,AnAddr,AFrame);
  176. If ExceptAddrStack=Nil then
  177. DoUnhandledException;
  178. if (RaiseProc <> nil) and (ExceptObjectStack <> nil) then
  179. with ExceptObjectStack^ do
  180. RaiseProc(FObject,Addr,FrameCount,Frames);
  181. longjmp(ExceptAddrStack^.Buf^,FPC_Exception);
  182. end;
  183. Procedure fpc_PopAddrStack;[Public, Alias : 'FPC_POPADDRSTACK']; {$ifdef hascompilerproc} compilerproc; {$endif}
  184. {$ifndef HAS_ADDR_STACK_ON_STACK}
  185. var
  186. hp : PExceptAddr;
  187. {$endif HAS_ADDR_STACK_ON_STACK}
  188. begin
  189. {$ifdef excdebug}
  190. writeln ('In Popaddrstack');
  191. {$endif}
  192. If ExceptAddrStack=nil then
  193. begin
  194. writeln ('At end of ExceptionAddresStack');
  195. halt (255);
  196. end
  197. else
  198. begin
  199. {$ifndef HAS_ADDR_STACK_ON_STACK}
  200. hp:=ExceptAddrStack;
  201. ExceptAddrStack:=ExceptAddrStack^.Next;
  202. dispose(hp^.buf);
  203. dispose(hp);
  204. {$else HAS_ADDR_STACK_ON_STACK}
  205. ExceptAddrStack:=ExceptAddrStack^.Next;
  206. {$endif HAS_ADDR_STACK_ON_STACK}
  207. end;
  208. end;
  209. function fpc_PopObjectStack : TObject;[Public, Alias : 'FPC_POPOBJECTSTACK']; {$ifdef hascompilerproc} compilerproc; {$endif}
  210. var
  211. hp : PExceptObject;
  212. begin
  213. {$ifdef excdebug}
  214. writeln ('In PopObjectstack');
  215. {$endif}
  216. If ExceptObjectStack=nil then
  217. begin
  218. writeln ('At end of ExceptionObjectStack');
  219. halt (1);
  220. end
  221. else
  222. begin
  223. { we need to return the exception object to dispose it }
  224. if ExceptObjectStack^.refcount = 0 then begin
  225. fpc_PopObjectStack:=ExceptObjectStack^.FObject;
  226. end else begin
  227. fpc_PopObjectStack:=nil;
  228. end;
  229. hp:=ExceptObjectStack;
  230. ExceptObjectStack:=ExceptObjectStack^.next;
  231. if assigned(hp^.frames) then
  232. freemem(hp^.frames);
  233. dispose(hp);
  234. end;
  235. end;
  236. { this is for popping exception objects when a second exception is risen }
  237. { in an except/on }
  238. function fpc_PopSecondObjectStack : TObject;[Public, Alias : 'FPC_POPSECONDOBJECTSTACK']; {$ifdef hascompilerproc} compilerproc; {$endif}
  239. var
  240. hp : PExceptObject;
  241. begin
  242. {$ifdef excdebug}
  243. writeln ('In PopObjectstack');
  244. {$endif}
  245. If not(assigned(ExceptObjectStack)) or
  246. not(assigned(ExceptObjectStack^.next)) then
  247. begin
  248. writeln ('At end of ExceptionObjectStack');
  249. halt (1);
  250. end
  251. else
  252. begin
  253. if ExceptObjectStack^.next^.refcount=0 then
  254. { we need to return the exception object to dispose it if refcount=0 }
  255. fpc_PopSecondObjectStack:=ExceptObjectStack^.next^.FObject
  256. else
  257. fpc_PopSecondObjectStack:=nil;
  258. hp:=ExceptObjectStack^.next;
  259. ExceptObjectStack^.next:=hp^.next;
  260. if assigned(hp^.frames) then
  261. freemem(hp^.frames);
  262. dispose(hp);
  263. end;
  264. end;
  265. Procedure fpc_ReRaise;[Public, Alias : 'FPC_RERAISE']; {$ifdef hascompilerproc} compilerproc; {$endif}
  266. begin
  267. {$ifdef excdebug}
  268. writeln ('In reraise');
  269. {$endif}
  270. If ExceptAddrStack=Nil then
  271. DoUnHandledException;
  272. ExceptObjectStack^.refcount := 0;
  273. longjmp(ExceptAddrStack^.Buf^,FPC_Exception);
  274. end;
  275. Function fpc_Catches(Objtype : TClass) : TObject;[Public, Alias : 'FPC_CATCHES']; {$ifdef hascompilerproc} compilerproc; {$endif}
  276. var
  277. _Objtype : TExceptObjectClass;
  278. begin
  279. If ExceptObjectStack=Nil then
  280. begin
  281. Writeln ('Internal error.');
  282. halt (255);
  283. end;
  284. _Objtype := TExceptObjectClass(Objtype);
  285. if Not ((_Objtype = TExceptObjectClass(CatchAllExceptions)) or
  286. (ExceptObjectStack^.FObject is _ObjType)) then
  287. fpc_Catches:=Nil
  288. else
  289. begin
  290. // catch !
  291. fpc_Catches:=ExceptObjectStack^.FObject;
  292. { this can't be done, because there could be a reraise (PFV)
  293. PopObjectStack;
  294. Also the PopAddrStack shouldn't be done, we do it now
  295. immediatly in the exception handler (FK)
  296. PopAddrStack; }
  297. end;
  298. end;
  299. Procedure fpc_DestroyException(o : TObject);[Public, Alias : 'FPC_DESTROYEXCEPTION']; {$ifdef hascompilerproc} compilerproc; {$endif}
  300. begin
  301. { with free we're on the really save side }
  302. o.Free;
  303. end;
  304. Procedure SysInitExceptions;
  305. {
  306. Initialize exceptionsupport
  307. }
  308. begin
  309. ExceptObjectstack:=Nil;
  310. ExceptAddrStack:=Nil;
  311. end;
  312. {
  313. $Log: except.inc,v $
  314. Revision 1.21 2005/05/08 21:20:26 michael
  315. + Patch to return nil if there is no exception object (as in Delphi)
  316. Revision 1.20 2005/04/03 11:32:05 florian
  317. * ref. counting for popping second exceptiono object fixed
  318. Revision 1.19 2005/02/14 17:13:22 peter
  319. * truncate log
  320. Revision 1.18 2005/01/29 17:01:18 peter
  321. * fix crash with backtrace if invalid frame is passed
  322. Revision 1.17 2005/01/26 17:07:10 peter
  323. * retrieve backtrace when exception is raised
  324. * RaiseMaxFrameCount added to limit the number of backtraces, setting
  325. it to 0 disables backtraces. Default is 16
  326. }