--- gforth/except.fs 2002/09/24 17:57:28 1.4 +++ gforth/except.fs 2006/05/26 21:18:45 1.10 @@ -1,6 +1,6 @@ \ catch, throw, etc. -\ Copyright (C) 1999,2000 Free Software Foundation, Inc. +\ Copyright (C) 1999,2000,2003 Free Software Foundation, Inc. \ This file is part of Gforth. @@ -33,21 +33,64 @@ Defer store-backtrace ' noop IS store-backtrace \ [THEN] -: (try) ( -- ) - \ inline argument: address of the handler +\ Ok, here's the story about how we get to the native code for the +\ recovery code in case of a THROW, and why there is all this funny +\ stuff being compiled by TRY and RECOVER: + +\ Upon a THROW, we cannot just return through the ordinary return +\ address, but have to use a different one, for code after the +\ RECOVER. How do we do that, in a way portable between the various +\ threaded and native code engines? In particular, how does the +\ native code engine learn about the address of the native recovery +\ code? + +\ On the Forth level, we can compile only references to threaded code. +\ The only thing that translates a threaded code address to a native +\ code address is docol, which is only called with EXECUTE and +\ friends. So we start the recovery code with a docol, and invoke it +\ with PERFORM; the recovery code then rdrops the superfluously +\ generated return address and continues with the proper recovery +\ code. + +\ At compile time, since we cannot compile a forward reference (to the +\ recovery code) as a literal (backpatching does not work for +\ native-code literals), we produce a data cell (wrapped in AHEAD +\ ... THEN) that we can backpatch, and compile the address of that as +\ literal. + +\ Overall, this leads to the following resulting code: + +\ ahead +\ +>-+ +\ | then | +\ +-lit | +\ (try) | +\ ... | +\ (recover) | +\ ahead | +\ docol: <-----------+ +\ rdrop +\ ... +\ then +\ ... + +\ !! explain handler on-stack structure + +: (try) ( ahandler -- ) r> - dup @ >r \ recovery address + swap >r \ recovery address rp@ 'catch >r sp@ >r fp@ >r lp@ >r handler @ >r rp@ handler ! - backtrace-empty on - cell+ >r ; + >r ; : try ( compilation -- orig ; run-time -- ) \ gforth - POSTPONE (try) >mark ; immediate compile-only + \ !! does not work correctly for gforth-native + POSTPONE ahead here >r >mark 1 cs-roll POSTPONE then + r> POSTPONE literal POSTPONE (try) ; immediate compile-only : (recover) ( -- ) \ normal end of try block: restore handler, forget rest @@ -63,7 +106,10 @@ Defer store-backtrace : recover ( compilation orig1 -- orig2 ; run-time -- ) \ gforth \ !! check using a special tag POSTPONE (recover) - POSTPONE else ; immediate compile-only + POSTPONE else + docol: here 0 , 0 , code-address! \ start a colon def + postpone rdrop \ drop the return address +; immediate compile-only : endtry ( compilation orig -- ; run-time -- ) \ gforth POSTPONE then ; immediate compile-only @@ -79,18 +125,34 @@ is catch :noname ( y1 .. ym error/0 -- y1 .. ym / z1 .. zn error ) \ exception ?DUP IF [ here forthstart 9 cells + ! ] - store-backtrace + store-backtrace error-stack off handler @ ?dup-0=-IF - cr ." uncaught exception: " .error cr + >stderr cr ." uncaught exception: " .error cr 2 (bye) - quit +\ quit THEN rp! r> handler ! r> lp! r> fp! r> swap >r sp! drop r> - rdrop 'throw + rdrop 'throw r> perform THEN ; is throw - +[IFDEF] throw>error +:noname ( y1 .. ym error/0 -- y1 .. ym / z1 .. zn error ) \ exception + ?DUP IF + handler @ ?dup-0=-IF + >stderr cr ." uncaught exception: " .error cr + 2 (bye) +\ quit + THEN + rp! + r> handler ! + r> lp! + r> fp! + r> swap >r sp! drop r> + rdrop 'throw r> perform + THEN ; +is throw>error +[THEN] \ No newline at end of file