3.14 Altering the flow with the Return Stack

The mere fact that return addresses are kept on the stack means that you can alter the flow of a program. This is hardly ever necessary, but if you're a real hacker you'll try this anyway, so we'd better give you some pointers on how it is done. Let's take a look at this program. Note that we comment on the Return Stack effects:

     : soup ." soup " ;                   ( r1 r2)
     : dessert ." dessert " ;             ( r1 r6)
     : chicken ." chicken " ;             ( r1 r3 r4)
     : rice ." rice " ;                   ( r1 r3 r5)
     : entree chicken rice ;              ( r1 r3)
     : dinner soup entree dessert ;       ( r1)
     dinner cr                            ( --)

And this is the output:

     soup chicken rice dessert

Before we execute "DINNER" the Return Stack is empty. When we enter "DINNER" the return address to the main program is on the Return Stack (r1).

"DINNER" calls "SOUP". When we enter "SOUP" the return address to "DINNER" is on the Return Stack (r2). When we are done with "SOUP", its return address disappears from the Return Stack and execution continues within "DINNER".

Then "ENTREE" is called, putting another return address on the Return Stack (r3). "ENTREE" on its turn, calls "CHICKEN". Another return address (r4) is put on the Return Stack. Let's take a look on what currently lies on the Return Stack:

TORSTop Of Return Stack
r4returns to ENTREE
r3returns to DINNER
r1returns to main program

As we already know, ';' compiles an 'EXIT', which takes the TORS and jumps to that address. What if we lose the current TORS? Will the system crash?

Apart from other stack effects (e.g. too few or the wrong data are left on the Data Stack) nothing will go wrong. Unless the colon-definition was called from inside a DO..LOOP, of course. But what DOES happen? The solution is provided by the table: it will jump back to "DINNER" and continue execution from there.

     : soup ." soup " ;                   ( r1 r2)
     : dessert ." dessert " ;             ( r1 r6)
     : chicken ." chicken " r> drop ;     ( r1 r3 - r4 gets lost!)
     : rice ." rice " ;                   ( r1 r3 r5)
     : entree chicken rice ;              ( r1 r3)
     : dinner soup entree dessert ;  ( r1)
     dinner cr                            ( --)

Since "CHICKEN" gets rid of the return address to "ENTREE", "RICE" is never called. Instead, a jump is made to "DINNER" that assumes that "ENTREE" is done, so it continues with "DESSERT". This is the output:

     soup chicken dessert

Note that this is not common practice and we do not encourage its use. However, it gives you a pretty good idea how the Return Stack is used by the system.