home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 5 / 05.iso / a / a012 / 1.ddi / README.EXE / PROCNAME.TXT < prev    next >
Encoding:
Text File  |  1991-05-01  |  7.1 KB  |  191 lines

  1.                      5.0 FOCUS: PROCNAME() and PROCLINE()
  2.                                  By Greg Lief
  3.  
  4.  
  5. Introduction
  6.  
  7. In Summer '87, PROCNAME() and PROCLINE() returned the name of the
  8. current procedure name and source code line number.  However, 5.0
  9. greatly expands upon that limited use, thanks to the implementation of
  10. an internal "activation stack".
  11.  
  12. The Activation Stack
  13.  
  14. The activation stack keeps track of all calls to procedures,
  15. functions, and code blocks (we'll come back to these a bit later).
  16. This enables us to trace our steps at any time during the course of
  17. our application, and is particularly handy when the beast crashes (a
  18. familiar occurrence as we make our first steps into the brave new
  19. world of 5.0).  It does not have any apparent limits in terms of
  20. size (although I suppose that if you have the time to look long
  21. enough, you will undoubtedly discover any such limits).
  22.  
  23. Take the following test code (please!):
  24.  
  25.    function main
  26.    private x
  27.    x = 0
  28.    test()
  29.    return 0   && instead of NIL for S'87 compatibility
  30.  
  31.    function test
  32.    x = x + 1  && instead of x++ for S'87 compatibility
  33.    test2()
  34.    return 0   && same as above
  35.  
  36.    function test2
  37.    x = str(x)
  38.    test3()
  39.    return 0   && same as above
  40.  
  41.    function test3
  42.    ? x * 5
  43.    return 0   && same as above
  44.  
  45. (Note: this is deliberately written using the nasty habit of inherited
  46. PRIVATE variables so that it will crash.  Yes, I actually have to
  47. force myself to write bugs -- what a world!)
  48.  
  49. Running this code under Summer '87 presents us with the following:
  50.  
  51.    Proc TEST3, line 18, type mismatch
  52.  
  53. Quite informative!  This is the sort of run-time error that gives
  54. Clipper programs and programmers a rotten name.  Let's run that again,
  55. this time under 5.0:
  56.  
  57.    Error BASE/1083  Argument error: *
  58.    Called from TEST3(18)
  59.    Called from TEST2(14)
  60.    Called from TEST(9)
  61.    Called from MAIN(4)
  62.  
  63. Aha!  We can see exactly how we got to the offending line of code, and
  64. thus are far better equipped to squash the bug quickly.
  65.  
  66. Depth
  67.  
  68. Each time that you call a procedure or function, the current procedure
  69. and line number are added to the activation stack.  To refer to the
  70. previous procedure and line number, you would call PROCNAME(1) and
  71. PROCLINE(1).  That parameter indicates how many levels back to look in
  72. the activation stack.  Of course, you can still call PROCNAME() and
  73. PROCLINE() with no parameters, in which case they will return the
  74. current procedure and line number (just like the bad old days).  But
  75. the ability to pass the depth parameter is an extremely welcome
  76. addition.
  77.  
  78. If you pass a depth parameter for which there is nothing in the
  79. activation stack, PROCNAME() will return a null character string ("")
  80. and PROCLINE() will return zero.
  81.  
  82. Code Blocks
  83.  
  84. As mentioned above, each time that you invoke a code block, that
  85. information will be added to the activation stack.  Be warned,
  86. however, that this works a bit differently than the straight
  87. procedure/function call.  Time for another example... this test
  88. program initializes a code block B (which as you can see is expecting
  89. a character-type parameter by the presence of the LEN() function) and
  90. deliberately crashes it by passing it a numeric:
  91.  
  92.    function main
  93.    local b := { | a | len(a) }
  94.    eval(b, 5)
  95.    return nil
  96.  
  97. (Again, I really enjoy writing bugs on purpose.  In fact, I will
  98. take this to an extreme near the end of this article.)
  99.  
  100. When we run this test program, voila!
  101.  
  102.    Error BASE/1111  Argument error: LEN
  103.    Called from (b)MAIN(2)
  104.    Called from MAIN(3)
  105.  
  106. Whoa, what's that (b)MAIN nonsense?  Simple... the (b) preface
  107. indicates that this is a code block.  If you glance again at the code
  108. above, you will see that the code block is indeed initialized at line
  109. two.  When we attempt to EVALuate it at line three, program control
  110. jumps back to the line at which the code block was initialized.  This
  111. is always how your programs will react when attempting to evaluate a
  112. code block.  In fact, if you step through a program that uses TBROWSE
  113. with any related code blocks (SkipBlock, GoTop, GoBottom, to name a
  114. few), you will be amazed and befuddled at how often the program jumps
  115. around.  The actual logic will often bear little resemblance to what
  116. we have come to expect in a procedural language, but once you get used
  117. to it, it is not only understandable, but amusing.
  118.  
  119. When you compile a code block at run-time using the & operator, the
  120. information stored in the activation stack will not include a line
  121. number:
  122.  
  123.    function main
  124.    local b := "{ | a | len(a) }", x
  125.    x := &(b)   // code block is created here
  126.    eval(x, 5)
  127.    return nil
  128.  
  129. The output from this program is:
  130.  
  131.    Error BASE/1111  Argument error: LEN
  132.    Called from (b)MAIN(0)
  133.    Called from MAIN(4)
  134.  
  135. The Big Example
  136.  
  137. Not only is the accompanying example educational, but it makes a
  138. terrific April Fool's Joke. It simulates a Clipper 5.0 program crash
  139. (as if you did not already have enough problems already).
  140.  
  141. Seriously, though, it demonstrates stepping through the activation
  142. stack to show all the places we have been.  It also shows cursor
  143. control and relative screen control (using maxrow() and maxcol()) in
  144. 5.0, which are both quite different from their Summer '87
  145. counterparts.
  146.  
  147. The critical piece of code in this example is the DO..WHILE loop:
  148.  
  149.    i := 1
  150.    do while ! empty(procname(i))
  151.       ? "Called from", Trim(procname(i)) + ;
  152.         "(" + ltrim(str(procline(i++))) + ")  "
  153.    enddo
  154.  
  155. We can write this loop condition because we know that PROCNAME() will
  156. return an empty character string when we hit the end of the activation
  157. stack.  Notice how we cleverly increment the loop counter with the
  158. post-increment ("++") operator.
  159.  
  160. CRASH() will clear the screen, display a random error message and the
  161. contents of the activation stack, and cap it off with a bogus DOS
  162. prompt.  The clincher is that it will then wait for a set number of
  163. keypresses (default 5) before resuming the application.  This is a
  164. great prank to play on end users who are driving you nuts.  You could
  165. even structure the logic at the entry of your program to accept a
  166. command-line parameter (for example, "/NC") which would pre-empt the
  167. CRASH() call:
  168.  
  169.    function main(params)
  170.    local dont_crash := ("/NC" $ upper(params))
  171.    .
  172.    .
  173.    .
  174.    if ! dont_crash
  175.       crash(300)  // allow 300 keystrokes at the fake DOS prompt
  176.    endif
  177.  
  178. Naturally, the user will be unaware of this parameter (because you
  179. will not document it, right?).  They will then run the program and
  180. have it crash repeatedly.  You can walk over, run the program with the
  181. command-line parameter, and then defy them to show you where it
  182. crashes.
  183.  
  184. There is always the possibility that the person you spring this on
  185. will not appreciate your sense of humor.  Therefore, I must whip out
  186. the following disclaimer: your humble author will NOT be liable for
  187. consequential, special, indirect, or other similar damages or claims,
  188. including loss of profits or any other commercial damage. In other
  189. words, if your victim gets mad and tosses your computer out the
  190. window, please do not expect me to buy you a new computer!
  191.