except.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  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 : longint;
  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,AFrame : Pointer);
  96. var
  97. Newobj : PExceptObject;
  98. _ExceptObjectStack : ^PExceptObject;
  99. framebufsize,
  100. framecount : longint;
  101. frames : PPointer;
  102. prev_frame,
  103. curr_frame,
  104. curr_addr,
  105. caller_frame,
  106. caller_addr : Pointer;
  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(pointer));
  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,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,_ExceptObjectStack : PExceptObject;
  233. begin
  234. {$ifdef excdebug}
  235. writeln ('In PopObjectstack');
  236. {$endif}
  237. _ExceptObjectStack:=ExceptObjectStack;
  238. If _ExceptObjectStack=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 _ExceptObjectStack^.refcount = 0 then begin
  249. fpc_PopObjectStack:=_ExceptObjectStack^.FObject;
  250. end else begin
  251. fpc_PopObjectStack:=nil;
  252. end;
  253. hp:=_ExceptObjectStack;
  254. ExceptObjectStack:=_ExceptObjectStack^.next;
  255. if assigned(hp^.frames) then
  256. freemem(hp^.frames);
  257. dispose(hp);
  258. erroraddr:=nil;
  259. end;
  260. end;
  261. { this is for popping exception objects when a second exception is risen }
  262. { in an except/on }
  263. function fpc_PopSecondObjectStack : TObject;[Public, Alias : 'FPC_POPSECONDOBJECTSTACK']; compilerproc;
  264. var
  265. hp,_ExceptObjectStack : PExceptObject;
  266. begin
  267. {$ifdef excdebug}
  268. writeln ('In PopObjectstack');
  269. {$endif}
  270. _ExceptObjectStack:=ExceptObjectStack;
  271. If not(assigned(_ExceptObjectStack)) or
  272. not(assigned(_ExceptObjectStack^.next)) then
  273. begin
  274. {$ifdef excdebug}
  275. writeln ('At end of ExceptionObjectStack');
  276. {$endif}
  277. halt (1);
  278. end
  279. else
  280. begin
  281. if _ExceptObjectStack^.next^.refcount=0 then
  282. { we need to return the exception object to dispose it if refcount=0 }
  283. fpc_PopSecondObjectStack:=_ExceptObjectStack^.next^.FObject
  284. else
  285. fpc_PopSecondObjectStack:=nil;
  286. hp:=_ExceptObjectStack^.next;
  287. _ExceptObjectStack^.next:=hp^.next;
  288. if assigned(hp^.frames) then
  289. freemem(hp^.frames);
  290. dispose(hp);
  291. end;
  292. end;
  293. {$ifndef FPC_SYSTEM_HAS_RERAISE}
  294. Procedure fpc_ReRaise;[Public, Alias : 'FPC_RERAISE']; compilerproc;
  295. var
  296. _ExceptAddrStack : PExceptAddr;
  297. begin
  298. {$ifdef excdebug}
  299. writeln ('In reraise');
  300. {$endif}
  301. _ExceptAddrStack:=ExceptAddrStack;
  302. If _ExceptAddrStack=Nil then
  303. DoUnHandledException;
  304. ExceptObjectStack^.refcount := 0;
  305. longjmp(_ExceptAddrStack^.Buf^,FPC_Exception);
  306. end;
  307. {$endif FPC_SYSTEM_HAS_RERAISE}
  308. function Internal_PopSecondObjectStack : TObject; external name 'FPC_POPSECONDOBJECTSTACK';
  309. function Internal_PopObjectStack: TObject; external name 'FPC_POPOBJECTSTACK';
  310. procedure Internal_Reraise; external name 'FPC_RERAISE';
  311. Function fpc_Catches(Objtype : TClass) : TObject;[Public, Alias : 'FPC_CATCHES']; compilerproc;
  312. var
  313. _ExceptObjectStack : PExceptObject;
  314. begin
  315. _ExceptObjectStack:=ExceptObjectStack;
  316. If _ExceptObjectStack=Nil then
  317. begin
  318. {$ifdef excdebug}
  319. Writeln ('Internal error.');
  320. {$endif}
  321. halt (255);
  322. end;
  323. if Not ((Objtype = TClass(CatchAllExceptions)) or
  324. (_ExceptObjectStack^.FObject is ObjType)) then
  325. fpc_Catches:=Nil
  326. else
  327. begin
  328. // catch !
  329. fpc_Catches:=_ExceptObjectStack^.FObject;
  330. { this can't be done, because there could be a reraise (PFV)
  331. PopObjectStack;
  332. Also the PopAddrStack shouldn't be done, we do it now
  333. immediatly in the exception handler (FK)
  334. PopAddrStack; }
  335. end;
  336. end;
  337. Procedure fpc_DestroyException(o : TObject);[Public, Alias : 'FPC_DESTROYEXCEPTION']; compilerproc;
  338. begin
  339. { with free we're on the really safe side }
  340. o.Free;
  341. end;
  342. { TODO: no longer used, clean up }
  343. function fpc_GetExceptionAddr : Pointer;[Public, Alias : 'FPC_GETEXCEPTIONADDR']; compilerproc;
  344. var
  345. _ExceptObjectStack : PExceptObject;
  346. begin
  347. _ExceptObjectStack:=ExceptObjectStack;
  348. if _ExceptObjectStack=nil then
  349. fpc_GetExceptionAddr:=nil
  350. else
  351. fpc_GetExceptionAddr:=_ExceptObjectStack^.Addr;
  352. end;
  353. Procedure SysInitExceptions;
  354. {
  355. Initialize exceptionsupport
  356. }
  357. begin
  358. ExceptObjectstack:=Nil;
  359. ExceptAddrStack:=Nil;
  360. end;
  361. procedure fpc_doneexception;[public,alias:'FPC_DONEEXCEPTION'] compilerproc;
  362. begin
  363. Internal_PopObjectStack.Free;
  364. end;
  365. procedure fpc_raise_nested;[public,alias:'FPC_RAISE_NESTED']compilerproc;
  366. begin
  367. Internal_PopSecondObjectStack.Free;
  368. Internal_Reraise;
  369. end;
  370. function fpc_safecallhandler(obj: TObject): HResult; [public,alias:'FPC_SAFECALLHANDLER']; compilerproc;
  371. var
  372. raiselist: PExceptObject;
  373. adr: Pointer;
  374. exc: TObject;
  375. begin
  376. raiselist:=ExceptObjectStack;
  377. if Assigned(raiseList) then
  378. adr:=raiseList^.Addr
  379. else
  380. adr:=nil;
  381. exc:=Internal_PopObjectStack;
  382. if Assigned(obj) and Assigned(exc) then
  383. result:=obj.SafeCallException(exc,adr)
  384. else
  385. result:=E_UNEXPECTED;
  386. exc.Free;
  387. end;