home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / harbb30g.zip / DOC / codebloc.txt next >
Text File  |  1999-09-01  |  5KB  |  142 lines

  1. The Harbour implementation of codeblocks.
  2. Ryszard Glab <rglab@imid.med.pl>
  3.  
  4.  
  5. Compilation of a codeblock.
  6.   During compile time the codeblock is stored in the following form:
  7. - the header
  8. - the stream of pcode bytes
  9.  
  10.   The header stores information about referenced local variables.
  11. +0: the pcode byte for _PUSHBLOCK
  12. +1: the number of bytes that defines a codeblock
  13. +3: number of codeblock parameters (declared between || in a codeblock)
  14. +5: number of used local variables declared in procedure/function where
  15.     the codeblock is created
  16. +7: the list of procedure/function local variables positions on the eval
  17.     stack of procedure/function. Every local variable used in a codeblock
  18.     occupies 2 bytes in this list. When nested codeblocks are used then this
  19.     list is created for the outermost codeblock only.
  20. +x:  The stream of pcode bytes follows the header.
  21. +y: the pcode byte for _ENDBLOCK
  22.  
  23.  
  24. Creation of a codeblock.
  25.   When HB_P_PUSHBLOCK opcode is executed then the HB_ITEM structure is created
  26. and placed on the eval stack. The type of item is IT_BLOCK. The value of this
  27. item is a pointer to HB_CODEBLOCK structure. Additionally this item stores the
  28. base of static variables defined for the current function/procedure - this
  29. is used during a codeblock evaluation when the evaluation is called from a code
  30. from other PRG module. Also the number of expected parameters is stored.
  31.  
  32.   The HB_CODEBLOCK structure stores a pointer to the pcodes stream that is
  33. executed during a codeblock evaluation. It stores also the pointer to a table
  34. with local variables references. Values of all local variables defined in a
  35. procedure and used in a codeblock are replaced with a reference to a
  36. value stored in a global memory variables pool. This allows the correct access
  37. to detached local variables in a codeblock returned from this function (either
  38. directly in RETURN statement or indirectly by assigning it to a static or
  39. memvar variable. This automatic and unconditional replace is required because
  40. there is no safe method to find if a codeblock will be accessed from an outside
  41. of a function where it is created.
  42.  
  43.   When nested codeblocks are  used then only the outermost codeblock creates
  44. the table - all inner codeblock uses this table. The first element of this
  45. table contains a reference counter for this table. It allows to share the table
  46. between nested codeblock - the table is deleted if there is no more references
  47. to it. This is caused by the fact that a inner codeblock can be created during
  48. evaluation of outer codeblock when local variables don't exist like in this
  49. example:
  50.  
  51. PROCEDUE MAIN()
  52. PRIVATE foo, bar
  53.  
  54.   Test()
  55.   EVAL( foo )
  56.   EVAL( bar )
  57.  
  58. PROCEDURE Test()
  59. LOCAL a:='FOO', b:='BAR'
  60.  
  61.   foo ={ || a + ( bar:=EVAL( {|| b} ) ) }
  62.  
  63. RETURN
  64.  
  65.  
  66. Evaluation of a codeblock.
  67.   Parameters passed to a codeblock are placed on the eval stack before a
  68. codeblock evaluation. They are accessed just like usual function
  69. parameters.  When a codeblock parameter is referenced then its position on the
  70. eval stack is used.  When a procedure local variable is referenced then the
  71. index into the table of local variables positions (copied from the header) is
  72. used.  The negative value is used as an index to distinguish it from the
  73. reference to a codeblock parameter.
  74.  
  75.  
  76. Incompatbility with the Clipper.
  77.  
  78. 1) Detached locals passed by reference
  79.   There is a little difference between the handling of variables passed by
  80. the reference in a codeblock.
  81. The following code explains it (thanks to David G. Holm)
  82.  
  83. Function Main()
  84. Local nTest
  85. Local bBlock1 := MakeBlock()
  86. Local bBlock2 := {|| DoThing( @nTest ), qout("From Main: ", nTest ) }
  87.  
  88.    eval( bBlock1 )
  89.    eval( bBlock2 )
  90.  
  91. Return( NIL )
  92.  
  93. Function MakeBlock()
  94. Local nTest
  95. Return( {|| DoThing( @nTest ), qout("From MakeBlock: ", nTest ) } )
  96.  
  97. Function DoThing( n )
  98.  
  99.    n := 42
  100.  
  101. Return( NIL )
  102.  
  103.  
  104.   In Clipper it produces:
  105. From MakeBlock: NIL
  106. From Main: 42
  107.  
  108.   In Harbour it produces (it is the correct output, IMHO)
  109. From MakeBlock: 42
  110. From Main: 42
  111.  
  112. 2) Scope of undeclared variables
  113.  Consider the following code:
  114.  
  115. PROCEDURE MAIN()
  116. LOCAL cb
  117.   cb :=Detach()
  118.   ? EVAL( cb, 10 )
  119.  
  120. RETURN
  121.  
  122. FUNCTION Detach()
  123. LOCAL b:={|x| x+a}
  124. LOCAL a:=0
  125. RETURN b
  126.  
  127.   In Clipper the 'a' variable in a codeblock has the *local* scope however in
  128. Harbour the 'a' variable has the *private* scope. As a result, in Clipper
  129. this code will print 10 and in Harbour it will raise 'argument error' in
  130. '+' operation.
  131.   This will be true also when the 'a' variable will be declared as PRIVATE
  132.  
  133. PROCEDURE MAIN()
  134. LOCAL cb
  135. PRIVATE a
  136.   cb :=Detach()
  137.   ? EVAL( cb, 10)
  138. RETURN
  139.  
  140. The above code also prints 10 in Clipper (even if compiled with -a or -v
  141. switches)
  142.