except.inc 10 KB

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