except.inc 11 KB

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