19 May 2011

Passing a Hash to a subroutine (II) - The Descriptor

In the previous post we discussed the GFA-BASIC 32 compiler bug when a Hash variable is passed to a subroutine. The compiler produces incorrect code for the Hash Xxx commands, but it works well in case hash-operators are used.
The situation is like this:
Sub q(hs As Hash String)
  ' Errorneous:
  Hash Add hs["mykey"], "Entry1"
  Hash Add hs[2], "Entry2"
  ' OK:
  hs[3] = "Entry3"
  Print hs[%]
EndSub
Note that the Hash variable hs is passed by-reference (implicitly due to the use of the keyword Sub).

Hash descriptor
A Hash variable is actually a 8-byte structure (user-defined type) containing two LONG integers. The Hash variable references the address of this hash-type, called the descriptor. The Hash-descriptor is defined as follows:
Type HashDesc
  pHashData As Long
  pType As Long
EndType
After declaring a hash variable (Dim hs As Hash type) the first long integer of the descriptor is Null indicating that the hash didn't allocate any memory. The second long contains a value indicating the GFA-BASIC 32 primary data type used to store values in the hash table. The data type could be Int, Byte, String, Variant, etc. Every GFA-BASIC 32 data type can be used, with the exception of UDT types. As an example the next code declares a Hash to store (long) Integer values and displays the descriptor:
Local HB As Hash Int
Local hs_desc As Pointer To HashDesc
Pointer(hs_desc) = *HB
Debug "HASH DESCRIPTOR"
Trace Hex(*hs_desc)
Trace Hex(hs_desc.pHashData)    ' = 0
Trace Hex(hs_desc.pType)        ' = $4018
The variable HB references the starting address of the 8-byte descriptor. As with any GFA-BASIC 32 variable that uses a descriptor, the address can be obtained using ArrPtr(HB) or *HB. Using the preceding code you can inspect the descriptor.

Hash MUST be declared as ByRef 
When a hash variable is passed to a subroutine, its descriptor address is always passed by reference (whether or not ByRef or ByVal is used in the procedure header). BUT, the Hash parameter MUST be ByRef, otherwise the code generated for the procedure will use a wrong address. A ByVal Hash parameter sets the compiler in error mode without returning to the editor. As a consequence all code following might (will actually) be compiled wrongly.
When the Hash parameter is passed correctly by-reference the compiler still generates incorrect code for the Hash Xxx commands, like Hash Add, Hash Remove,...
The advise remains the same, don't pass Hash tables around, but use them as global variables only.

No comments:

Post a Comment