except.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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 : ObjpasInt;
  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. { This routine is called only from fpc_raiseexception, which uses ExceptTryLevel
  87. flag to guard against repeated exceptions which can occur due to corrupted stack
  88. or heap. }
  89. Procedure PushExceptObject(Obj : TObject; AnAddr : CodePointer; AFrame : Pointer);
  90. var
  91. Newobj : PExceptObject;
  92. _ExceptObjectStack : ^PExceptObject;
  93. framebufsize,
  94. framecount : longint;
  95. frames : PCodePointer;
  96. prev_frame,
  97. curr_frame : Pointer;
  98. curr_addr : CodePointer;
  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. { The frame pointer of this procedure is used as initial stack bottom value. }
  117. prev_frame:=get_frame;
  118. while (framecount<RaiseMaxFrameCount) and (curr_frame > prev_frame) and
  119. (curr_frame<StackTop) do
  120. Begin
  121. prev_frame:=curr_frame;
  122. get_caller_stackinfo(curr_frame,curr_addr);
  123. if (curr_addr=nil) or
  124. (curr_frame=nil) then
  125. break;
  126. if (framecount>=framebufsize) then
  127. begin
  128. inc(framebufsize,16);
  129. reallocmem(frames,framebufsize*sizeof(codepointer));
  130. end;
  131. frames[framecount]:=curr_addr;
  132. inc(framecount);
  133. End;
  134. NewObj^.framecount:=framecount;
  135. NewObj^.frames:=frames;
  136. end;
  137. Procedure DoUnHandledException;
  138. var
  139. _ExceptObjectStack : PExceptObject;
  140. begin
  141. _ExceptObjectStack:=ExceptObjectStack;
  142. If (ExceptProc<>Nil) and (_ExceptObjectStack<>Nil) then
  143. with _ExceptObjectStack^ do
  144. begin
  145. TExceptProc(ExceptProc)(FObject,Addr,FrameCount,Frames);
  146. halt(217)
  147. end;
  148. if erroraddr = nil then
  149. RunError(217)
  150. else
  151. Halt(errorcode);
  152. end;
  153. {$ifndef FPC_SYSTEM_HAS_RAISEEXCEPTION}
  154. procedure fpc_RaiseException (Obj : TObject; AnAddr : CodePointer; AFrame : Pointer);[Public, Alias : 'FPC_RAISEEXCEPTION']; compilerproc;
  155. var
  156. _ExceptObjectStack : PExceptObject;
  157. _ExceptAddrstack : PExceptAddr;
  158. begin
  159. {$ifdef excdebug}
  160. writeln ('In RaiseException');
  161. {$endif}
  162. if ExceptTryLevel<>0 then
  163. Halt(217);
  164. ExceptTryLevel:=1;
  165. PushExceptObject(Obj,AnAddr,AFrame);
  166. { if PushExceptObject causes another exception, the following won't be executed,
  167. causing halt upon entering this routine recursively. }
  168. ExceptTryLevel:=0;
  169. _ExceptAddrstack:=ExceptAddrStack;
  170. If _ExceptAddrStack=Nil then
  171. DoUnhandledException;
  172. _ExceptObjectStack:=ExceptObjectStack;
  173. if (RaiseProc <> nil) and (_ExceptObjectStack <> nil) then
  174. with _ExceptObjectStack^ do
  175. RaiseProc(FObject,Addr,FrameCount,Frames);
  176. longjmp(_ExceptAddrStack^.Buf^,FPC_Exception);
  177. end;
  178. {$endif FPC_SYSTEM_HAS_RAISEEXCEPTION}
  179. Procedure fpc_PopAddrStack;[Public, Alias : 'FPC_POPADDRSTACK']; compilerproc;
  180. var
  181. hp : ^PExceptAddr;
  182. begin
  183. {$ifdef excdebug}
  184. writeln ('In Popaddrstack');
  185. {$endif}
  186. hp:=@ExceptAddrStack;
  187. If hp^=nil then
  188. begin
  189. {$ifdef excdebug}
  190. writeln ('At end of ExceptionAddresStack');
  191. {$endif}
  192. halt (255);
  193. end
  194. else
  195. begin
  196. hp^:=hp^^.Next;
  197. end;
  198. end;
  199. function fpc_PopObjectStack : TObject;[Public, Alias : 'FPC_POPOBJECTSTACK']; compilerproc;
  200. var
  201. hp : PExceptObject;
  202. begin
  203. {$ifdef excdebug}
  204. writeln ('In PopObjectstack');
  205. {$endif}
  206. hp:=ExceptObjectStack;
  207. if hp=nil then
  208. begin
  209. {$ifdef excdebug}
  210. writeln ('At end of ExceptionObjectStack');
  211. {$endif}
  212. halt (1);
  213. end
  214. else
  215. begin
  216. { we need to return the exception object to dispose it }
  217. if hp^.refcount = 0 then
  218. fpc_PopObjectStack:=hp^.FObject
  219. else
  220. fpc_PopObjectStack:=nil;
  221. ExceptObjectStack:=hp^.next;
  222. if assigned(hp^.frames) then
  223. freemem(hp^.frames);
  224. dispose(hp);
  225. erroraddr:=nil;
  226. end;
  227. end;
  228. { this is for popping exception objects when a second exception is risen }
  229. { in an except/on }
  230. function fpc_PopSecondObjectStack : TObject;[Public, Alias : 'FPC_POPSECONDOBJECTSTACK']; compilerproc;
  231. var
  232. hp,_ExceptObjectStack : PExceptObject;
  233. begin
  234. {$ifdef excdebug}
  235. writeln ('In PopObjectstack');
  236. {$endif}
  237. _ExceptObjectStack:=ExceptObjectStack;
  238. If not(assigned(_ExceptObjectStack)) or
  239. not(assigned(_ExceptObjectStack^.next)) then
  240. begin
  241. {$ifdef excdebug}
  242. writeln ('At end of ExceptionObjectStack');
  243. {$endif}
  244. halt (1);
  245. end
  246. else
  247. begin
  248. if _ExceptObjectStack^.next^.refcount=0 then
  249. { we need to return the exception object to dispose it if refcount=0 }
  250. fpc_PopSecondObjectStack:=_ExceptObjectStack^.next^.FObject
  251. else
  252. fpc_PopSecondObjectStack:=nil;
  253. hp:=_ExceptObjectStack^.next;
  254. _ExceptObjectStack^.next:=hp^.next;
  255. if assigned(hp^.frames) then
  256. freemem(hp^.frames);
  257. dispose(hp);
  258. end;
  259. end;
  260. {$ifndef FPC_SYSTEM_HAS_RERAISE}
  261. Procedure fpc_ReRaise;[Public, Alias : 'FPC_RERAISE']; compilerproc;
  262. var
  263. _ExceptAddrStack : PExceptAddr;
  264. begin
  265. {$ifdef excdebug}
  266. writeln ('In reraise');
  267. {$endif}
  268. _ExceptAddrStack:=ExceptAddrStack;
  269. If _ExceptAddrStack=Nil then
  270. DoUnHandledException;
  271. ExceptObjectStack^.refcount := 0;
  272. longjmp(_ExceptAddrStack^.Buf^,FPC_Exception);
  273. end;
  274. {$endif FPC_SYSTEM_HAS_RERAISE}
  275. function Internal_PopSecondObjectStack : TObject; external name 'FPC_POPSECONDOBJECTSTACK';
  276. function Internal_PopObjectStack: TObject; external name 'FPC_POPOBJECTSTACK';
  277. procedure Internal_Reraise; external name 'FPC_RERAISE';
  278. Function fpc_Catches(Objtype : TClass) : TObject;[Public, Alias : 'FPC_CATCHES']; compilerproc;
  279. var
  280. _ExceptObjectStack : PExceptObject;
  281. begin
  282. _ExceptObjectStack:=ExceptObjectStack;
  283. If _ExceptObjectStack=Nil then
  284. begin
  285. {$ifdef excdebug}
  286. Writeln ('Internal error.');
  287. {$endif}
  288. halt (255);
  289. end;
  290. if Not ((Objtype = TClass(CatchAllExceptions)) or
  291. (_ExceptObjectStack^.FObject is ObjType)) then
  292. fpc_Catches:=Nil
  293. else
  294. begin
  295. // catch !
  296. fpc_Catches:=_ExceptObjectStack^.FObject;
  297. { this can't be done, because there could be a reraise (PFV)
  298. PopObjectStack;
  299. Also the PopAddrStack shouldn't be done, we do it now
  300. immediatly in the exception handler (FK)
  301. PopAddrStack; }
  302. end;
  303. end;
  304. {$ifdef VER2_6}
  305. Procedure fpc_DestroyException(o : TObject);[Public, Alias : 'FPC_DESTROYEXCEPTION']; compilerproc;
  306. begin
  307. { with free we're on the really safe side }
  308. o.Free;
  309. end;
  310. { TODO: no longer used, clean up }
  311. function fpc_GetExceptionAddr : CodePointer;[Public, Alias : 'FPC_GETEXCEPTIONADDR']; compilerproc;
  312. var
  313. _ExceptObjectStack : PExceptObject;
  314. begin
  315. _ExceptObjectStack:=ExceptObjectStack;
  316. if _ExceptObjectStack=nil then
  317. fpc_GetExceptionAddr:=nil
  318. else
  319. fpc_GetExceptionAddr:=_ExceptObjectStack^.Addr;
  320. end;
  321. {$endif VER2_6}
  322. Procedure SysInitExceptions;
  323. {
  324. Initialize exceptionsupport
  325. }
  326. begin
  327. ExceptObjectstack:=Nil;
  328. ExceptAddrStack:=Nil;
  329. end;
  330. {$ifndef FPC_SYSTEM_HAS_DONEEXCEPTION}
  331. procedure fpc_doneexception;[public,alias:'FPC_DONEEXCEPTION'] compilerproc;
  332. begin
  333. Internal_PopObjectStack.Free;
  334. end;
  335. {$endif FPC_SYSTEM_HAS_DONEEXCEPTION}
  336. procedure fpc_raise_nested;[public,alias:'FPC_RAISE_NESTED']compilerproc;
  337. begin
  338. Internal_PopSecondObjectStack.Free;
  339. Internal_Reraise;
  340. end;
  341. {$ifndef FPC_SYSTEM_HAS_SAFECALLHANDLER}
  342. function fpc_safecallhandler(obj: TObject): HResult; [public,alias:'FPC_SAFECALLHANDLER']; compilerproc;
  343. var
  344. raiselist: PExceptObject;
  345. adr: CodePointer;
  346. exc: TObject;
  347. begin
  348. raiselist:=ExceptObjectStack;
  349. if Assigned(raiseList) then
  350. adr:=raiseList^.Addr
  351. else
  352. adr:=nil;
  353. exc:=Internal_PopObjectStack;
  354. if Assigned(obj) and Assigned(exc) then
  355. result:=obj.SafeCallException(exc,adr)
  356. else
  357. result:=E_UNEXPECTED;
  358. exc.Free;
  359. end;
  360. {$endif FPC_SYSTEM_HAS_SAFECALLHANDLER}