except.inc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by Michael Van Canneyt
  5. member of the Free Pascal development team
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {****************************************************************************
  13. Exception support
  14. ****************************************************************************}
  15. Const
  16. { Type of exception. Currently only one. }
  17. FPC_EXCEPTION = 1;
  18. { types of frames for the exception address stack }
  19. cExceptionFrame = 1;
  20. cFinalizeFrame = 2;
  21. Type
  22. PExceptAddr = ^TExceptAddr;
  23. TExceptAddr = record
  24. buf : pjmp_buf;
  25. next : PExceptAddr;
  26. frametype : Longint;
  27. end;
  28. TExceptObjectClass = Class of TObject;
  29. Const
  30. CatchAllExceptions : PtrInt = -1;
  31. {$ifdef SUPPORT_THREADVAR}
  32. ThreadVar
  33. {$else SUPPORT_THREADVAR}
  34. Var
  35. {$endif SUPPORT_THREADVAR}
  36. ExceptAddrStack : PExceptAddr;
  37. ExceptObjectStack : PExceptObject;
  38. {$IFNDEF VIRTUALPASCAL}
  39. Function RaiseList : PExceptObject;
  40. begin
  41. RaiseList:=ExceptObjectStack;
  42. end;
  43. {$ENDIF}
  44. function AcquireExceptionObject: Pointer;
  45. begin
  46. If ExceptObjectStack=nil then begin
  47. AcquireExceptionObject := nil
  48. end else begin
  49. Inc(ExceptObjectStack^.refcount);
  50. AcquireExceptionObject := ExceptObjectStack^.FObject;
  51. end;
  52. end;
  53. procedure ReleaseExceptionObject;
  54. begin
  55. If ExceptObjectStack <> nil then begin
  56. if ExceptObjectStack^.refcount > 0 then begin
  57. Dec(ExceptObjectStack^.refcount);
  58. end;
  59. end;
  60. end;
  61. {$ifndef HAS_ADDR_STACK_ON_STACK}
  62. Function fpc_PushExceptAddr (Ft: Longint): PJmp_buf ;
  63. [Public, Alias : 'FPC_PUSHEXCEPTADDR'];{$ifndef NOSAVEREGISTERS}saveregisters;{$endif}
  64. {$else HAS_ADDR_STACK_ON_STACK}
  65. Function fpc_PushExceptAddr (Ft: Longint;_buf,_newaddr : pointer): PJmp_buf ;
  66. [Public, Alias : 'FPC_PUSHEXCEPTADDR'];{$ifndef NOSAVEREGISTERS}saveregisters;{$endif}{$ifdef hascompilerproc} compilerproc; {$endif}
  67. {$endif HAS_ADDR_STACK_ON_STACK}
  68. var
  69. Buf : PJmp_buf;
  70. NewAddr : PExceptAddr;
  71. begin
  72. {$ifdef excdebug}
  73. writeln ('In PushExceptAddr');
  74. {$endif}
  75. If ExceptAddrstack=Nil then
  76. begin
  77. {$ifndef HAS_ADDR_STACK_ON_STACK}
  78. New(ExceptAddrStack);
  79. {$else HAS_ADDR_STACK_ON_STACK}
  80. ExceptAddrStack:=PExceptAddr(_newaddr);
  81. {$endif HAS_ADDR_STACK_ON_STACK}
  82. ExceptAddrStack^.Next:=Nil;
  83. end
  84. else
  85. begin
  86. {$ifndef HAS_ADDR_STACK_ON_STACK}
  87. New(NewAddr);
  88. {$else HAS_ADDR_STACK_ON_STACK}
  89. NewAddr:=PExceptAddr(_newaddr);
  90. {$endif HAS_ADDR_STACK_ON_STACK}
  91. NewAddr^.Next:=ExceptAddrStack;
  92. ExceptAddrStack:=NewAddr;
  93. end;
  94. {$ifndef HAS_ADDR_STACK_ON_STACK}
  95. new(buf);
  96. {$else HAS_ADDR_STACK_ON_STACK}
  97. buf:=PJmp_Buf(_buf);
  98. {$endif HAS_ADDR_STACK_ON_STACK}
  99. ExceptAddrStack^.Buf:=Buf;
  100. ExceptAddrStack^.FrameType:=ft;
  101. fpc_PushExceptAddr:=Buf;
  102. end;
  103. Procedure fpc_PushExceptObj (Obj : TObject; AnAddr,AFrame : Pointer);
  104. [Public, Alias : 'FPC_PUSHEXCEPTOBJECT'];{$ifndef NOSAVEREGISTERS}saveregisters;{$endif}{$ifdef hascompilerproc} compilerproc; {$endif}
  105. var
  106. Newobj : PExceptObject;
  107. framebufsize,
  108. framecount : longint;
  109. frames : PPointer;
  110. prev_frame,
  111. curr_frame,
  112. caller_frame,
  113. caller_addr : Pointer;
  114. begin
  115. {$ifdef excdebug}
  116. writeln ('In PushExceptObject');
  117. {$endif}
  118. If ExceptObjectStack=Nil then
  119. begin
  120. New(ExceptObjectStack);
  121. ExceptObjectStack^.Next:=Nil;
  122. end
  123. else
  124. begin
  125. New(NewObj);
  126. NewObj^.Next:=ExceptObjectStack;
  127. ExceptObjectStack:=NewObj;
  128. end;
  129. ExceptObjectStack^.FObject:=Obj;
  130. ExceptObjectStack^.Addr:=AnAddr;
  131. ExceptObjectStack^.refcount:=0;
  132. { Backtrace }
  133. curr_frame:=AFrame;
  134. prev_frame:=get_frame;
  135. frames:=nil;
  136. framebufsize:=0;
  137. framecount:=0;
  138. while (framecount<RaiseMaxFrameCount) and (curr_frame > prev_frame) 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$
  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. }