except.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  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. Const
  28. CatchAllExceptions = PtrInt(-1);
  29. ThreadVar
  30. ExceptAddrStack : PExceptAddr;
  31. ExceptObjectStack : PExceptObject;
  32. ExceptTryLevel : longint;
  33. Function RaiseList : PExceptObject;
  34. begin
  35. RaiseList:=ExceptObjectStack;
  36. end;
  37. function AcquireExceptionObject: Pointer;
  38. var
  39. _ExceptObjectStack : PExceptObject;
  40. begin
  41. _ExceptObjectStack:=ExceptObjectStack;
  42. If _ExceptObjectStack<>nil then
  43. begin
  44. Inc(_ExceptObjectStack^.refcount);
  45. AcquireExceptionObject := _ExceptObjectStack^.FObject;
  46. end
  47. else
  48. RunError(231);
  49. end;
  50. procedure ReleaseExceptionObject;
  51. var
  52. _ExceptObjectStack : PExceptObject;
  53. begin
  54. _ExceptObjectStack:=ExceptObjectStack;
  55. If _ExceptObjectStack <> nil then
  56. begin
  57. if _ExceptObjectStack^.refcount > 0 then
  58. Dec(_ExceptObjectStack^.refcount);
  59. end
  60. else
  61. RunError(231);
  62. end;
  63. Function fpc_PushExceptAddr (Ft: Longint;_buf,_newaddr : pointer): PJmp_buf ;
  64. [Public, Alias : 'FPC_PUSHEXCEPTADDR'];compilerproc;
  65. var
  66. _ExceptAddrstack : ^PExceptAddr;
  67. begin
  68. {$ifdef excdebug}
  69. writeln ('In PushExceptAddr');
  70. {$endif}
  71. _ExceptAddrstack:=@ExceptAddrstack;
  72. PExceptAddr(_newaddr)^.Next:=_ExceptAddrstack^;
  73. _ExceptAddrStack^:=PExceptAddr(_newaddr);
  74. PExceptAddr(_newaddr)^.Buf:=PJmp_Buf(_buf);
  75. PExceptAddr(_newaddr)^.FrameType:=ft;
  76. result:=PJmp_Buf(_buf);
  77. end;
  78. {$ifdef FPC_HAS_FEATURE_EXCEPTIONS}
  79. { get_caller_XX funxctions can generate exceptions on their own
  80. for instance ifd the stack got corrupted
  81. Here we protect calls to those function by a
  82. try/except bloc, with a global indicator TryExceptLevel
  83. to avoid calling get_caller_XXX functions again if
  84. any exception appears while calling them }
  85. {$define FPC_CHECK_GET_CALLER_EXCEPTIONS}
  86. {$endif}
  87. Procedure PushExceptObject(Obj : TObject; AnAddr,AFrame : Pointer);
  88. var
  89. Newobj : PExceptObject;
  90. _ExceptObjectStack : ^PExceptObject;
  91. framebufsize,
  92. framecount : longint;
  93. frames : PPointer;
  94. prev_frame,
  95. curr_frame,
  96. curr_addr,
  97. caller_frame,
  98. caller_addr : Pointer;
  99. begin
  100. {$ifdef excdebug}
  101. writeln ('In PushExceptObject');
  102. {$endif}
  103. _ExceptObjectStack:=@ExceptObjectStack;
  104. New(NewObj);
  105. NewObj^.Next:=_ExceptObjectStack^;
  106. _ExceptObjectStack^:=NewObj;
  107. NewObj^.FObject:=Obj;
  108. NewObj^.Addr:=AnAddr;
  109. NewObj^.refcount:=0;
  110. { Backtrace }
  111. curr_frame:=AFrame;
  112. curr_addr:=AnAddr;
  113. frames:=nil;
  114. framebufsize:=0;
  115. framecount:=0;
  116. if ExceptTryLevel = 0 then
  117. begin
  118. Inc(ExceptTrylevel);
  119. {$ifdef FPC_CHECK_GET_CALLER_EXCEPTIONS}
  120. try
  121. {$endif FPC_CHECK_GET_CALLER_EXCEPTIONS}
  122. prev_frame:=get_caller_frame(curr_addr, curr_frame);
  123. {$ifdef FPC_CHECK_GET_CALLER_EXCEPTIONS}
  124. except
  125. halt(217);
  126. end;
  127. {$endif FPC_CHECK_GET_CALLER_EXCEPTIONS}
  128. while (framecount<RaiseMaxFrameCount) and (curr_frame > prev_frame) and
  129. (curr_frame<(StackBottom + StackLength)) do
  130. Begin
  131. {$ifdef FPC_CHECK_GET_CALLER_EXCEPTIONS}
  132. try
  133. caller_addr := get_caller_addr(curr_frame, curr_addr);
  134. except
  135. halt(217);
  136. end;
  137. try
  138. caller_frame := get_caller_frame(curr_frame, curr_addr);
  139. except
  140. halt(217);
  141. end;
  142. {$else not FPC_CHECK_GET_CALLER_EXCEPTIONS}
  143. caller_addr := get_caller_addr(curr_frame, curr_addr);
  144. caller_frame := get_caller_frame(curr_frame, curr_addr);
  145. {$endif FPC_CHECK_GET_CALLER_EXCEPTIONS}
  146. if (caller_addr=nil) or
  147. (caller_frame=nil) then
  148. break;
  149. if (framecount>=framebufsize) then
  150. begin
  151. inc(framebufsize,16);
  152. reallocmem(frames,framebufsize*sizeof(pointer));
  153. end;
  154. frames[framecount]:=caller_addr;
  155. inc(framecount);
  156. prev_frame:=curr_frame;
  157. curr_addr:=caller_addr;
  158. curr_frame:=caller_frame;
  159. End;
  160. Dec(ExceptTryLevel);
  161. end;
  162. NewObj^.framecount:=framecount;
  163. NewObj^.frames:=frames;
  164. end;
  165. Procedure DoUnHandledException;
  166. var
  167. _ExceptObjectStack : PExceptObject;
  168. begin
  169. _ExceptObjectStack:=ExceptObjectStack;
  170. If (ExceptProc<>Nil) and (_ExceptObjectStack<>Nil) then
  171. with _ExceptObjectStack^ do
  172. begin
  173. TExceptProc(ExceptProc)(FObject,Addr,FrameCount,Frames);
  174. halt(217)
  175. end;
  176. if erroraddr = nil then
  177. RunError(217)
  178. else
  179. Halt(errorcode);
  180. end;
  181. {$ifndef FPC_SYSTEM_HAS_RAISEEXCEPTION}
  182. Function fpc_RaiseException (Obj : TObject; AnAddr,AFrame : Pointer) : TObject;[Public, Alias : 'FPC_RAISEEXCEPTION']; compilerproc;
  183. var
  184. _ExceptObjectStack : PExceptObject;
  185. _ExceptAddrstack : PExceptAddr;
  186. begin
  187. {$ifdef excdebug}
  188. writeln ('In RaiseException');
  189. {$endif}
  190. fpc_Raiseexception:=nil;
  191. PushExceptObject(Obj,AnAddr,AFrame);
  192. _ExceptAddrstack:=ExceptAddrStack;
  193. If _ExceptAddrStack=Nil then
  194. DoUnhandledException;
  195. _ExceptObjectStack:=ExceptObjectStack;
  196. if (RaiseProc <> nil) and (_ExceptObjectStack <> nil) then
  197. with _ExceptObjectStack^ do
  198. RaiseProc(FObject,Addr,FrameCount,Frames);
  199. longjmp(_ExceptAddrStack^.Buf^,FPC_Exception);
  200. end;
  201. {$endif FPC_SYSTEM_HAS_RAISEEXCEPTION}
  202. Procedure fpc_PopAddrStack;[Public, Alias : 'FPC_POPADDRSTACK']; compilerproc;
  203. var
  204. hp : ^PExceptAddr;
  205. begin
  206. {$ifdef excdebug}
  207. writeln ('In Popaddrstack');
  208. {$endif}
  209. hp:=@ExceptAddrStack;
  210. If hp^=nil then
  211. begin
  212. {$ifdef excdebug}
  213. writeln ('At end of ExceptionAddresStack');
  214. {$endif}
  215. halt (255);
  216. end
  217. else
  218. begin
  219. hp^:=hp^^.Next;
  220. end;
  221. end;
  222. function fpc_PopObjectStack : TObject;[Public, Alias : 'FPC_POPOBJECTSTACK']; compilerproc;
  223. var
  224. hp,_ExceptObjectStack : PExceptObject;
  225. begin
  226. {$ifdef excdebug}
  227. writeln ('In PopObjectstack');
  228. {$endif}
  229. _ExceptObjectStack:=ExceptObjectStack;
  230. If _ExceptObjectStack=nil then
  231. begin
  232. {$ifdef excdebug}
  233. writeln ('At end of ExceptionObjectStack');
  234. {$endif}
  235. halt (1);
  236. end
  237. else
  238. begin
  239. { we need to return the exception object to dispose it }
  240. if _ExceptObjectStack^.refcount = 0 then begin
  241. fpc_PopObjectStack:=_ExceptObjectStack^.FObject;
  242. end else begin
  243. fpc_PopObjectStack:=nil;
  244. end;
  245. hp:=_ExceptObjectStack;
  246. ExceptObjectStack:=_ExceptObjectStack^.next;
  247. if assigned(hp^.frames) then
  248. freemem(hp^.frames);
  249. dispose(hp);
  250. erroraddr:=nil;
  251. end;
  252. end;
  253. { this is for popping exception objects when a second exception is risen }
  254. { in an except/on }
  255. function fpc_PopSecondObjectStack : TObject;[Public, Alias : 'FPC_POPSECONDOBJECTSTACK']; compilerproc;
  256. var
  257. hp,_ExceptObjectStack : PExceptObject;
  258. begin
  259. {$ifdef excdebug}
  260. writeln ('In PopObjectstack');
  261. {$endif}
  262. _ExceptObjectStack:=ExceptObjectStack;
  263. If not(assigned(_ExceptObjectStack)) or
  264. not(assigned(_ExceptObjectStack^.next)) then
  265. begin
  266. {$ifdef excdebug}
  267. writeln ('At end of ExceptionObjectStack');
  268. {$endif}
  269. halt (1);
  270. end
  271. else
  272. begin
  273. if _ExceptObjectStack^.next^.refcount=0 then
  274. { we need to return the exception object to dispose it if refcount=0 }
  275. fpc_PopSecondObjectStack:=_ExceptObjectStack^.next^.FObject
  276. else
  277. fpc_PopSecondObjectStack:=nil;
  278. hp:=_ExceptObjectStack^.next;
  279. _ExceptObjectStack^.next:=hp^.next;
  280. if assigned(hp^.frames) then
  281. freemem(hp^.frames);
  282. dispose(hp);
  283. end;
  284. end;
  285. {$ifndef FPC_SYSTEM_HAS_RERAISE}
  286. Procedure fpc_ReRaise;[Public, Alias : 'FPC_RERAISE']; compilerproc;
  287. var
  288. _ExceptAddrStack : PExceptAddr;
  289. begin
  290. {$ifdef excdebug}
  291. writeln ('In reraise');
  292. {$endif}
  293. _ExceptAddrStack:=ExceptAddrStack;
  294. If _ExceptAddrStack=Nil then
  295. DoUnHandledException;
  296. ExceptObjectStack^.refcount := 0;
  297. longjmp(_ExceptAddrStack^.Buf^,FPC_Exception);
  298. end;
  299. {$endif FPC_SYSTEM_HAS_RERAISE}
  300. function Internal_PopSecondObjectStack : TObject; external name 'FPC_POPSECONDOBJECTSTACK';
  301. function Internal_PopObjectStack: TObject; external name 'FPC_POPOBJECTSTACK';
  302. procedure Internal_Reraise; external name 'FPC_RERAISE';
  303. Function fpc_Catches(Objtype : TClass) : TObject;[Public, Alias : 'FPC_CATCHES']; compilerproc;
  304. var
  305. _ExceptObjectStack : PExceptObject;
  306. begin
  307. _ExceptObjectStack:=ExceptObjectStack;
  308. If _ExceptObjectStack=Nil then
  309. begin
  310. {$ifdef excdebug}
  311. Writeln ('Internal error.');
  312. {$endif}
  313. halt (255);
  314. end;
  315. if Not ((Objtype = TClass(CatchAllExceptions)) or
  316. (_ExceptObjectStack^.FObject is ObjType)) then
  317. fpc_Catches:=Nil
  318. else
  319. begin
  320. // catch !
  321. fpc_Catches:=_ExceptObjectStack^.FObject;
  322. { this can't be done, because there could be a reraise (PFV)
  323. PopObjectStack;
  324. Also the PopAddrStack shouldn't be done, we do it now
  325. immediatly in the exception handler (FK)
  326. PopAddrStack; }
  327. end;
  328. end;
  329. Procedure fpc_DestroyException(o : TObject);[Public, Alias : 'FPC_DESTROYEXCEPTION']; compilerproc;
  330. begin
  331. { with free we're on the really safe side }
  332. o.Free;
  333. end;
  334. { TODO: no longer used, clean up }
  335. function fpc_GetExceptionAddr : Pointer;[Public, Alias : 'FPC_GETEXCEPTIONADDR']; compilerproc;
  336. var
  337. _ExceptObjectStack : PExceptObject;
  338. begin
  339. _ExceptObjectStack:=ExceptObjectStack;
  340. if _ExceptObjectStack=nil then
  341. fpc_GetExceptionAddr:=nil
  342. else
  343. fpc_GetExceptionAddr:=_ExceptObjectStack^.Addr;
  344. end;
  345. Procedure SysInitExceptions;
  346. {
  347. Initialize exceptionsupport
  348. }
  349. begin
  350. ExceptObjectstack:=Nil;
  351. ExceptAddrStack:=Nil;
  352. end;
  353. procedure fpc_doneexception;[public,alias:'FPC_DONEEXCEPTION'] compilerproc;
  354. begin
  355. Internal_PopObjectStack.Free;
  356. end;
  357. procedure fpc_raise_nested;[public,alias:'FPC_RAISE_NESTED']compilerproc;
  358. begin
  359. Internal_PopSecondObjectStack.Free;
  360. Internal_Reraise;
  361. end;
  362. function fpc_safecallhandler(obj: TObject): HResult; [public,alias:'FPC_SAFECALLHANDLER']; compilerproc;
  363. var
  364. raiselist: PExceptObject;
  365. adr: Pointer;
  366. exc: TObject;
  367. begin
  368. raiselist:=ExceptObjectStack;
  369. if Assigned(raiseList) then
  370. adr:=raiseList^.Addr
  371. else
  372. adr:=nil;
  373. exc:=Internal_PopObjectStack;
  374. if Assigned(obj) and Assigned(exc) then
  375. result:=obj.SafeCallException(exc,adr)
  376. else
  377. result:=E_UNEXPECTED;
  378. exc.Free;
  379. end;