except.inc 11 KB

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