except.inc 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  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) Do
  138. Begin
  139. caller_addr := get_caller_addr(curr_frame);
  140. caller_frame := get_caller_frame(curr_frame);
  141. if (caller_addr=nil) or
  142. (caller_frame=nil) then
  143. break;
  144. if (framecount>=framebufsize) then
  145. begin
  146. inc(framebufsize,16);
  147. reallocmem(frames,framebufsize*sizeof(pointer));
  148. end;
  149. frames[framecount]:=caller_addr;
  150. inc(framecount);
  151. prev_frame:=curr_frame;
  152. curr_frame:=caller_frame;
  153. End;
  154. ExceptObjectStack^.framecount:=framecount;
  155. ExceptObjectStack^.frames:=frames;
  156. end;
  157. {$ifdef hascompilerproc}
  158. { make it avalable for local use }
  159. Procedure fpc_PushExceptObj (Obj : TObject; AnAddr,AFrame : Pointer); [external name 'FPC_PUSHEXCEPTOBJECT'];
  160. {$endif}
  161. Procedure DoUnHandledException;
  162. begin
  163. If (ExceptProc<>Nil) and (ExceptObjectStack<>Nil) then
  164. with ExceptObjectStack^ do
  165. TExceptProc(ExceptProc)(FObject,Addr,FrameCount,Frames);
  166. RunError(217);
  167. end;
  168. Function fpc_Raiseexception (Obj : TObject; AnAddr,AFrame : Pointer) : TObject;[Public, Alias : 'FPC_RAISEEXCEPTION']; {$ifdef hascompilerproc} compilerproc; {$endif}
  169. begin
  170. {$ifdef excdebug}
  171. writeln ('In RaiseException');
  172. {$endif}
  173. fpc_Raiseexception:=nil;
  174. fpc_PushExceptObj(Obj,AnAddr,AFrame);
  175. If ExceptAddrStack=Nil then
  176. DoUnhandledException;
  177. if (RaiseProc <> nil) and (ExceptObjectStack <> nil) then
  178. with ExceptObjectStack^ do
  179. RaiseProc(FObject,Addr,FrameCount,Frames);
  180. longjmp(ExceptAddrStack^.Buf^,FPC_Exception);
  181. end;
  182. Procedure fpc_PopAddrStack;[Public, Alias : 'FPC_POPADDRSTACK']; {$ifdef hascompilerproc} compilerproc; {$endif}
  183. {$ifndef HAS_ADDR_STACK_ON_STACK}
  184. var
  185. hp : PExceptAddr;
  186. {$endif HAS_ADDR_STACK_ON_STACK}
  187. begin
  188. {$ifdef excdebug}
  189. writeln ('In Popaddrstack');
  190. {$endif}
  191. If ExceptAddrStack=nil then
  192. begin
  193. writeln ('At end of ExceptionAddresStack');
  194. halt (255);
  195. end
  196. else
  197. begin
  198. {$ifndef HAS_ADDR_STACK_ON_STACK}
  199. hp:=ExceptAddrStack;
  200. ExceptAddrStack:=ExceptAddrStack^.Next;
  201. dispose(hp^.buf);
  202. dispose(hp);
  203. {$else HAS_ADDR_STACK_ON_STACK}
  204. ExceptAddrStack:=ExceptAddrStack^.Next;
  205. {$endif HAS_ADDR_STACK_ON_STACK}
  206. end;
  207. end;
  208. function fpc_PopObjectStack : TObject;[Public, Alias : 'FPC_POPOBJECTSTACK']; {$ifdef hascompilerproc} compilerproc; {$endif}
  209. var
  210. hp : PExceptObject;
  211. begin
  212. {$ifdef excdebug}
  213. writeln ('In PopObjectstack');
  214. {$endif}
  215. If ExceptObjectStack=nil then
  216. begin
  217. writeln ('At end of ExceptionObjectStack');
  218. halt (1);
  219. end
  220. else
  221. begin
  222. { we need to return the exception object to dispose it }
  223. if ExceptObjectStack^.refcount = 0 then begin
  224. fpc_PopObjectStack:=ExceptObjectStack^.FObject;
  225. end else begin
  226. fpc_PopObjectStack:=nil;
  227. end;
  228. hp:=ExceptObjectStack;
  229. ExceptObjectStack:=ExceptObjectStack^.next;
  230. if assigned(hp^.frames) then
  231. freemem(hp^.frames);
  232. dispose(hp);
  233. end;
  234. end;
  235. { this is for popping exception objects when a second exception is risen }
  236. { in an except/on }
  237. function fpc_PopSecondObjectStack : TObject;[Public, Alias : 'FPC_POPSECONDOBJECTSTACK']; {$ifdef hascompilerproc} compilerproc; {$endif}
  238. var
  239. hp : PExceptObject;
  240. begin
  241. {$ifdef excdebug}
  242. writeln ('In PopObjectstack');
  243. {$endif}
  244. If not(assigned(ExceptObjectStack)) or
  245. not(assigned(ExceptObjectStack^.next)) then
  246. begin
  247. writeln ('At end of ExceptionObjectStack');
  248. halt (1);
  249. end
  250. else
  251. begin
  252. if ExceptObjectStack^.next^.refcount=0 then
  253. { we need to return the exception object to dispose it if refcount=0 }
  254. fpc_PopSecondObjectStack:=ExceptObjectStack^.next^.FObject
  255. else
  256. fpc_PopSecondObjectStack:=nil;
  257. hp:=ExceptObjectStack^.next;
  258. ExceptObjectStack^.next:=hp^.next;
  259. if assigned(hp^.frames) then
  260. freemem(hp^.frames);
  261. dispose(hp);
  262. end;
  263. end;
  264. Procedure fpc_ReRaise;[Public, Alias : 'FPC_RERAISE']; {$ifdef hascompilerproc} compilerproc; {$endif}
  265. begin
  266. {$ifdef excdebug}
  267. writeln ('In reraise');
  268. {$endif}
  269. If ExceptAddrStack=Nil then
  270. DoUnHandledException;
  271. ExceptObjectStack^.refcount := 0;
  272. longjmp(ExceptAddrStack^.Buf^,FPC_Exception);
  273. end;
  274. Function fpc_Catches(Objtype : TClass) : TObject;[Public, Alias : 'FPC_CATCHES']; {$ifdef hascompilerproc} compilerproc; {$endif}
  275. var
  276. _Objtype : TExceptObjectClass;
  277. begin
  278. If ExceptObjectStack=Nil then
  279. begin
  280. Writeln ('Internal error.');
  281. halt (255);
  282. end;
  283. _Objtype := TExceptObjectClass(Objtype);
  284. if Not ((_Objtype = TExceptObjectClass(CatchAllExceptions)) or
  285. (ExceptObjectStack^.FObject is _ObjType)) then
  286. fpc_Catches:=Nil
  287. else
  288. begin
  289. // catch !
  290. fpc_Catches:=ExceptObjectStack^.FObject;
  291. { this can't be done, because there could be a reraise (PFV)
  292. PopObjectStack;
  293. Also the PopAddrStack shouldn't be done, we do it now
  294. immediatly in the exception handler (FK)
  295. PopAddrStack; }
  296. end;
  297. end;
  298. Procedure fpc_DestroyException(o : TObject);[Public, Alias : 'FPC_DESTROYEXCEPTION']; {$ifdef hascompilerproc} compilerproc; {$endif}
  299. begin
  300. { with free we're on the really save side }
  301. o.Free;
  302. end;
  303. Procedure SysInitExceptions;
  304. {
  305. Initialize exceptionsupport
  306. }
  307. begin
  308. ExceptObjectstack:=Nil;
  309. ExceptAddrStack:=Nil;
  310. end;
  311. {
  312. $Log: except.inc,v $
  313. Revision 1.21 2005/05/08 21:20:26 michael
  314. + Patch to return nil if there is no exception object (as in Delphi)
  315. Revision 1.20 2005/04/03 11:32:05 florian
  316. * ref. counting for popping second exceptiono object fixed
  317. Revision 1.19 2005/02/14 17:13:22 peter
  318. * truncate log
  319. Revision 1.18 2005/01/29 17:01:18 peter
  320. * fix crash with backtrace if invalid frame is passed
  321. Revision 1.17 2005/01/26 17:07:10 peter
  322. * retrieve backtrace when exception is raised
  323. * RaiseMaxFrameCount added to limit the number of backtraces, setting
  324. it to 0 disables backtraces. Default is 16
  325. }