Files
harbour-core/harbour/doc/codebloc.txt
1999-07-16 04:58:54 +00:00

108 lines
3.3 KiB
Plaintext

The Harbour implementation of codeblocks.
Ryszard Glab <rglab@imid.med.pl>
The compilation of a codeblock.
During compile time the codeblock is stored in the following form:
- the header
- the stream of pcode bytes
The header stores information about referenced local variables.
+0: the pcode byte for _PUSHBLOCK
+1: the number of bytes that defines a codeblock
+3: number of codeblock parameters (declared between || in a codeblock)
+5: number of used local variables declared in procedure/function where
the codeblock is created
+7: the list of procedure/function local variables positions on the the eval
stack of procedure/function. Every local variable used in a codeblock
occupies 2 bytes in this list.
+x: The stream of pcode bytes follows the header.
+y: the pcode byte for _ENDBLOCK
The evaluation of a codeblock.
Before a codeblock evaluation the virtual machine creates the eval stack
where all codeblock parameters are stored (just like function parameters).
When a codeblock parameter is referenced then its position on the eval stack
is used. When a procedure local variable is referenced then the index into
the table of local variables positions (copied from the header) is used.
The negative value is used as an index to distinguish it from the reference
to a codeblock parameter. The table of local variables positions is created
during creation of a codeblock (in PushBlock() function).
During a codeblock creation, values of all local variables defined in a
procedure and accessed in a codeblock are replaced with a reference to
a value stored in a global memory variables pool. This allows to correct
access for detached local variables in a codeblock returned from this
function either directly (in RETURN statement) or indirectly (by assigning
it to a static or memvar variable). This automatic and unconditional
replace is required because there is no safe method to find if a codeblock
will be accessed from an outside of a function where it is created.
Incompatbility with the Clipper.
1) Detached locals passed by reference
There is a little difference between the handling of variables passed by
the reference in a codeblock.
The following code explains it (thanks to David G. Holm)
Function Main()
Local nTest
Local bBlock1 := MakeBlock()
Local bBlock2 := {|| DoThing( @nTest ), qout("From Main: ", nTest ) }
eval( bBlock1 )
eval( bBlock2 )
Return( NIL )
Function MakeBlock()
Local nTest
Return( {|| DoThing( @nTest ), qout("From MakeBlock: ", nTest ) } )
Function DoThing( n )
n := 42
Return( NIL )
In Clipper it produces:
From MakeBlock: NIL
From Main: 42
In Harbour it produces (it is the correct output, IMHO)
From MakeBlock: 42
From Main: 42
2) Scope of undeclared variables
Consider the following code:
PROCEDURE MAIN()
LOCAL cb
cb :=Detach()
? EVAL( cb, 10 )
RETURN
FUNCTION Detach()
LOCAL b:={|x| x+a}
LOCAL a:=0
RETURN b
In Clipper the 'a' variable in a codeblock has the *local* scope however in
Harbour the 'a' variable has the *private* scope. As a result, in Clipper
this code will print 10 and in Harbour it will raise 'argument error' in
'+' operation.
This will be true also when the 'a' variable will be declared as PRIVATE
PROCEDURE MAIN()
LOCAL cb
PRIVATE a
cb :=Detach()
? EVAL( cb, 10)
RETURN
The above code also prints 10 in Clipper (even if compiled with -a or -v
switches)