en:pfw:stackchecking
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revision | |||
en:pfw:stackchecking [2023-09-04 18:22] – gelöscht - Externe Bearbeitung (Unbekanntes Datum) 127.0.0.1 | en:pfw:stackchecking [2023-09-04 18:22] (current) – ↷ Seite von pfw:stackchecking nach en:pfw:stackchecking verschoben uho | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ===== Stack Checking Utility: Debugging through Run-Time Stack Checks ===== | ||
+ | |||
+ | ==== Stack Checking idea ==== | ||
+ | |||
+ | The Stack Checking utility is a debugging tool for Forth, used to validate stack comments and the actual stack behaviour of a word. It's a run-time check that ensures the number of items in the stack before and after the execution of a word, matches the comment description. | ||
+ | |||
+ | Often, an incorrect stack effect, i.e., the actual stack effect differing from the one specified, results in programming errors. With the Stack Checking utility, these can be identified and rectified in the programming phase itself, thus ensuring robust programs and applications. | ||
+ | |||
+ | ==== Stack Checking Implementation ==== | ||
+ | |||
+ | The Stack Checking utility uses the stack comments provided to check the stack. This does not aim to understand the semantics of the stack comment but uses the information about the number of stack items available before and after a word's execution. Stack comments used for checking are started with **(?** and have the form | ||
+ | |||
+ | < | ||
+ | (? items before execution -- items after execution ) | ||
+ | </ | ||
+ | |||
+ | The utility analyses these kinds of comments and checks for two things: | ||
+ | |||
+ | 1. The number of stack items a given word expects on the data stack. If this number is violated, it indicates improper use of the word. | ||
+ | |||
+ | 2. The number of items a given word will additionally put on the top of the data stack (or remove). If the word leaves less or more items than this number, it indicates improper behavior of the word. | ||
+ | |||
+ | It checks the stack depth on entry of a word and calculates the expected stack depth on exit. Once the execution of the word has finished, the expected stack depth is checked against the actual stack depth. | ||
+ | |||
+ | ==== Pseudo code for the Stack Checking implementation ==== | ||
+ | |||
+ | Analyze stack comments by counting the number of tokens before and after the - -. This gives the number of items expected by the word and the number of items then left by the word. We can easily calculate the change in the number of items (the stack effect). | ||
+ | |||
+ | Assuming we know the **required number of items** of the current word and **its stack effect**. We can generate code that will check the stack on entry and (and by creating a stack frame) on exit of the word. This code will | ||
+ | |||
+ | * Examine the current stack depth before the execution of the word. | ||
+ | * Check if the stack depth is at least equal to the number of items expected by the word. | ||
+ | * If not, trigger a runtime error message. | ||
+ | * If yes, calculate the expected stack depth after the execution of the word. | ||
+ | * Create a stack frame do that we can execute code after the current words execution. | ||
+ | * Execute the current word. | ||
+ | * In the exit code check the actual stack depth after execution against the expected stack depth. | ||
+ | * If they are different, trigger a runtime error message. | ||
+ | |||
+ | ==== Various Stack Checking Implementations ==== | ||
+ | |||
+ | Stack Checking is actually performed by the word CHECK that uses the noForth specifica DIVE facility to execute code before and after a word. See the [[https:// | ||
+ | |||
+ | INS and OUTS counts the number of items in the stack comment before and after - -. | ||
+ | |||
+ | === Stack Checking for noForth T === | ||
+ | |||
+ | < | ||
+ | (* Runtime Stack Checking a Debugging Aid, Ulrich Hoffmann see euroFORML' | ||
+ | noForth Forth Version | ||
+ | |||
+ | Use (? as stack comment start to check the stack of the current word at runtime. | ||
+ | |||
+ | *) | ||
+ | |||
+ | |||
+ | inside also definitions | ||
+ | |||
+ | : check ( name #chge #before -- ) | ||
+ | depth 3 - > | ||
+ | IF swap count type ." | ||
+ | depth 2 - + r> -rot 2>r >r ( pass name and #after ) | ||
+ | dive 2r> ( name #after -- ) | ||
+ | 2 + depth = IF drop EXIT THEN | ||
+ | count type ." | ||
+ | |||
+ | : ins ( -- n ) 0 BEGIN bl-word count s" --" s<> | ||
+ | : outs ( -- n ) 0 BEGIN bl-word count s" )" | ||
+ | |||
+ | forth definitions | ||
+ | |||
+ | : (? ( -- ) | ||
+ | created LFA>N postpone LITERAL \ word name | ||
+ | ins outs over - postpone LITERAL \ stack depth change | ||
+ | postpone LITERAL | ||
+ | postpone CHECK | ||
+ | ; immediate | ||
+ | </ | ||
+ | |||
+ | === How to use the Stack Checking Utility === | ||
+ | |||
+ | < | ||
+ | \ a word with mismatched stack effect and comment | ||
+ | |||
+ | : unchecked ( a b -- c ) over + ; \ wrong, unchecked | ||
+ | |||
+ | \ 1 2 unchecked | ||
+ | |||
+ | \ a word with mismatched stack effect and comment now with stack checking | ||
+ | |||
+ | : test (? a b c -- 1 2 ) drop drop drop 1 ; \ wrong | ||
+ | |||
+ | \ 1 2 3 test -> error message "test has incorrect stack effect" | ||
+ | |||
+ | \ a word that uses the incorrect TEST | ||
+ | : t (? a -- b ) dup dup test drop ; \ wrong | ||
+ | |||
+ | \ 1 t -> error message "test has incorrect stack effect" | ||
+ | |||
+ | \ a word with non static stack effect - not checked | ||
+ | : -dup ( n -- n n | 0 ) \ unchecked | ||
+ | dup IF dup THEN ; | ||
+ | |||
+ | \ 10 -dup -> will just run without checking nor error | ||
+ | |||
+ | \ a word with matching stack effect and stack comment | ||
+ | : fine (? n1 n2 n3 -- n3 n3 n3 ) nip nip ( n3 ) dup dup ; \ correct | ||
+ | |||
+ | \ 1 2 3 fine -> just runs fine | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | Alternative implementations of stack checking might exist and can be added at the end of this description. | ||
+ | |||
+ | |||
+ | ==== Possible pitfalls with Stack Checking ==== | ||
+ | |||
+ | Some Forth words, such as "? | ||
+ | |||
+ | ==== Contributions ==== | ||
+ | |||
+ | < | ||
+ | |||
+ | === Albert Nijhof' | ||
+ | |||
+ | Albert proposes to avoid counting traditional stack comments but to add the stack effect at the end of the stack comments. This results in simpler code: | ||
+ | |||
+ | A poor man's stack checker for noForth - (an) 02may2023 | ||
+ | |||
+ | < | ||
+ | : STACK.CHECK ( stack-growth nfa -- ) | ||
+ | depth | ||
+ | swap r> 2> | ||
+ | + r> 2> | ||
+ | dive \ finish the main word now | ||
+ | 2r> depth - \ depth not as expected? | ||
+ | dup if cr ." Error in " | ||
+ | over count hx 1F and type space | ||
+ | dup s>d if ." +" then negate . | ||
+ | ? | ||
+ | then 2drop ; | ||
+ | | ||
+ | 0 value CHECK? | ||
+ | |||
+ | : (? ( -- ) | ||
+ | [char] ) parse + 2 - \ read " | ||
+ | check? | ||
+ | if count swap c@ \ sign and digit | ||
+ | [char] 0 - swap | ||
+ | [char] - = if negate then | ||
+ | postpone literal | ||
+ | created lfa>n postpone literal | ||
+ | postpone stack.check | ||
+ | exit then | ||
+ | drop ; immediate | ||
+ | </ | ||
+ | |||
+ | | ||
+ | Start a definition with (? ... n) which means that at EXIT the stack is | ||
+ | | ||
+ | n consists of two characters, the first must be + or -, | ||
+ | the second a digit in [0,9]. See examples below. | ||
+ | Usual stack comment is possible between (? and n), for example: | ||
+ | : ? | ||
+ | A flag in the value CHECK? determines whether the stack checker | ||
+ | will be compiled or not. | ||
+ | |||
+ | | ||
+ | < | ||
+ | DIVE Exchange IP and top of return stack | ||
+ | ? | ||
+ | CREATED LFA of the definition which is being build at the moment | ||
+ | LFA> | ||
+ | HX The following number is in HEX | ||
+ | </ | ||
+ | Examples | ||
+ | < | ||
+ | true to check? | ||
+ | : T1 (? +1) ?dup ; \ stack is expected to grow with 1 element | ||
+ | : T2 (? +2) ; \ stack should grow with 2 elements | ||
+ | : T3 (? -3) ; \ stack should shrink with 3 elements | ||
+ | : T4 (? +0) ; \ stack depth should not change | ||
+ | </ | ||
+ | |||
+ | Test results | ||
+ | < | ||
+ | @)5 t1 .s 2drop ( 5 5 ) OK.0 | ||
+ | @)0 t1 .s drop | ||
+ | Error in T1 -1 | ||
+ | Msg from STACK.CHECK \ Error # AB29 -54D7 | ||
+ | ( this means that there is 1 element less than expected on the stack ) | ||
+ | |||
+ | @)t2 | ||
+ | Error in T2 -2 | ||
+ | Msg from STACK.CHECK \ Error # AB29 -54D7 | ||
+ | ( 2 elements less than expected ) | ||
+ | |||
+ | @)t3 | ||
+ | Error in T3 3 | ||
+ | Msg from STACK.CHECK \ Error # AB29 -54D7 | ||
+ | ( 3 elements more than expected ) | ||
+ | |||
+ | @)t4 OK.0 | ||
+ | </ | ||
+ | |||
+ | === Do you have another approach to Stack Checking? === | ||
+ | |||
+ | Please add it at the end of this document. | ||
+ | |||
+ | ~~DISCUSSION~~ | ||